diff --git a/README.md b/README.md index 4ded453..acb88d6 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ This game is aimed to be a puzzle game in which the player sets different laws f - [ ] Creation - [ ] Interaction with entities - [ ] Parsing of rules and creation of subsequent decorators - - [ ] Raytrace in order to find best goal for pathfinding ? - [ ] Graphics - [x] Scene rendering - [ ] UI @@ -34,5 +33,4 @@ This game is aimed to be a puzzle game in which the player sets different laws f - [ ] Entity behaviour evolution - [ ] Rule application - [ ] Saving state through serialization ? - - [ ] UML - - [ ] Unit tests \ No newline at end of file + - [ ] UML \ No newline at end of file diff --git a/UML_Class_Diagram.png b/UML_Class_Diagram.png index e5faeff..7800665 100644 Binary files a/UML_Class_Diagram.png and b/UML_Class_Diagram.png differ diff --git a/resources/test_level.xml b/resources/test_level.xml index 0c7812f..65faa1f 100644 --- a/resources/test_level.xml +++ b/resources/test_level.xml @@ -1,7 +1,7 @@ - + - + \ No newline at end of file diff --git a/src/Entity.cpp b/src/Entity.cpp index 163bcd9..921df1f 100644 --- a/src/Entity.cpp +++ b/src/Entity.cpp @@ -19,10 +19,9 @@ Entity::Entity(int x, int y, EntityType type, sf::Texture* texture, int width, i shape.setPosition((x+0.5*width)*pro_maat::pixelsPerUnit,(y+0.5*width)*pro_maat::pixelsPerUnit); shape.setTexture(texture); - // FIXME : Testing purposes - currentState = State::Moving; + currentState = State::Idle; nextState = State::Idle; - target = pro_maat::GridPos(x+10,y); + target = sf::Vector2i(shape.getPosition()); nextTarget = target; } @@ -37,6 +36,7 @@ Entity::Entity(const pugi::xml_node& entityNode, sf::Texture* texture) void Entity::move(Orientation orientation) { // TODO : Add speed ? + shape.setRotation(static_cast(orientation)); sf::Vector2f movementVector(0,0); switch (orientation) @@ -53,11 +53,8 @@ void Entity::move(Orientation orientation) case Orientation::West: movementVector.x = -pro_maat::pixelsPerUnit; break; - case Orientation::None: - return; } - shape.setRotation(static_cast(orientation)); shape.setPosition(shape.getPosition()+movementVector); } @@ -74,25 +71,22 @@ const State Entity::getState() const return currentState; } -const pro_maat::GridPos Entity::getPosition() const +const sf::Vector2i Entity::getPosition() const { // Safe : size is a multiple of pro_maat::pixelsPerUnit - pro_maat::GridUnit x = shape.getPosition().x/pro_maat::pixelsPerUnit; - pro_maat::GridUnit y = shape.getPosition().y/pro_maat::pixelsPerUnit; - return pro_maat::GridPos(x,y); + uint8_t x = (shape.getPosition().x-0.5*shape.getSize().x)/pro_maat::pixelsPerUnit; + uint8_t y = (shape.getPosition().y-0.5*shape.getSize().y)/pro_maat::pixelsPerUnit; + return sf::Vector2i(x,y); } -const std::vector Entity::getOccupiedSquares() const +const std::vector> Entity::getOccupiedSquares() const { - std::vector occupiedSquares; - + std::vector> occupiedSquares; // Safe : size is a multiple of pro_maat::pixelsPerUnit - pro_maat::GridUnit w = shape.getSize().x/pro_maat::pixelsPerUnit; - pro_maat::GridUnit h = shape.getSize().y/pro_maat::pixelsPerUnit; - pro_maat::GridUnit x = shape.getPosition().x/pro_maat::pixelsPerUnit - 0.5*w; - pro_maat::GridUnit y = shape.getPosition().y/pro_maat::pixelsPerUnit - 0.5*h; - - occupiedSquares.reserve(w*h); + uint8_t w = shape.getSize().x/pro_maat::pixelsPerUnit; + uint8_t h = shape.getSize().y/pro_maat::pixelsPerUnit; + uint8_t x = shape.getPosition().x/pro_maat::pixelsPerUnit - 0.5*w; + uint8_t y = shape.getPosition().y/pro_maat::pixelsPerUnit - 0.5*h; for(int i = 0;i getOccupiedSquares() const; + const std::vector> getOccupiedSquares() const; + /// Position of the target of the current action on the map - pro_maat::GridPos target; + sf::Vector2i target; private: static const std::map entityTypeLookup; EntityType type; // As it contains position, size and orientation, we do not need anything more - // TODO : Maybe we need something more : lots of computations sf::RectangleShape shape; State currentState; @@ -73,7 +69,7 @@ private: State nextState; /// Used with rules : last to update has priority - pro_maat::GridPos nextTarget; + sf::Vector2i nextTarget; }; diff --git a/src/Level.cpp b/src/Level.cpp index 5a446b0..e5c4e22 100644 --- a/src/Level.cpp +++ b/src/Level.cpp @@ -7,7 +7,7 @@ Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore) : textures(textureStore), - size(xmlDoc.child("Level").attribute("w").as_int(),xmlDoc.child("Level").attribute("h").as_int()) + size(xmlDoc.child("Level").attribute("width").as_int(),xmlDoc.child("Level").attribute("width").as_int()) { pugi::xml_node levelNode = xmlDoc.child("Level"); for(const pugi::xml_node& child : levelNode.children()) @@ -17,7 +17,7 @@ Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore) entities.emplace_back(child,textures.at(child.attribute("textureId").as_int(0)).get()); // Initialize the occupied squares vector with the new entity's squares - std::vector entitySquares = entities.rbegin()->getOccupiedSquares(); + std::vector> entitySquares = entities.rbegin()->getOccupiedSquares(); std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(occupiedSquares)); } } @@ -33,7 +33,7 @@ void Level::render(sf::RenderWindow& renderWindow) const void Level::runStep() { - std::vector newOccupiedSquares{}; + std::vector> newOccupiedSquares{}; newOccupiedSquares.reserve(occupiedSquares.size()); for(Entity& entity: entities) @@ -62,14 +62,8 @@ void Level::runStep() case State::Idle:break; } - std::vector entitySquares = entity.getOccupiedSquares(); - - // FIXME : Very heavy memory usage and a lot of duplicates, slows down occupiedSquares.find() calls too. - // Copy the squares to the currently occupied squares : prevents moving into an entity that just moved - occupiedSquares.insert(occupiedSquares.end(),entitySquares.begin(),entitySquares.end()); - std::sort(occupiedSquares.begin(),occupiedSquares.end()); - // Moves the occupied squares from the entity to the new occupied squares vector + std::vector> entitySquares = entity.getOccupiedSquares(); std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(newOccupiedSquares)); } @@ -78,108 +72,8 @@ void Level::runStep() std::sort(occupiedSquares.begin(),occupiedSquares.end()); } -Orientation Level::findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int sign) +Orientation Level::findPath(sf::Vector2i start, sf::Vector2i end, int sign) { - std::set openNodes{start}; - std::set closedNodes{}; - - std::map estimatedCosts{{start,pro_maat::manhattanDistance(start,goal)*sign}}; - std::map pathCosts{{start,0}}; - std::map paths{}; - - - const std::vector 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(); - auto goalNeighboursEndIterator = goalNeighbours.end(); - - - // FIXME : Find an efficient way to get rid of openNodes.find calls - // Lambda checking if the current element is also in the open nodes set - auto compWithOpen = [&openNodes](const std::pair& leftHandSide, - const std::pair& rightHandSide){ - if(openNodes.find(leftHandSide.first) == openNodes.end()) - { - return (false); - } - else if(openNodes.find(rightHandSide.first) == openNodes.end()) - { - return (true); - } - else - { - return (leftHandSide.second < rightHandSide.second); - } - }; - - - while(!openNodes.empty()) - { - // Expand from the open node with the smallest estimated cost - pro_maat::GridPos currentNode = std::min_element(estimatedCosts.begin(),estimatedCosts.end(),compWithOpen)->first; - - if(std::find(goalNeighboursBeginIterator,goalNeighboursEndIterator,currentNode) != goalNeighboursEndIterator) - { - // Trace back to the start - pro_maat::GridPos& previousNode = paths[currentNode]; - for(;paths[previousNode]!=start;previousNode = paths[previousNode]) - {} - - pro_maat::GridUnit dx = previousNode.first - start.first; - pro_maat::GridUnit dy = previousNode.second - start.second; - - if(dx < 0) - { - return(Orientation::West); - } - else if(dx > 0) - { - return(Orientation::East); - } - else if(dy < 0) - { - return(Orientation::North); - } - else if(dy > 0 ) - { - return(Orientation::South); - } - else - { - return(Orientation::None); - } - } - - openNodes.erase(currentNode); - closedNodes.insert(currentNode); - - for(pro_maat::GridPos neighbour : pro_maat::getNeighbours(currentNode,size)) - { - // Checks if node has been closed or is an obstacle - if(std::find(occupiedSquares.begin(),occupiedSquares.end(),neighbour) != occupiedSquares.end() || - closedNodes.find(neighbour) != closedNodes.end()) - { - continue; - } - - // As the neighbours are adjacent squares, distance from them is always +-1 - float newPathCost = pathCosts[currentNode] + sign; - - if(openNodes.find(neighbour) == openNodes.end()) - { - openNodes.insert(neighbour); - } - else if(newPathCost >= pathCosts[neighbour]) // If the node is open but this path is longer, ignore it - { - continue; - } - - pathCosts.insert_or_assign(neighbour,newPathCost); - estimatedCosts.insert_or_assign(neighbour,newPathCost + pro_maat::manhattanDistance(neighbour,goal)); - auto returnPathIt = paths.insert_or_assign(neighbour,currentNode); - } - } - - // If we did not find a path, do not move - return Orientation::None; + // TODO : A* which returns the next move + return Orientation::East; } diff --git a/src/Level.h b/src/Level.h index 7f4f8c8..681a6a6 100644 --- a/src/Level.h +++ b/src/Level.h @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include "Utils.h" #include "Entity.h" @@ -25,7 +23,7 @@ public: void runStep(); private: - const pro_maat::GridPos size; + const sf::Vector2i size; std::vector entities; @@ -35,17 +33,9 @@ private: // Pathfinding // - std::vector occupiedSquares; + std::vector> occupiedSquares; - /// If sign is +1, finds a path between start and goal positions. - /// If sign is -1, finds a path away from the goal position, from the starting position. - /// THIS DOES NOT HANDLE ENTITIES BIGGER THAN ONE SQUARE - /// - /// \param start Starting position - /// \param goal Goal position - /// \param sign +1 or -1, defines if we go towards or away from the goal - /// \return Orientation of the next move if successful, Orientation::None otherwise. - Orientation findPath(pro_maat::GridPos start, pro_maat::GridPos goal, int sign); + Orientation findPath(sf::Vector2i start, sf::Vector2i end, int sign); }; diff --git a/src/Utils.cpp b/src/Utils.cpp index 4e820ca..4c75c2f 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -36,34 +36,4 @@ namespace pro_maat window.display(); } } - - float manhattanDistance(const GridPos& leftHandSide, const GridPos& rightHandSide) - { - // The *0.01 helps with breaking ties and minimizing exploration - // As per http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties - return (std::abs(rightHandSide.first-leftHandSide.first)+ - std::abs(rightHandSide.second-leftHandSide.second))*1.01; - } - - const std::vector getNeighbours(const pro_maat::GridPos& origin, const GridPos& gridSize) - { - std::vector neighbours{}; - neighbours.reserve(4); - - for(GridUnit i = std::max(origin.first-1,0);i<=std::min(static_cast(origin.first+1),gridSize.first);i++) - { - // Do not add the original position - if(i == origin.first) continue; - - neighbours.emplace_back(i,origin.second); - } - for(GridUnit i = std::max(origin.second-1,0);i<=std::min(static_cast(origin.second+1),gridSize.second);i++) - { - if(i == origin.second) continue; - - neighbours.emplace_back(origin.first,i); - } - - return std::move(neighbours); - } } \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h index 4491e10..4632cb0 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -12,22 +12,14 @@ namespace pro_maat { -// Used as the base of all grid based computations -using GridUnit = int16_t; -// Used when dealing with the grid representation of the map -using GridPos = std::pair; -static constexpr uint8_t pixelsPerUnit = 30; +static constexpr uint8_t pixelsPerUnit = 50; static constexpr char levelFolder[] = "resources/"; static constexpr char textureFolder[] = "resources/"; static constexpr char fontFolder[] = "resources/"; void errorWindow(const std::string& error); -// 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 -const std::vector getNeighbours(const GridPos& origin, const GridPos& gridSize); } #endif //PROJECT_MAAT_UTILS_H