Collision rework

This commit is contained in:
Matthias Schiffer 2014-09-26 00:58:27 +02:00
parent 8b4ca336ce
commit c3e32345e5
8 changed files with 135 additions and 46 deletions

View file

@ -62,32 +62,32 @@ void MapContext::movePlayer(Model::Direction dir, uint64_t time) {
void MapContext::movePlayerContinue(uint64_t time) { void MapContext::movePlayerContinue(uint64_t time) {
if (inputHandler->isKeyPressed(SDL_SCANCODE_UP)) if (inputHandler->isKeyPressed(SDL_SCANCODE_UP))
movePlayer(Model::NORTH, time); movePlayer(Model::Direction::NORTH, time);
else if (inputHandler->isKeyPressed(SDL_SCANCODE_RIGHT)) else if (inputHandler->isKeyPressed(SDL_SCANCODE_RIGHT))
movePlayer(Model::EAST, time); movePlayer(Model::Direction::EAST, time);
else if (inputHandler->isKeyPressed(SDL_SCANCODE_DOWN)) else if (inputHandler->isKeyPressed(SDL_SCANCODE_DOWN))
movePlayer(Model::SOUTH, time); movePlayer(Model::Direction::SOUTH, time);
else if (inputHandler->isKeyPressed(SDL_SCANCODE_LEFT)) else if (inputHandler->isKeyPressed(SDL_SCANCODE_LEFT))
movePlayer(Model::WEST, time); movePlayer(Model::Direction::WEST, time);
} }
void MapContext::keyPressed(uint16_t key, uint64_t time) { void MapContext::keyPressed(uint16_t key, uint64_t time) {
switch (key) { switch (key) {
case SDL_SCANCODE_UP: case SDL_SCANCODE_UP:
movePlayer(Model::NORTH, time); movePlayer(Model::Direction::NORTH, time);
break; break;
case SDL_SCANCODE_RIGHT: case SDL_SCANCODE_RIGHT:
movePlayer(Model::EAST, time); movePlayer(Model::Direction::EAST, time);
break; break;
case SDL_SCANCODE_DOWN: case SDL_SCANCODE_DOWN:
movePlayer(Model::SOUTH, time); movePlayer(Model::Direction::SOUTH, time);
break; break;
case SDL_SCANCODE_LEFT: case SDL_SCANCODE_LEFT:
movePlayer(Model::WEST, time); movePlayer(Model::Direction::WEST, time);
} }
} }

View file

@ -31,7 +31,7 @@ namespace RPGEdit {
namespace Model { namespace Model {
enum CollisionType { enum class CollisionType {
BLOCKED = 0, BLOCKED = 0,
EMPTY, EMPTY,
}; };

View file

@ -31,13 +31,32 @@ namespace RPGEdit {
namespace Model { namespace Model {
enum Direction { enum class Direction {
NORTH, NORTH,
EAST, EAST,
SOUTH, SOUTH,
WEST, WEST
}; };
static inline Direction operator-(Direction dir) {
switch (dir) {
case Direction::NORTH:
return Direction::SOUTH;
case Direction::EAST:
return Direction::WEST;
case Direction::SOUTH:
return Direction::NORTH;
case Direction::WEST:
return Direction::EAST;
default:
__builtin_unreachable();
}
}
} }
} }

View file

@ -47,7 +47,7 @@ private:
public: public:
Entity(const std::string &name0) Entity(const std::string &name0)
: name(name0), direction(NORTH) { : name(name0), direction(Direction::NORTH) {
} }
const std::string & getName() const { const std::string & getName() const {

View file

@ -57,13 +57,9 @@ bool Map::moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end
entity->setDirection(dir); entity->setDirection(dir);
Position<int> pos = state.position + dir; if (isBlocked(state.position + dir))
if (getCollisionAt(pos.x, pos.y) == BLOCKED)
return false; return false;
setCollisionAt(pos.x, pos.y, BLOCKED);
state.transitionStart = start; state.transitionStart = start;
state.transitionEnd = end; state.transitionEnd = end;
state.direction = dir; state.direction = dir;
@ -74,12 +70,11 @@ bool Map::moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end
void Map::moveEntityTo(Entity *entity, Position<int> pos) { void Map::moveEntityTo(Entity *entity, Position<int> pos) {
EntityState &state = entityStates.at(entity); EntityState &state = entityStates.at(entity);
setCollisionAt(state.position.x, state.position.y, EMPTY); removeEntityPosition(state.position, entity);
setCollisionAt(pos.x, pos.y, BLOCKED); addEntityPosition(pos, entity);
state.position = pos; state.position = pos;
state.transitionStart = state.transitionEnd = 0; state.transitionStart = state.transitionEnd = 0;
} }
void Map::finishEntityTransition(Entity *entity) { void Map::finishEntityTransition(Entity *entity) {
@ -98,17 +93,17 @@ std::unique_ptr<Map> Map::load(__attribute__((unused)) const std::string &name)
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) { for (int j = 0; j < 16; j++) {
if (4 <= i && i < 12 && 4 <= j && j < 12) { if (4 <= i && i < 12 && 4 <= j && j < 12) {
map->setTileAt(0, i, j, 1); map->setTileAt(0, Position<int>{i, j}, 1);
map->setCollisionAt(i, j, EMPTY); map->setCollisionAt(Position<int>{i, j}, CollisionType::EMPTY);
} }
else { else {
map->setTileAt(0, i, j, 0); map->setTileAt(0, Position<int>{i, j}, 0);
} }
if (4 <= i && i < 12 && j == 8) if (4 <= i && i < 12 && j == 8)
map->setTileAt(1, i, j, 2); map->setTileAt(1, Position<int>{i, j}, 2);
else else
map->setTileAt(1, i, j, 0); map->setTileAt(1, Position<int>{i, j}, 0);
} }
} }

View file

@ -30,6 +30,7 @@
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
#include "CollisionType.hpp" #include "CollisionType.hpp"
@ -68,12 +69,27 @@ private:
}; };
std::vector<std::unique_ptr<Entity>> entities; std::vector<std::unique_ptr<Entity>> entities;
std::unordered_map<Position<int>, std::unordered_set<Entity *>> positions;
std::unordered_map<const Entity *, EntityState> entityStates; std::unordered_map<const Entity *, EntityState> entityStates;
void addEntityPosition(const Position<int> &position, Entity *entity) {
positions[position].insert(entity);
}
void removeEntityPosition(const Position<int> &position, Entity *entity) {
auto it = positions.find(position);
it->second.erase(entity);
if (it->second.empty())
positions.erase(it);
}
void pushEntity(Entity *entity, EntityState &&state) { void pushEntity(Entity *entity, EntityState &&state) {
entities.push_back(std::unique_ptr<Entity>(entity)); entities.push_back(std::unique_ptr<Entity>(entity));
entityStates.emplace(entity, state); addEntityPosition(state.position, entity);
entityStates.emplace(entity, std::move(state));
} }
void copyEntities(const Map &other) { void copyEntities(const Map &other) {
@ -81,6 +97,37 @@ private:
pushEntity(new Entity(*e), EntityState(other.entityStates.at(e.get()))); pushEntity(new Entity(*e), EntityState(other.entityStates.at(e.get())));
} }
bool hasTransitionTo(const Position<int> &p, Direction dir) const {
auto it = positions.find(p+dir);
if (it == positions.end())
return false;
for (const Entity *entity : it->second) {
const EntityState &state = entityStates.at(entity);
if (state.transitionEnd && (state.direction == -dir))
return true;
}
return false;
}
bool hasTransitionTo(const Position<int> &p) const {
if (hasTransitionTo(p, Direction::NORTH))
return true;
if (hasTransitionTo(p, Direction::EAST))
return true;
if (hasTransitionTo(p, Direction::SOUTH))
return true;
if (hasTransitionTo(p, Direction::WEST))
return true;
return false;
}
Map(size_t width0, size_t height0, size_t layers) : _Map(width0, height0, layers) { Map(size_t width0, size_t height0, size_t layers) : _Map(width0, height0, layers) {
} }
@ -126,7 +173,6 @@ public:
Entity * addEntity(const std::string &name, const Position<int> &pos) { Entity * addEntity(const std::string &name, const Position<int> &pos) {
Entity *e = new Entity(name); Entity *e = new Entity(name);
pushEntity(e, EntityState(pos)); pushEntity(e, EntityState(pos));
setCollisionAt(pos.x, pos.y, BLOCKED);
return e; return e;
} }
@ -142,33 +188,39 @@ public:
return tiles.size(); return tiles.size();
} }
void setCollisionAt(size_t x, size_t y, CollisionType value) { void setCollisionAt(const Position<int> &p, CollisionType value) {
if (x >= width || y >= height) if (p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height)
throw std::range_error("Map::setCollisionAt: bad coordinates"); throw std::range_error("Map::setCollisionAt: bad coordinates");
collision[y*width + x] = value; collision[p.y*width + p.x] = value;
} }
CollisionType getCollisionAt(ssize_t x, ssize_t y) const { bool isBlocked(const Position<int> &p) const {
if (x < 0 || (size_t)x >= width || y < 0 || (size_t)y >= height) if (p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height)
return BLOCKED; return true;
return collision[y*width + x]; if (positions.find(p) != positions.end())
return true;
if (hasTransitionTo(p))
return true;
return collision[p.y*width + p.x] == CollisionType::BLOCKED;
} }
void setTileAt(size_t layer, size_t x, size_t y, uint32_t value) { void setTileAt(size_t layer, const Position<int> &p, uint32_t value) {
if (layer >= tiles.size() || x >= width || y >= height) if (layer >= tiles.size() || p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height)
throw std::range_error("Map::setTileAt: bad coordinates"); throw std::range_error("Map::setTileAt: bad coordinates");
tiles[layer][y*width + x] = value; tiles[layer][p.y*width + p.x] = value;
} }
uint32_t getTileAt(size_t layer, ssize_t x, ssize_t y) const { uint32_t getTileAt(size_t layer, const Position<int> &p) const {
if (layer >= tiles.size() || x < 0 || (size_t)x >= width || y < 0 || (size_t)y >= height) if (layer >= tiles.size() || p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height)
return 0; return 0;
return tiles[layer][y*width + x]; return tiles[layer][p.y*width + p.x];
} }
Position<float> getEntityPosition(const Entity *entity, uint64_t time) const; Position<float> getEntityPosition(const Entity *entity, uint64_t time) const;

