From c3e32345e53650a1c231f4e1a41d40c97955b893 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 26 Sep 2014 00:58:27 +0200 Subject: Collision rework --- src/control/MapContext.cpp | 16 ++++----- src/model/CollisionType.hpp | 2 +- src/model/Direction.hpp | 23 +++++++++++-- src/model/Entity.hpp | 2 +- src/model/Map.cpp | 21 +++++------- src/model/Map.hpp | 82 ++++++++++++++++++++++++++++++++++++--------- src/model/Position.hpp | 31 ++++++++++++++--- src/view/MapView.cpp | 4 +-- 8 files changed, 135 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/control/MapContext.cpp b/src/control/MapContext.cpp index aa246a9..f5d4c05 100644 --- a/src/control/MapContext.cpp +++ b/src/control/MapContext.cpp @@ -62,32 +62,32 @@ void MapContext::movePlayer(Model::Direction dir, uint64_t time) { void MapContext::movePlayerContinue(uint64_t time) { if (inputHandler->isKeyPressed(SDL_SCANCODE_UP)) - movePlayer(Model::NORTH, time); + movePlayer(Model::Direction::NORTH, time); else if (inputHandler->isKeyPressed(SDL_SCANCODE_RIGHT)) - movePlayer(Model::EAST, time); + movePlayer(Model::Direction::EAST, time); else if (inputHandler->isKeyPressed(SDL_SCANCODE_DOWN)) - movePlayer(Model::SOUTH, time); + movePlayer(Model::Direction::SOUTH, time); 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) { switch (key) { case SDL_SCANCODE_UP: - movePlayer(Model::NORTH, time); + movePlayer(Model::Direction::NORTH, time); break; case SDL_SCANCODE_RIGHT: - movePlayer(Model::EAST, time); + movePlayer(Model::Direction::EAST, time); break; case SDL_SCANCODE_DOWN: - movePlayer(Model::SOUTH, time); + movePlayer(Model::Direction::SOUTH, time); break; case SDL_SCANCODE_LEFT: - movePlayer(Model::WEST, time); + movePlayer(Model::Direction::WEST, time); } } diff --git a/src/model/CollisionType.hpp b/src/model/CollisionType.hpp index 4877669..50bb608 100644 --- a/src/model/CollisionType.hpp +++ b/src/model/CollisionType.hpp @@ -31,7 +31,7 @@ namespace RPGEdit { namespace Model { -enum CollisionType { +enum class CollisionType { BLOCKED = 0, EMPTY, }; diff --git a/src/model/Direction.hpp b/src/model/Direction.hpp index db288f6..e817170 100644 --- a/src/model/Direction.hpp +++ b/src/model/Direction.hpp @@ -31,13 +31,32 @@ namespace RPGEdit { namespace Model { -enum Direction { +enum class Direction { NORTH, EAST, 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(); + } +} + } } diff --git a/src/model/Entity.hpp b/src/model/Entity.hpp index eab6983..331cb4f 100644 --- a/src/model/Entity.hpp +++ b/src/model/Entity.hpp @@ -47,7 +47,7 @@ private: public: Entity(const std::string &name0) - : name(name0), direction(NORTH) { + : name(name0), direction(Direction::NORTH) { } const std::string & getName() const { diff --git a/src/model/Map.cpp b/src/model/Map.cpp index b7b40f5..749b25f 100644 --- a/src/model/Map.cpp +++ b/src/model/Map.cpp @@ -57,13 +57,9 @@ bool Map::moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end entity->setDirection(dir); - Position pos = state.position + dir; - - if (getCollisionAt(pos.x, pos.y) == BLOCKED) + if (isBlocked(state.position + dir)) return false; - setCollisionAt(pos.x, pos.y, BLOCKED); - state.transitionStart = start; state.transitionEnd = end; 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 pos) { EntityState &state = entityStates.at(entity); - setCollisionAt(state.position.x, state.position.y, EMPTY); - setCollisionAt(pos.x, pos.y, BLOCKED); + removeEntityPosition(state.position, entity); + addEntityPosition(pos, entity); state.position = pos; state.transitionStart = state.transitionEnd = 0; - } void Map::finishEntityTransition(Entity *entity) { @@ -98,17 +93,17 @@ std::unique_ptr Map::load(__attribute__((unused)) const std::string &name) for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { if (4 <= i && i < 12 && 4 <= j && j < 12) { - map->setTileAt(0, i, j, 1); - map->setCollisionAt(i, j, EMPTY); + map->setTileAt(0, Position{i, j}, 1); + map->setCollisionAt(Position{i, j}, CollisionType::EMPTY); } else { - map->setTileAt(0, i, j, 0); + map->setTileAt(0, Position{i, j}, 0); } if (4 <= i && i < 12 && j == 8) - map->setTileAt(1, i, j, 2); + map->setTileAt(1, Position{i, j}, 2); else - map->setTileAt(1, i, j, 0); + map->setTileAt(1, Position{i, j}, 0); } } diff --git a/src/model/Map.hpp b/src/model/Map.hpp index 4e434b1..0343709 100644 --- a/src/model/Map.hpp +++ b/src/model/Map.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "CollisionType.hpp" @@ -68,12 +69,27 @@ private: }; std::vector> entities; + std::unordered_map, std::unordered_set> positions; std::unordered_map entityStates; + void addEntityPosition(const Position &position, Entity *entity) { + positions[position].insert(entity); + } + + void removeEntityPosition(const Position &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) { entities.push_back(std::unique_ptr(entity)); - entityStates.emplace(entity, state); + addEntityPosition(state.position, entity); + entityStates.emplace(entity, std::move(state)); } void copyEntities(const Map &other) { @@ -81,6 +97,37 @@ private: pushEntity(new Entity(*e), EntityState(other.entityStates.at(e.get()))); } + bool hasTransitionTo(const Position &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 &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) { } @@ -126,7 +173,6 @@ public: Entity * addEntity(const std::string &name, const Position &pos) { Entity *e = new Entity(name); pushEntity(e, EntityState(pos)); - setCollisionAt(pos.x, pos.y, BLOCKED); return e; } @@ -142,33 +188,39 @@ public: return tiles.size(); } - void setCollisionAt(size_t x, size_t y, CollisionType value) { - if (x >= width || y >= height) + void setCollisionAt(const Position &p, CollisionType value) { + 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"); - collision[y*width + x] = value; + collision[p.y*width + p.x] = value; } - CollisionType getCollisionAt(ssize_t x, ssize_t y) const { - if (x < 0 || (size_t)x >= width || y < 0 || (size_t)y >= height) - return BLOCKED; + bool isBlocked(const Position &p) const { + if (p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height) + return true; + + if (positions.find(p) != positions.end()) + return true; + + if (hasTransitionTo(p)) + return true; - return collision[y*width + x]; + return collision[p.y*width + p.x] == CollisionType::BLOCKED; } - void setTileAt(size_t layer, size_t x, size_t y, uint32_t value) { - if (layer >= tiles.size() || x >= width || y >= height) + void setTileAt(size_t layer, const Position &p, uint32_t value) { + 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"); - 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 { - if (layer >= tiles.size() || x < 0 || (size_t)x >= width || y < 0 || (size_t)y >= height) + uint32_t getTileAt(size_t layer, const Position &p) const { + if (layer >= tiles.size() || p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height) return 0; - return tiles[layer][y*width + x]; + return tiles[layer][p.y*width + p.x]; } Position getEntityPosition(const Entity *entity, uint64_t time) const; diff --git a/src/model/Position.hpp b/src/model/Position.hpp index a88c151..c8fc477 100644 --- a/src/model/Position.hpp +++ b/src/model/Position.hpp @@ -28,6 +28,8 @@ #include "Direction.hpp" +#include + namespace RPGEdit { @@ -41,19 +43,19 @@ struct Position { Position p = *this; switch (dir) { - case NORTH: + case Direction::NORTH: p.y -= amount; break; - case EAST: + case Direction::EAST: p.x += amount; break; - case SOUTH: + case Direction::SOUTH: p.y += amount; break; - case WEST: + case Direction::WEST: p.x -= amount; } @@ -64,6 +66,10 @@ struct Position { return translate(dir, 1); } + bool operator==(const Position &p) const { + return (x == p.x) && (y == p.y); + } + template explicit operator Position() const { return Position{T2(x), T2(y)}; @@ -73,3 +79,20 @@ struct Position { } } + +namespace std { +template struct hash> { + typedef size_t result_type; + typedef RPGEdit::Model::Position argument_type; + + size_t operator()(const RPGEdit::Model::Position &pos) const noexcept { + const int shift = 4*sizeof(size_t); // half the bit count of size_t + + std::hash hash; + size_t hash1 = hash(pos.x), hash2 = hash(pos.y); + + return hash1 ^ ((hash2 << shift) | (hash2 >> shift)); + } +}; + +} diff --git a/src/view/MapView.cpp b/src/view/MapView.cpp index 287fc87..78bc24c 100644 --- a/src/view/MapView.cpp +++ b/src/view/MapView.cpp @@ -113,7 +113,7 @@ void MapView::render(const Model::Map *map, Model::Position center, uint6 for (size_t layer = 0; layer < map->getLayerCount(); layer++) { for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { - uint32_t tile = map->getTileAt(layer, x, y); + uint32_t tile = map->getTileAt(layer, Model::Position{x, y}); if (!tile) continue; @@ -141,7 +141,7 @@ void MapView::render(const Model::Map *map, Model::Position center, uint6 Model::Direction dir = entity->getDirection(); SDL_Rect src = { - .x = getTileSize()*dir, + .x = getTileSize()*int(dir), .y = 0, .w = getTileSize(), .h = getTileSize(), -- cgit v1.2.3