First implementation of rules
TODO : - Fix target as getter (currently broken because of inheritance by rules) - Smart pointers for entity store ?
This commit is contained in:
parent
febe0827ad
commit
08e922b0d4
12 changed files with 257 additions and 20 deletions
|
@ -21,10 +21,10 @@ This game is aimed to be a puzzle game in which the player sets different laws f
|
|||
- [x] Moving
|
||||
- [ ] Decorators (Rules)
|
||||
- [ ] Rules
|
||||
- [ ] Creation
|
||||
- [ ] Interaction with entities
|
||||
- [x] Creation
|
||||
- [x] Interaction with entities
|
||||
- [ ] Parsing of rules and creation of subsequent decorators
|
||||
- [ ] Raytrace in order to find best goal for pathfinding ?
|
||||
- [x] Find optimal target for pathfinding
|
||||
- [ ] Graphics
|
||||
- [x] Scene rendering
|
||||
- [ ] UI
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
<Entity x="0" y="0" type="Citizen"/>
|
||||
<Entity x="0" y="1" type="Citizen"/>
|
||||
<Entity x="10" y="10" type="Significant" textureId="1"/>
|
||||
<Entity x="1" y="1" w="5" h="5" type="House" textureId="2"/>
|
||||
<Entity x="1" y="1" w="4" h="4" type="House" textureId="2"/>
|
||||
</Level>
|
|
@ -4,7 +4,7 @@ project(project_maat)
|
|||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
|
||||
|
||||
add_library(engine Level.cpp Level.h Entity.cpp Entity.h Game.cpp Game.h Utils.h Utils.h Utils.cpp)
|
||||
add_library(engine Level.cpp Level.h Entity.cpp Entity.h Game.cpp Game.h Utils.h Utils.h Utils.cpp Rule.cpp Rule.h)
|
||||
|
||||
target_link_libraries(engine
|
||||
sfml-window
|
||||
|
|
|
@ -33,6 +33,9 @@ Entity::Entity(const pugi::xml_node& entityNode, sf::Texture* texture)
|
|||
entityNode.attribute("w").as_int(1),
|
||||
entityNode.attribute("h").as_int(1)) {}
|
||||
|
||||
Entity::Entity() : Entity(0,0,EntityType::Citizen,nullptr,0,0)
|
||||
{}
|
||||
|
||||
void Entity::move(Orientation orientation)
|
||||
{
|
||||
// TODO : Add speed ?
|
||||
|
|
23
src/Entity.h
23
src/Entity.h
|
@ -45,21 +45,29 @@ public:
|
|||
Entity(pro_maat::GridUnit x, pro_maat::GridUnit y, EntityType type, sf::Texture* texture, int width = 1,
|
||||
int height = 1);
|
||||
explicit Entity(const pugi::xml_node& entityNode, sf::Texture* texture);
|
||||
virtual ~Entity() = default;
|
||||
|
||||
void move(Orientation orientation);
|
||||
void update();
|
||||
virtual void move(Orientation orientation);
|
||||
virtual void update();
|
||||
|
||||
State getState() const;
|
||||
virtual State getState() const;
|
||||
|
||||
const sf::RectangleShape& getShape() const;
|
||||
virtual const sf::RectangleShape& getShape() const;
|
||||
/// Returns the grid coordinates at the center of the entity
|
||||
const pro_maat::GridPos getPosition() const;
|
||||
virtual const pro_maat::GridPos getPosition() const;
|
||||
|
||||
// Don't like it : iterates over every square every tick
|
||||
const std::vector<pro_maat::GridPos> getOccupiedSquares() const;
|
||||
virtual const std::vector<pro_maat::GridPos> getOccupiedSquares() const;
|
||||
|
||||
// FIXME : Replace with getter
|
||||
/// Position of the target of the current action on the map
|
||||
pro_maat::GridPos target;
|
||||
|
||||
protected:
|
||||
|
||||
/// Empty constructor for derived class instanciation
|
||||
Entity();
|
||||
|
||||
private:
|
||||
static const std::map<std::string,EntityType> entityTypeLookup;
|
||||
|
||||
|
@ -75,6 +83,9 @@ private:
|
|||
|
||||
/// Used with rules : last to update has priority
|
||||
pro_maat::GridPos nextTarget;
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
friend class Rule;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#include "Entity.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// Used for convenience
|
||||
using TextureStore = std::vector<std::unique_ptr<sf::Texture>>;
|
||||
|
||||
class Game {
|
||||
public:
|
||||
|
@ -37,7 +35,7 @@ private:
|
|||
/// Store the paths to texture files
|
||||
const std::vector<std::string> textureFiles;
|
||||
/// Stores pointers to textures for future use
|
||||
TextureStore textures;
|
||||
pro_maat::TextureStore textures;
|
||||
|
||||
void loadTextures();
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "Level.h"
|
||||
|
||||
|
||||
Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore)
|
||||
Level::Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore)
|
||||
: size(xmlDoc.child("Level").attribute("w").as_int(),xmlDoc.child("Level").attribute("h").as_int()),
|
||||
textures(textureStore)
|
||||
{
|
||||
|
@ -18,6 +18,10 @@ Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore)
|
|||
|
||||
// Initialize the occupied squares vector with the new entity's squares
|
||||
std::vector<pro_maat::GridPos> entitySquares = entities.rbegin()->getOccupiedSquares();
|
||||
|
||||
// FIXME : For testing purposes
|
||||
Rule<State::Moving,EntityType::House> newRule(*entities.rbegin(),entities,occupiedSquares,size);
|
||||
|
||||
std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(occupiedSquares));
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +93,8 @@ Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int
|
|||
std::map<pro_maat::GridPos,pro_maat::GridPos> paths{};
|
||||
|
||||
|
||||
// TODO : Should be OK with raycasting to find goal
|
||||
// FIXME : Just get to goal (Might break)
|
||||
const std::vector<pro_maat::GridPos> goalNeighbours = pro_maat::getNeighbours(goal,size);
|
||||
// Save the iterators : vector is const and .begin() and .end() might get called a lot
|
||||
auto goalNeighboursBeginIterator = goalNeighbours.begin();
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
|
||||
#include "Utils.h"
|
||||
#include "Entity.h"
|
||||
#include "Rule.h"
|
||||
|
||||
using TextureStore = std::vector<std::unique_ptr<sf::Texture>>;
|
||||
|
||||
class Level {
|
||||
public:
|
||||
Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore);
|
||||
Level(const pugi::xml_document& xmlDoc, const pro_maat::TextureStore& textureStore);
|
||||
|
||||
void render(sf::RenderWindow& renderWindow) const;
|
||||
void runStep();
|
||||
|
@ -29,7 +29,7 @@ private:
|
|||
|
||||
std::vector<Entity> entities;
|
||||
|
||||
const TextureStore& textures;
|
||||
const pro_maat::TextureStore& textures;
|
||||
|
||||
//
|
||||
// Pathfinding
|
||||
|
|
5
src/Rule.cpp
Normal file
5
src/Rule.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
//
|
||||
// Created by trotfunky on 09/06/19.
|
||||
//
|
||||
|
||||
#include "Rule.h"
|
204
src/Rule.h
Normal file
204
src/Rule.h
Normal file
|
@ -0,0 +1,204 @@
|
|||
//
|
||||
// Created by trotfunky on 09/06/19.
|
||||
//
|
||||
|
||||
#ifndef PROJECT_MAAT_RULES_H
|
||||
#define PROJECT_MAAT_RULES_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
|
||||
/// Decorates entities with rules which will modify its behaviour
|
||||
template <State targetState, EntityType targetType>
|
||||
class Rule : public Entity
|
||||
{
|
||||
public:
|
||||
Rule(Entity& entity, const std::vector<Entity>& entities,
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares, const pro_maat::GridPos& mapSize);
|
||||
|
||||
void update() override;
|
||||
|
||||
void move(Orientation orientation) override;
|
||||
State getState() const override;
|
||||
const sf::RectangleShape& getShape() const override;
|
||||
const pro_maat::GridPos getPosition() const override;
|
||||
const std::vector<pro_maat::GridPos> getOccupiedSquares() const override;
|
||||
|
||||
private:
|
||||
/// Finds the closest free square adjacent to the closest target entity type.
|
||||
/// \return Suitable target square or current position if none was found.
|
||||
pro_maat::GridPos findTarget();
|
||||
|
||||
Entity& entity;
|
||||
const std::vector<Entity>& entities;
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares;
|
||||
|
||||
const pro_maat::GridPos& mapSize;
|
||||
};
|
||||
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
Rule<targetState, targetType>::Rule(Entity& entity,
|
||||
const std::vector<Entity>& entities,
|
||||
const std::vector<pro_maat::GridPos>& occupiedSquares,
|
||||
const pro_maat::GridPos& mapSize)
|
||||
: entity(entity),
|
||||
entities(entities),
|
||||
occupiedSquares(occupiedSquares),
|
||||
mapSize(mapSize)
|
||||
{}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
void Rule<targetState, targetType>::update()
|
||||
{
|
||||
if constexpr (targetState == State::Moving || targetState == State::Fleeing)
|
||||
{
|
||||
entity.nextTarget = findTarget();
|
||||
if(entity.nextTarget == entity.getPosition())
|
||||
{
|
||||
entity.nextState = State::Idle;
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.nextState = targetState;
|
||||
}
|
||||
entity.update();
|
||||
}
|
||||
else if constexpr (targetState == State::Waiting)
|
||||
{
|
||||
entity.nextTarget = entity.getPosition();
|
||||
entity.nextState = State::Waiting;
|
||||
entity.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.nextTarget = entity.getPosition();
|
||||
entity.nextState = State::Idle;
|
||||
entity.update();
|
||||
}
|
||||
}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
pro_maat::GridPos Rule<targetState, targetType>::findTarget()
|
||||
{
|
||||
std::vector<Entity> sortedEntities{};
|
||||
sortedEntities.insert(sortedEntities.end(),entities.begin(),entities.end());
|
||||
|
||||
// Compares entities via their distance to the current entity
|
||||
auto distanceSortEntities = [this](const Entity& leftHandSide, const Entity& rightHandSide){
|
||||
return (pro_maat::manhattanDistance(entity.getPosition(),leftHandSide.getPosition()) <
|
||||
pro_maat::manhattanDistance(entity.getPosition(),rightHandSide.getPosition()));
|
||||
};
|
||||
|
||||
// Same but with grid coordinates
|
||||
auto distanceSortSquares = [this](const pro_maat::GridPos& leftHandSide, const pro_maat::GridPos& rightHandSide){
|
||||
return (pro_maat::manhattanDistance(entity.getPosition(),leftHandSide) <
|
||||
pro_maat::manhattanDistance(entity.getPosition(),rightHandSide));
|
||||
};
|
||||
|
||||
// Get the smallest, non-occupied, inside the map square
|
||||
auto bestTarget = [this](const pro_maat::GridPos& leftHandSide, const pro_maat::GridPos& rightHandSide){
|
||||
// If the left hand side operand is not in the map or occupied, it is not valid
|
||||
if(!pro_maat::isInMap(leftHandSide,mapSize) ||
|
||||
std::find(occupiedSquares.begin(),occupiedSquares.end(),leftHandSide) != occupiedSquares.end())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
else if(!pro_maat::isInMap(rightHandSide,mapSize) ||
|
||||
std::find(occupiedSquares.begin(),occupiedSquares.end(),rightHandSide) != occupiedSquares.end())
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(leftHandSide < rightHandSide);
|
||||
}};
|
||||
|
||||
|
||||
// Sort in order to minimize entities to process
|
||||
std::sort(sortedEntities.begin(),sortedEntities.end(),distanceSortEntities);
|
||||
|
||||
for(const Entity& processingEntity : sortedEntities)
|
||||
{
|
||||
std::vector<pro_maat::GridPos> potentialTargets{};
|
||||
|
||||
pro_maat::GridUnit entityWidth = processingEntity.shape.getSize().x/pro_maat::pixelsPerUnit;
|
||||
pro_maat::GridUnit entityHeight = processingEntity.shape.getSize().y/pro_maat::pixelsPerUnit;
|
||||
|
||||
potentialTargets.reserve((entityWidth+2)*2+entityHeight*2);
|
||||
|
||||
// Computes the top left corner of the entity
|
||||
pro_maat::GridPos topLeftCorner = processingEntity.getPosition();
|
||||
topLeftCorner.first -= entityWidth*0.5 - 1;
|
||||
topLeftCorner.second -= entityHeight*0.5 - 1;
|
||||
|
||||
|
||||
// Get all the top and bottom adjacent squares
|
||||
for(int i = 0;i<entityWidth+2;i++)
|
||||
{
|
||||
potentialTargets.emplace_back(topLeftCorner.first+i,topLeftCorner.second);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+i,topLeftCorner.second+entityHeight+2);
|
||||
}
|
||||
// Get the missing adjacent squares from the sides
|
||||
for(int i = 1;i<=entityHeight;i++)
|
||||
{
|
||||
potentialTargets.emplace_back(topLeftCorner.first,topLeftCorner.second+i);
|
||||
potentialTargets.emplace_back(topLeftCorner.first+entityWidth+2,topLeftCorner.second+i);
|
||||
}
|
||||
|
||||
|
||||
std::sort(potentialTargets.begin(),potentialTargets.end(),distanceSortSquares);
|
||||
auto target = std::min_element(potentialTargets.begin(),potentialTargets.end(),bestTarget);
|
||||
|
||||
if(target != potentialTargets.end())
|
||||
{
|
||||
return (*target);
|
||||
}
|
||||
}
|
||||
|
||||
return (entity.getPosition());
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// //
|
||||
// Delegate overrides //
|
||||
// //
|
||||
////////////////////////
|
||||
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
void Rule<targetState, targetType>::move(Orientation orientation)
|
||||
{
|
||||
entity.move(orientation);
|
||||
}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
State Rule<targetState, targetType>::getState() const
|
||||
{
|
||||
return entity.getState();
|
||||
}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
const sf::RectangleShape& Rule<targetState, targetType>::getShape() const
|
||||
{
|
||||
return entity.getShape();
|
||||
}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
const pro_maat::GridPos Rule<targetState, targetType>::getPosition() const
|
||||
{
|
||||
return entity.getPosition();
|
||||
}
|
||||
|
||||
template<State targetState, EntityType targetType>
|
||||
const std::vector<pro_maat::GridPos> Rule<targetState, targetType>::getOccupiedSquares() const
|
||||
{
|
||||
return entity.getOccupiedSquares();
|
||||
}
|
||||
|
||||
|
||||
#endif //PROJECT_MAAT_RULES_H
|
|
@ -37,6 +37,12 @@ namespace pro_maat
|
|||
}
|
||||
}
|
||||
|
||||
bool isInMap(const GridPos& square, const GridPos& gridSize)
|
||||
{
|
||||
return (square.first < 0 || square.second < 0 ||
|
||||
square.first >= gridSize.first || square.second >= gridSize.second);
|
||||
}
|
||||
|
||||
float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide)
|
||||
{
|
||||
// The *0.01 helps with breaking ties and minimizing exploration
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
|
||||
namespace pro_maat
|
||||
{
|
||||
// Used as the base of all grid based computations
|
||||
/// Used for convenience
|
||||
using TextureStore = std::vector<std::unique_ptr<sf::Texture>>;
|
||||
/// Used as the base of all grid based computations
|
||||
using GridUnit = int16_t;
|
||||
// Used when dealing with the grid representation of the map
|
||||
/// Used when dealing with the grid representation of the map
|
||||
using GridPos = std::pair<GridUnit,GridUnit>;
|
||||
|
||||
static constexpr uint8_t pixelsPerUnit = 30;
|
||||
|
@ -24,6 +26,8 @@ static constexpr char fontFolder[] = "resources/";
|
|||
|
||||
void errorWindow(const std::string& error);
|
||||
|
||||
/// Test if the coordinate is inside the map or not
|
||||
bool isInMap(const GridPos& square, const GridPos& gridSize);
|
||||
// Good heuristic on 4-way grids
|
||||
float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide);
|
||||
/// Gets the 4 cardinal neighbours on the grid with edge checking
|
||||
|
|
Loading…
Add table
Reference in a new issue