Collision rework
This commit is contained in:
parent
8b4ca336ce
commit
c3e32345e5
8 changed files with 135 additions and 46 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace RPGEdit {
|
|||
|
||||
namespace Model {
|
||||
|
||||
enum CollisionType {
|
||||
enum class CollisionType {
|
||||
BLOCKED = 0,
|
||||
EMPTY,
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
Reference in a new issue