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) {
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);
}
}

View file

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

View file

@ -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();
}
}
}
}

View file

@ -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 {

View file

@ -57,13 +57,9 @@ bool Map::moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end
entity->setDirection(dir);
Position<int> 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<int> 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> 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<int>{i, j}, 1);
map->setCollisionAt(Position<int>{i, j}, CollisionType::EMPTY);
}
else {
map->setTileAt(0, i, j, 0);
map->setTileAt(0, Position<int>{i, j}, 0);
}
if (4 <= i && i < 12 && j == 8)
map->setTileAt(1, i, j, 2);
map->setTileAt(1, Position<int>{i, j}, 2);
else
map->setTileAt(1, i, j, 0);
map->setTileAt(1, Position<int>{i, j}, 0);
}
}

View file

@ -30,6 +30,7 @@
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "CollisionType.hpp"
@ -68,12 +69,27 @@ private:
};
std::vector<std::unique_ptr<Entity>> entities;
std::unordered_map<Position<int>, std::unordered_set<Entity *>> positions;
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) {
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) {
@ -81,6 +97,37 @@ private:
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) {
}
@ -126,7 +173,6 @@ public:
Entity * addEntity(const std::string &name, const Position<int> &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<int> &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<int> &p) const {
if (p.x < 0 || size_t(p.x) >= width || p.y < 0 || size_t(p.y) >= height)
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) {
if (layer >= tiles.size() || x >= width || y >= height)
void setTileAt(size_t layer, const Position<int> &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<int> &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<float> getEntityPosition(const Entity *entity, uint64_t time) const;

View file

@ -28,6 +28,8 @@
#include "Direction.hpp"
#include <functional>
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<T> &p) const {
return (x == p.x) && (y == p.y);
}
template<typename T2>
explicit operator Position<T2>() const {
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 (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<int>{x, y});
if (!tile)
continue;
@ -141,7 +141,7 @@ void MapView::render(const Model::Map *map, Model::Position<float> center, uint6
Model::Direction dir = entity->getDirection();
SDL_Rect src = {
.x = getTileSize()*dir,
.x = getTileSize()*int(dir),
.y = 0,
.w = getTileSize(),
.h = getTileSize(),