Move external entity state from Entity to Map

This commit is contained in:
Matthias Schiffer 2014-09-25 20:53:17 +02:00
parent eb40e3de72
commit d82f3b7665
10 changed files with 177 additions and 111 deletions

View file

@ -5,7 +5,6 @@ add_executable(rpgedit
rpgedit.cpp
control/MapContext.cpp
control/RPGEdit.cpp
model/Entity.cpp
model/Map.cpp
view/MapView.cpp
view/SpriteCache.cpp

View file

@ -35,9 +35,7 @@ MapContext::MapContext(EventBus *eventBus0, InputHandler *inputHandler0, const s
: eventBus(eventBus0), inputHandler(inputHandler0), map(map0) {
view = std::unique_ptr<View::MapView>(new View::MapView(window, map.getTileset()));
map.getEntities().emplace_back("square");
playerEntity = &map.getEntities().back();
playerEntity->moveTo(&map, Model::Position{8, 8});
playerEntity = map.addEntity("square", Model::Position{8, 8});
view->updateEntities(map.getEntities());
@ -50,12 +48,12 @@ MapContext::MapContext(EventBus *eventBus0, InputHandler *inputHandler0, const s
}
void MapContext::movePlayer(Model::Direction dir, uint64_t time) {
if (!playerEntity->move(&map, dir, time, time+250))
if (!map.moveEntity(playerEntity, dir, time, time+250))
return;
eventBus->enqueue(
[=] {
playerEntity->finishTransition(&map);
map.finishEntityTransition(playerEntity);
movePlayerContinue(time+250);
},
time+250

View file

@ -54,7 +54,7 @@ private:
void keyPressed(uint16_t key, uint64_t time);
Model::Position getViewPosition(uint64_t time) {
return playerEntity->getPosition(time);
return map.getEntityPosition(playerEntity, time);
}
public:

View file

@ -24,44 +24,17 @@
*/
#include "Entity.hpp"
#include "Map.hpp"
#pragma once
namespace RPGEdit {
namespace Model {
bool Entity::move(Map *map, Direction dir, uint64_t start, uint64_t end) {
if (hasTransition())
return false;
direction = dir;
Position newPos = translate(dir, 1);
if (map->getCollisionAt(newPos.x, newPos.y) == Map::BLOCKED)
return false;
map->setCollisionAt(newPos.x, newPos.y, Map::BLOCKED);
transitionStart = start;
transitionEnd = end;
return true;
}
void Entity::moveTo(Map *map, Position newPos) {
map->setCollisionAt(pos.x, pos.y, Map::EMPTY);
map->setCollisionAt(newPos.x, newPos.y, Map::BLOCKED);
pos = newPos;
transitionStart = transitionEnd = 0;
}
void Entity::finishTransition(Map *map) {
if (hasTransition())
moveTo(map, translate(direction, 1));
}
enum CollisionType {
BLOCKED = 0,
EMPTY,
};
}

View file

@ -39,41 +39,12 @@ namespace RPGEdit {
namespace Model {
class Map;
class Entity {
private:
const std::string name;
std::string name;
Position pos;
Direction direction;
uint64_t transitionStart = 0;
uint64_t transitionEnd = 0;
Position translate(Direction dir, float amount) const {
Position p = pos;
switch (dir) {
case NORTH:
p.y -= amount;
break;
case EAST:
p.x += amount;
break;
case SOUTH:
p.y += amount;
break;
case WEST:
p.x -= amount;
}
return p;
}
public:
Entity(const std::string &name0)
: name(name0), direction(NORTH) {
@ -83,29 +54,13 @@ public:
return name;
}
Position getPosition(uint64_t time) const {
if (hasTransition())
if (time <= transitionStart)
return pos;
else if (time >= transitionEnd)
return translate(direction, 1);
else
return translate(direction, float(time-transitionStart)/float(transitionEnd-transitionStart));
else
return pos;
}
Direction getDirection() const {
return direction;
}
bool hasTransition() const {
return transitionEnd;
void setDirection(Direction dir) {
direction = dir;
}
void moveTo(Map *map, Position newPos);
bool move(Map *map, Direction dir, uint64_t start, uint64_t end);
void finishTransition(Map *map);
};
}

View file

@ -31,6 +31,62 @@ namespace RPGEdit {
namespace Model {
Position Map::getEntityPosition(const Entity *entity, uint64_t time) const {
const EntityState &state = entityStates.at(entity);
if (state.transitionEnd)
if (time <= state.transitionStart)
return state.position;
else if (time >= state.transitionEnd)
return state.position + state.direction;
else
return state.position.translate(state.direction,
float(time-state.transitionStart)/
float(state.transitionEnd-state.transitionStart));
else
return state.position;
}
bool Map::moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end) {
EntityState &state = entityStates.at(entity);
if (state.transitionEnd)
return false;
entity->setDirection(dir);
Position pos = state.position + dir;
if (getCollisionAt(pos.x, pos.y) == BLOCKED)
return false;
setCollisionAt(pos.x, pos.y, BLOCKED);
state.transitionStart = start;
state.transitionEnd = end;
state.direction = dir;
return true;
}
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);
state.position = pos;
state.transitionStart = state.transitionEnd = 0;
}
void Map::finishEntityTransition(Entity *entity) {
EntityState &state = entityStates.at(entity);
if (state.transitionEnd)
moveEntityTo(entity, state.position + state.direction);
}
std::unique_ptr<Map> Map::load(__attribute__((unused)) const std::string &name) {
std::unique_ptr<Map> map(new Map(16, 16, 2));
@ -54,8 +110,7 @@ std::unique_ptr<Map> Map::load(__attribute__((unused)) const std::string &name)
}
}
map->entities.emplace_back("square");
map->entities.back().moveTo(map.get(), Model::Position{6, 6});
map->addEntity("square", Model::Position{6, 6});
return map;
}

View file

@ -27,11 +27,12 @@
#pragma once
#include <cstdint>
#include <deque>
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include <vector>
#include "CollisionType.hpp"
#include "Entity.hpp"
@ -39,29 +40,77 @@ namespace RPGEdit {
namespace Model {
class Map {
public:
enum CollisionType {
BLOCKED = 0,
EMPTY,
};
private:
class _Map {
protected:
std::vector<std::string> tileset;
size_t width, height;
std::vector<CollisionType> collision;
std::vector<std::vector<uint32_t>> tiles;
std::deque<Entity> entities;
Map(size_t width0, size_t height0, size_t layers)
_Map(size_t width0, size_t height0, size_t layers)
: width(width0), height(height0), collision(width*height) {
for (size_t i = 0; i < layers; i++)
tiles.emplace_back(width*height);
}
};
class Map : private _Map {
private:
struct EntityState {
Position position;
Direction direction;
uint64_t transitionStart = 0;
uint64_t transitionEnd = 0;
EntityState(const Position &position0) : position(position0) {}
};
std::vector<std::unique_ptr<Entity>> entities;
std::unordered_map<const Entity *, EntityState> entityStates;
void pushEntity(Entity *entity, EntityState &&state) {
entities.push_back(std::unique_ptr<Entity>(entity));
entityStates.emplace(entity, state);
}
void copyEntities(const Map &other) {
for (auto &e : other.entities)
pushEntity(new Entity(*e), EntityState(other.entityStates.at(e.get())));
}
Map(size_t width0, size_t height0, size_t layers) : _Map(width0, height0, layers) {
}
public:
Map & operator=(const Map &other) {
static_cast<_Map>(*this) = other;
copyEntities(other);
return *this;
}
Map(const Map &other) : _Map(other) {
copyEntities(other);
}
Map & operator=(Map &&other) {
static_cast<_Map>(*this) = std::move(other);
entities = std::move(other.entities);
entityStates = std::move(other.entityStates);
return *this;
}
Map(Map &&other) : _Map(std::move(other)) {
entities = std::move(other.entities);
entityStates = std::move(other.entityStates);
}
std::vector<std::string> & getTileset() {
return tileset;
}
@ -70,12 +119,15 @@ public:
return tileset;
}
std::deque<Entity> & getEntities() {
const std::vector<std::unique_ptr<Entity>> & getEntities() const {
return entities;
}
const std::deque<Entity> & getEntities() const {
return entities;
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;
}
size_t getWidth() const {
@ -92,7 +144,7 @@ public:
void setCollisionAt(size_t x, size_t y, CollisionType value) {
if (x >= width || y >= height)
throw std::range_error("Map::setTileAt: bad coordinates");
throw std::range_error("Map::setCollisionAt: bad coordinates");
collision[y*width + x] = value;
}
@ -119,6 +171,11 @@ public:
return tiles[layer][y*width + x];
}
Position getEntityPosition(const Entity *entity, uint64_t time) const;
bool moveEntity(Entity *entity, Direction dir, uint64_t start, uint64_t end);
void moveEntityTo(Entity *entity, Position pos);
void finishEntityTransition(Entity *entity);
static std::unique_ptr<Map> load(const std::string &name);
};

View file

@ -26,6 +26,8 @@
#pragma once
#include "Direction.hpp"
namespace RPGEdit {
@ -33,6 +35,33 @@ namespace Model {
struct Position {
float x, y;
Position translate(Direction dir, float amount) const {
Position p = *this;
switch (dir) {
case NORTH:
p.y -= amount;
break;
case EAST:
p.x += amount;
break;
case SOUTH:
p.y += amount;
break;
case WEST:
p.x -= amount;
}
return p;
}
Position operator+(Direction dir) const {
return translate(dir, 1);
}
};
}

View file

@ -76,11 +76,11 @@ MapView::~MapView() {
clearEntities();
}
void MapView::updateEntities(const std::deque<Model::Entity> &entities) {
void MapView::updateEntities(const std::vector<std::unique_ptr<Model::Entity>> &entities) {
SpriteCache *spriteCache = window->getSpriteCache();
for (auto &entity : entities) {
const std::string &name = entity.getName();
const std::string &name = entity->getName();
if (!entitySprites[name])
entitySprites[name] = SDL_CreateTextureFromSurface(window->getRenderer(), spriteCache->get("entity", name));
@ -137,8 +137,8 @@ void MapView::render(const Model::Map *map, Model::Position center, uint64_t tim
}
for (auto &entity : map->getEntities()) {
Model::Position pos = entity.getPosition(time);
Model::Direction dir = entity.getDirection();
Model::Position pos = map->getEntityPosition(entity.get(), time);
Model::Direction dir = entity->getDirection();
SDL_Rect src = {
.x = getTileSize()*dir,
@ -154,7 +154,7 @@ void MapView::render(const Model::Map *map, Model::Position center, uint64_t tim
.h = tilePixels,
};
SDL_RenderCopy(window->getRenderer(), entitySprites[entity.getName()], &src, &dst);
SDL_RenderCopy(window->getRenderer(), entitySprites[entity->getName()], &src, &dst);
}
}

View file

@ -53,7 +53,7 @@ public:
MapView(const std::shared_ptr<Window> &window0, const std::vector<std::string> &tileset);
~MapView();
void updateEntities(const std::deque<Model::Entity> &entities);
void updateEntities(const std::vector<std::unique_ptr<Model::Entity>> &entities);
void clearEntities();
void clear();