View file

@ -28,6 +28,8 @@
#include "Direction.hpp" #include "Direction.hpp"
#include <functional>
namespace RPGEdit { namespace RPGEdit {
@ -41,19 +43,19 @@ struct Position {
Position p = *this; Position p = *this;
switch (dir) { switch (dir) {
case NORTH: case Direction::NORTH:
p.y -= amount; p.y -= amount;
break; break;
case EAST: case Direction::EAST:
p.x += amount; p.x += amount;
break; break;
case SOUTH: case Direction::SOUTH:
p.y += amount; p.y += amount;
break; break;
case WEST: case Direction::WEST:
p.x -= amount; p.x -= amount;
} }
@ -64,6 +66,10 @@ struct Position {
return translate(dir, 1); return translate(dir, 1);
} }
bool operator==(const Position<T> &p) const {
return (x == p.x) && (y == p.y);
}
template<typename T2> template<typename T2>
explicit operator Position<T2>() const { explicit operator Position<T2>() const {
return Position<T2>{T2(x), T2(y)}; return Position<T2>{T2(x), T2(y)};
@ -73,3 +79,20 @@ struct Position {
} }
} }
namespace std {
template<typename T> struct hash<RPGEdit::Model::Position<T>> {
typedef size_t result_type;
typedef RPGEdit::Model::Position<T> argument_type;
size_t operator()(const RPGEdit::Model::Position<T> &pos) const noexcept {
const int shift = 4*sizeof(size_t); // half the bit count of size_t
std::hash<T> hash;
size_t hash1 = hash(pos.x), hash2 = hash(pos.y);
return hash1 ^ ((hash2 << shift) | (hash2 >> shift));
}
};
}

View file

@ -113,7 +113,7 @@ void MapView::render(const Model::Map *map, Model::Position<float> center, uint6
for (size_t layer = 0; layer < map->getLayerCount(); layer++) { for (size_t layer = 0; layer < map->getLayerCount(); layer++) {
for (int x = minX; x <= maxX; x++) { for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) { for (int y = minY; y <= maxY; y++) {
uint32_t tile = map->getTileAt(layer, x, y); uint32_t tile = map->getTileAt(layer, Model::Position<int>{x, y});
if (!tile) if (!tile)
continue; continue;
@ -141,7 +141,7 @@ void MapView::render(const Model::Map *map, Model::Position<float> center, uint6
Model::Direction dir = entity->getDirection(); Model::Direction dir = entity->getDirection();
SDL_Rect src = { SDL_Rect src = {
.x = getTileSize()*dir, .x = getTileSize()*int(dir),
.y = 0, .y = 0,
.w = getTileSize(), .w = getTileSize(),
.h = getTileSize(), .h = getTileSize(),