Compare commits
5 commits
46d26b6e61
...
1a79679c21
Author | SHA1 | Date | |
---|---|---|---|
1a79679c21 | |||
d53f8b32e9 | |||
81364c7b4c | |||
dafc442512 | |||
3666b9e7e9 |
18 changed files with 269 additions and 47 deletions
|
@ -13,19 +13,19 @@ This game is aimed to be a puzzle game in which the player sets different laws f
|
|||
|
||||
- [ ] Level
|
||||
- [x] Structure
|
||||
- [ ] Parsing from XML
|
||||
- [x] Parsing from XML
|
||||
- [ ] A*
|
||||
- [ ] Entities
|
||||
- [x] Structure
|
||||
- [ ] Parsing from XML
|
||||
- [ ] Moving
|
||||
- [x] Parsing from XML
|
||||
- [x] Moving
|
||||
- [ ] Decorators (Rules)
|
||||
- [ ] Rules
|
||||
- [ ] Creation
|
||||
- [ ] Interaction with entities
|
||||
- [ ] Parsing of rules and creation of subsequent decorators
|
||||
- [ ] Graphics
|
||||
- [ ] Scene rendering
|
||||
- [x] Scene rendering
|
||||
- [ ] UI
|
||||
- [ ] Menu ?
|
||||
- [ ] Gameloop
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
BIN
resources/Building.png
Normal file
BIN
resources/Building.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 930 B |
BIN
resources/Head_Boy.png
Normal file
BIN
resources/Head_Boy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 770 B |
BIN
resources/Head_Significant_Boy.png
Normal file
BIN
resources/Head_Significant_Boy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 829 B |
|
@ -2,5 +2,6 @@
|
|||
<Level w="10" h="10" textureId="0">
|
||||
<Entity x="0" y="0" type="Citizen"/>
|
||||
<Entity x="0" y="1" type="Citizen"/>
|
||||
<Entity x="1" y="1" type="House"/>
|
||||
<Entity x="10" y="10" type="Significant" textureId="1"/>
|
||||
<Entity x="1" y="1" w="8" h="8" type="House" textureId="2"/>
|
||||
</Level>
|
BIN
resources/verdana.ttf
Normal file
BIN
resources/verdana.ttf
Normal file
Binary file not shown.
|
@ -4,9 +4,10 @@ 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)
|
||||
add_library(engine Level.cpp Level.h Entity.cpp Entity.h Game.cpp Game.h Utils.h Utils.h Utils.cpp)
|
||||
|
||||
target_link_libraries(engine
|
||||
sfml-window
|
||||
sfml-graphics
|
||||
sfml-system)
|
||||
sfml-system
|
||||
pugixml)
|
|
@ -6,36 +6,95 @@
|
|||
|
||||
const std::map<std::string,EntityType> Entity::entityTypeLookup = {
|
||||
{"Citizen",EntityType::Citizen},
|
||||
{"Player",EntityType::Player},
|
||||
{"Significant",EntityType::Significant},
|
||||
{"House",EntityType::House},
|
||||
{"Car",EntityType::Car}};
|
||||
|
||||
Entity::Entity(int x, int y, EntityType type, int width, int height)
|
||||
Entity::Entity(int x, int y, EntityType type, sf::Texture* texture, int width, int height) : type(type)
|
||||
{
|
||||
shape = sf::RectangleShape(sf::Vector2f(width*pro_maat::pixelsPerUnit,height*pro_maat::pixelsPerUnit));
|
||||
// Sets the origin at the center of the entity
|
||||
shape.setOrigin(shape.getSize()/2.0f);
|
||||
// Adjust position for offset origin
|
||||
shape.setPosition((x+0.5*width)*pro_maat::pixelsPerUnit,(y+0.5*width)*pro_maat::pixelsPerUnit);
|
||||
shape.setTexture(texture);
|
||||
|
||||
currentState = State::Idle;
|
||||
nextState = State::Idle;
|
||||
target = sf::Vector2i(shape.getPosition());
|
||||
nextTarget = target;
|
||||
}
|
||||
|
||||
Entity::Entity(const pugi::xml_node& entityNode)
|
||||
{
|
||||
Entity::Entity(const pugi::xml_node& entityNode, sf::Texture* texture)
|
||||
: Entity(entityNode.attribute("x").as_int(),
|
||||
entityNode.attribute("y").as_int(),
|
||||
entityTypeLookup.at(entityNode.attribute("type").as_string()),
|
||||
texture,
|
||||
entityNode.attribute("w").as_int(1),
|
||||
entityNode.attribute("h").as_int(1)) {}
|
||||
|
||||
void Entity::move(Orientation orientation)
|
||||
{
|
||||
// TODO : Add speed ?
|
||||
shape.setRotation(static_cast<float>(orientation));
|
||||
|
||||
sf::Vector2f movementVector(0,0);
|
||||
switch (orientation)
|
||||
{
|
||||
case Orientation::North:
|
||||
movementVector.y = -pro_maat::pixelsPerUnit;
|
||||
break;
|
||||
case Orientation::East:
|
||||
movementVector.x = pro_maat::pixelsPerUnit;
|
||||
break;
|
||||
case Orientation::South:
|
||||
movementVector.y = pro_maat::pixelsPerUnit;
|
||||
break;
|
||||
case Orientation::West:
|
||||
movementVector.x = -pro_maat::pixelsPerUnit;
|
||||
break;
|
||||
}
|
||||
|
||||
void Entity::render(sf::RenderWindow& renderWindow) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Entity::move()
|
||||
{
|
||||
|
||||
shape.setPosition(shape.getPosition()+movementVector);
|
||||
}
|
||||
|
||||
void Entity::update()
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
const sf::RectangleShape& Entity::getShape() const
|
||||
{
|
||||
return(shape);
|
||||
}
|
||||
|
||||
const State Entity::getState() const
|
||||
{
|
||||
return currentState;
|
||||
}
|
||||
|
||||
const sf::Vector2i Entity::getPosition() const
|
||||
{
|
||||
// Safe : size is a multiple of pro_maat::pixelsPerUnit
|
||||
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<std::pair<uint8_t, uint8_t>> Entity::getOccupiedSquares() const
|
||||
{
|
||||
std::vector<std::pair<uint8_t, uint8_t>> occupiedSquares;
|
||||
// Safe : size is a multiple of pro_maat::pixelsPerUnit
|
||||
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<w;i++)
|
||||
{
|
||||
for(int j = 0;j<h;j++)
|
||||
{
|
||||
occupiedSquares.emplace_back(x+i,y+j);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(occupiedSquares);
|
||||
}
|
||||
|
|
25
src/Entity.h
25
src/Entity.h
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <pugixml.hpp>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
|
@ -14,7 +15,7 @@
|
|||
enum class EntityType
|
||||
{
|
||||
Citizen,
|
||||
Player,
|
||||
Significant,
|
||||
House,
|
||||
Car,
|
||||
};
|
||||
|
@ -29,10 +30,10 @@ enum class State
|
|||
|
||||
enum class Orientation
|
||||
{
|
||||
Nort,
|
||||
East,
|
||||
South,
|
||||
West,
|
||||
North = 0,
|
||||
East = 90,
|
||||
South = 180,
|
||||
West = 270,
|
||||
};
|
||||
|
||||
|
||||
|
@ -40,20 +41,26 @@ class Entity
|
|||
{
|
||||
public:
|
||||
/// x,y, width and height are in grid coordinates
|
||||
Entity(int x, int y, EntityType type, int width = 1, int height = 1);
|
||||
Entity(const pugi::xml_node& entityNode);
|
||||
Entity(int x, int y, EntityType type, sf::Texture* texture, int width = 1, int height = 1);
|
||||
explicit Entity(const pugi::xml_node& entityNode, sf::Texture* texture);
|
||||
|
||||
void render(sf::RenderWindow& renderWindow) const;
|
||||
void move();
|
||||
void move(Orientation orientation);
|
||||
void update();
|
||||
|
||||
const sf::RectangleShape& getShape() const;
|
||||
const State getState() const;
|
||||
const sf::Vector2i getPosition() const;
|
||||
// Don't like it : iterates over every square every tick
|
||||
const std::vector<std::pair<uint8_t, uint8_t>> getOccupiedSquares() const;
|
||||
|
||||
|
||||
/// Position of the target of the current action on the map
|
||||
sf::Vector2i target;
|
||||
private:
|
||||
static const std::map<std::string,EntityType> entityTypeLookup;
|
||||
|
||||
EntityType type;
|
||||
|
||||
// As it contains position, size and orientation, we do not need anything more
|
||||
sf::RectangleShape shape;
|
||||
|
||||
|
|
40
src/Game.cpp
40
src/Game.cpp
|
@ -7,22 +7,20 @@
|
|||
#include "Game.h"
|
||||
|
||||
|
||||
Game::Game(const std::vector<std::string>& levels, const std::vector<std::string>& textures)
|
||||
Game::Game(std::vector<std::string>& levels, std::vector<std::string>& textures) : levelFiles(std::move(levels)),
|
||||
textureFiles(std::move(textures))
|
||||
{
|
||||
|
||||
|
||||
loadTextures();
|
||||
}
|
||||
|
||||
void Game::loadLevel(int levelId)
|
||||
{
|
||||
pugi::xml_document document;
|
||||
pugi::xml_parse_result result = document.load_file(levelFiles.at(levelId).c_str());
|
||||
pugi::xml_parse_result result = document.load_file((pro_maat::levelFolder+levelFiles.at(levelId)).c_str());
|
||||
|
||||
if(!result)
|
||||
{
|
||||
std::cerr << "Error while loading level :\n" << "\t" << result.description() << std::endl;
|
||||
exit(-1);
|
||||
pro_maat::errorWindow(result.description());
|
||||
}
|
||||
|
||||
currentLevel = std::make_unique<Level>(document,textures);
|
||||
|
@ -34,11 +32,39 @@ void Game::loadTextures()
|
|||
for(const std::string& filePath : textureFiles)
|
||||
{
|
||||
textures.emplace_back(std::make_unique<sf::Texture>());
|
||||
textures.back()->loadFromFile(filePath);
|
||||
textures.back()->loadFromFile(pro_maat::textureFolder+filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::runGame()
|
||||
{
|
||||
sf::RenderWindow window(sf::VideoMode(1280,1024),"Project Maat");
|
||||
|
||||
sf::Clock clock;
|
||||
|
||||
while (window.isOpen())
|
||||
{
|
||||
sf::Event event;
|
||||
while (window.pollEvent(event))
|
||||
{
|
||||
if (event.type == sf::Event::Closed)
|
||||
{
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
if (clock.getElapsedTime().asMilliseconds() >= 200)
|
||||
{
|
||||
currentLevel->runStep();
|
||||
clock.restart();
|
||||
}
|
||||
window.clear(sf::Color::White);
|
||||
// TODO : Consider drawing in a sf::RenderTexture to allow positioning the level at a fixed position ?
|
||||
currentLevel->render(window);
|
||||
window.display();
|
||||
}
|
||||
}
|
||||
|
||||
Game::operator bool() const
|
||||
{
|
||||
return (currentLevel ? true : false);
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
#include "Level.h"
|
||||
#include "Entity.h"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
// Used for convenience
|
||||
using TextureStore = std::vector<std::unique_ptr<sf::Texture>>;
|
||||
|
||||
class Game {
|
||||
public:
|
||||
Game(const std::vector<std::string>& levels, const std::vector<std::string>& textures);
|
||||
Game(std::vector<std::string>& levels, std::vector<std::string>& textures);
|
||||
|
||||
/// Loads the level of corresponding ID from Game::levelFiles
|
||||
/// Closes the program if there is a fatal error (Missing file, bad file...)
|
||||
|
@ -29,6 +29,8 @@ public:
|
|||
// This should not be called before a level has been loaded
|
||||
void runGame();
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
private:
|
||||
/// Store the paths to level files
|
||||
const std::vector<std::string> levelFiles;
|
||||
|
|
|
@ -14,17 +14,66 @@ Level::Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore)
|
|||
{
|
||||
if(!strncmp(child.name(),"Entity",6))
|
||||
{
|
||||
entities.emplace_back(child);
|
||||
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<std::pair<uint8_t,uint8_t>> entitySquares = entities.rbegin()->getOccupiedSquares();
|
||||
std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(occupiedSquares));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Level::render(sf::RenderWindow& renderWindow) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Level::runStep() const
|
||||
for(const Entity& entity : entities)
|
||||
{
|
||||
|
||||
renderWindow.draw(entity.getShape());
|
||||
}
|
||||
}
|
||||
|
||||
void Level::runStep()
|
||||
{
|
||||
std::vector<std::pair<uint8_t,uint8_t>> newOccupiedSquares{};
|
||||
newOccupiedSquares.reserve(occupiedSquares.size());
|
||||
|
||||
for(Entity& entity: entities)
|
||||
{
|
||||
entity.update();
|
||||
int heuristicSign = 0;
|
||||
switch (entity.getState())
|
||||
{
|
||||
case State::Moving:
|
||||
{
|
||||
heuristicSign = 1;
|
||||
}
|
||||
case State::Fleeing:
|
||||
{
|
||||
if(heuristicSign == 0)
|
||||
{
|
||||
heuristicSign = -1;
|
||||
}
|
||||
if(entity.target != entity.getPosition())
|
||||
{
|
||||
entity.move(findPath(entity.getPosition(),entity.target,heuristicSign));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::Waiting:break;
|
||||
case State::Idle:break;
|
||||
}
|
||||
|
||||
// Moves the occupied squares from the entity to the new occupied squares vector
|
||||
std::vector<std::pair<uint8_t,uint8_t>> entitySquares = entity.getOccupiedSquares();
|
||||
std::move(entitySquares.begin(),entitySquares.end(),std::back_inserter(newOccupiedSquares));
|
||||
}
|
||||
|
||||
occupiedSquares.swap(newOccupiedSquares);
|
||||
// Sort the vector as to get O(ln(n)) complexity when searching for a square
|
||||
std::sort(occupiedSquares.begin(),occupiedSquares.end());
|
||||
}
|
||||
|
||||
Orientation Level::findPath(sf::Vector2i start, sf::Vector2i end, int sign)
|
||||
{
|
||||
// TODO : A* which returns the next move
|
||||
return Orientation::East;
|
||||
}
|
||||
|
|
10
src/Level.h
10
src/Level.h
|
@ -20,7 +20,7 @@ public:
|
|||
Level(const pugi::xml_document& xmlDoc, const TextureStore& textureStore);
|
||||
|
||||
void render(sf::RenderWindow& renderWindow) const;
|
||||
void runStep() const;
|
||||
void runStep();
|
||||
|
||||
private:
|
||||
const sf::Vector2i size;
|
||||
|
@ -28,6 +28,14 @@ private:
|
|||
std::vector<Entity> entities;
|
||||
|
||||
const TextureStore& textures;
|
||||
|
||||
//
|
||||
// Pathfinding
|
||||
//
|
||||
|
||||
std::vector<std::pair<uint8_t,uint8_t>> occupiedSquares;
|
||||
|
||||
Orientation findPath(sf::Vector2i start, sf::Vector2i end, int sign);
|
||||
};
|
||||
|
||||
|
||||
|
|
39
src/Utils.cpp
Normal file
39
src/Utils.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Created by trotfunky on 07/06/19.
|
||||
//
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
namespace pro_maat
|
||||
{
|
||||
void errorWindow(const std::string& error)
|
||||
{
|
||||
sf::Font font;
|
||||
if(!font.loadFromFile(std::string(pro_maat::fontFolder)+"verdana.ttf"))
|
||||
{
|
||||
std::cerr << "Error while loading font for error :" << std::endl;
|
||||
std::cerr << error << std::endl;
|
||||
}
|
||||
|
||||
sf::Text errorMessage(error,font);
|
||||
errorMessage.setFillColor(sf::Color::Red);
|
||||
sf::Rect errorArea = errorMessage.getGlobalBounds();
|
||||
errorMessage.setPosition(errorArea.width*(1.5/2-0.5),errorArea.height*(0.5));
|
||||
sf::RenderWindow window(sf::VideoMode(errorArea.width*1.5,errorArea.height*2),"ERROR");
|
||||
while (window.isOpen())
|
||||
{
|
||||
sf::Event event;
|
||||
while(window.pollEvent(event))
|
||||
{
|
||||
if (event.type == sf::Event::Closed || event.type == sf::Event::KeyPressed || event.type == sf::Event::MouseButtonPressed)
|
||||
{
|
||||
window.close();
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
window.clear(sf::Color::White);
|
||||
window.draw(errorMessage);
|
||||
window.display();
|
||||
}
|
||||
}
|
||||
}
|
11
src/Utils.h
11
src/Utils.h
|
@ -6,12 +6,19 @@
|
|||
#define PROJECT_MAAT_UTILS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <string>
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace pro_maat
|
||||
{
|
||||
|
||||
static constexpr uint8_t pixelsPerUnit = 20;
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,13 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
|
|||
|
||||
add_executable(gTests gTests.cpp gTests.cpp)
|
||||
|
||||
target_include_directories(gTests PRIVATE ${PROJECT_SOURCE_DIR}/../src)
|
||||
|
||||
target_link_libraries(gTests
|
||||
engine
|
||||
gtest
|
||||
pthread)
|
||||
pthread
|
||||
sfml-window
|
||||
sfml-graphics
|
||||
sfml-system
|
||||
pugixml)
|
|
@ -3,6 +3,23 @@
|
|||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
TEST(Setup,GameSetup)
|
||||
{
|
||||
// TODO : Think about parsing from file ? Currently cumbersome and error-prone
|
||||
std::vector<std::string> textures = {"Head_Boy.png","Head_Significant_Boy.png","Building.png"};
|
||||
std::vector<std::string> levels = {"test_level.xml"};
|
||||
|
||||
Game game(levels,textures);
|
||||
ASSERT_FALSE(game);
|
||||
game.loadLevel(0);
|
||||
ASSERT_TRUE(game);
|
||||
game.runGame();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue