diff --git a/CMakeLists.txt b/CMakeLists.txt index 5305a1d..86e56e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,55 +1,180 @@ cmake_minimum_required(VERSION 3.21) -project(CMakeSFMLProject LANGUAGES CXX) +# Compiler flags +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /EHsc" CACHE INTERNAL "" FORCE) +elseif(APPLE) + message("hello apple") + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9 CACHE STRING "") +endif() +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +project(PRACTICAL_7_PLATFORMER) include(FetchContent) -FetchContent_Declare(SFML - GIT_REPOSITORY https://github.com/SFML/SFML.git - GIT_TAG 2.6.x) -FetchContent_MakeAvailable(SFML) -# B2D - Box2D physics library -add_subdirectory("lib/b2d/Box2D") -#include_directories("lib/b2d/Box2D/") -set(B2D_INCS "lib/b2d/Box2D/") -link_directories("${CMAKE_BINARY_DIR}/lib/sfml/lib") +FetchContent_Declare( + sfml + GIT_REPOSITORY https://github.com/SFML/SFML.git + GIT_TAG 2.6.x +) +FetchContent_MakeAvailable(sfml) +# populate the lib directory with the sfml libraries +FetchContent_GetProperties(sfml) # can I get cmake to list out the names of the properties? A: yes, use the following line: get_cmake_property(_variableNames VARIABLES) +if(NOT sfml_POPULATED) + FetchContent_Populate(sfml) + add_subdirectory(${sfml_SOURCE_DIR} ${sfml_BINARY_DIR}) +endif() +get_cmake_property(_variableNames VARIABLES) +list(FILTER _variableNames INCLUDE REGEX "sfml") +message("variableNames: ${_variableNames}") + +foreach (_variableName ${_variableNames}) + message(STATUS "${_variableName}=${${_variableName}}") +endforeach() + +message ("SFML_SOURCE_DIR: ${sfml_SOURCE_DIR}") + +message ("SFML_SOURCE_DIR: ${sfml_SOURCE_DIR}") + +message("SFML_BINARY_DIR: ${sfml_BINARY_DIR}") + + +# The following downloads box2d into the build directory and builds it +FetchContent_Declare(box2d + GIT_REPOSITORY https://github.com/dooglz/Box2D.git + GIT_TAG v2.3.1) + +# We need finer control over the build of box2d so we set the following variables +set(BOX2D_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +if(NOT box2d_POPULATED) + FetchContent_Populate(box2d) + add_subdirectory(${box2d_SOURCE_DIR}/Box2D ${box2d_BINARY_DIR}) +endif() +get_cmake_property(_variableNamesbox VARIABLES) +list(FILTER _variableNamesbox INCLUDE REGEX "box") +message("_variableNamesbox: ${_variableNamesbox}") +foreach (_variableName ${_variableNamesbox}) + message(STATUS "${_variableName}=${${_variableName}}") +endforeach() + +#### setup Directories #### +#Main output directory +set(OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/") +# Ouput all DLLs from all libs into main build folder +set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) + +add_custom_target(copy_resources ALL COMMAND ${CMAKE_COMMAND} + -E copy_directory + "${PROJECT_SOURCE_DIR}/res" + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res +) + +# _________________________________________________________________________________________________________ +# -- The alternative was to use submodule (for both sfml and box2d - below) from github and use that to download and populate the directory - We went with the more "straightforward" approach of using fetchcontent +#### Add External Dependencies #### +# SFML - graphics library +#add_subdirectory("lib/sfml" EXCLUDE_FROM_ALL) +#include_directories("lib/sfml/include") +#set(SFML_INCS "lib/sfml/include") +#link_directories("${CMAKE_BINARY_DIR}/lib/sfml/lib") # Does fetchcontent do this for me? A: yes, but I need to tell it to do so by adding the following line: FetchContent_MakeAvailable(sfml) + +# B2D - Box2D phyics library +#add_subdirectory("lib/b2d/Box2D" EXCLUDE_FROM_ALL) +#include_directories("lib/b2d/Box2D/") +#set(B2D_INCS "lib/b2d/Box2D/") +#link_directories("${CMAKE_BINARY_DIR}/lib/sfml/lib") +# _________________________________________________________________________________________________________ + +# The following allows us to tell the compiler where the header files are for box2D. This is normally managed by fetchcontent but we need to do it manually for box2d - one of the problems in the build +set (B2D_INCS "${box2d_SOURCE_DIR}/Box2D") # note that fetchcontent will download the box2d source code into the box2d_SOURCE_DIR #### Add Engine and our own library projects#### +set (SFML_INCS "${sfml_SOURCE_DIR}/include") # note that fetchcontent will download the sfml source code into the sfml_SOURCE_DIR + +set (lib_box2d "${box2d_BINARY_DIR}/Box2D/libBox2D.a") # Again, not ideal, but we need to tell the compiler where the box2d library is. This is normally managed by fetchcontent but we need to do it manually for box2d - one of the problems in the build + + file(GLOB_RECURSE RESOURCE_FILES "res/*.*") -## Engine ## +#### file(GLOB_RECURSE SOURCE_FILES engine/*.cpp engine/*.h) add_library(lib_engine STATIC ${SOURCE_FILES} ${RESOURCE_FILES}) target_include_directories(lib_engine INTERFACE "${CMAKE_SOURCE_DIR}/engine/" PUBLIC SYSTEM ${B2D_INCS} ) target_link_libraries(lib_engine lib_tile_level_loader lib_maths lib_ecm Box2D sfml-graphics sfml-audio) +## Maths lib +add_library(lib_maths INTERFACE) +target_sources(lib_maths INTERFACE "${CMAKE_SOURCE_DIR}/lib_maths/maths.h") +target_include_directories(lib_maths INTERFACE "${CMAKE_SOURCE_DIR}/lib_maths" INTERFACE ${SFML_INCS}) + ## Tile loader lib file(GLOB_RECURSE SOURCE_FILES lib_tile_level_loader/*.cpp lib_tile_level_loader/*.h) -add_library(lib_tile_level_loader STATIC ${SOURCE_FILES}) +add_library(lib_tile_level_loader STATIC ${SOURCE_FILES} ${RESOURCE_FILES}) target_include_directories(lib_tile_level_loader INTERFACE "${CMAKE_SOURCE_DIR}/lib_tile_level_loader/" ) target_link_libraries(lib_tile_level_loader lib_maths sfml-graphics) -# Maths lib -add_library(lib_maths INTERFACE) -target_sources(lib_maths INTERFACE "${CMAKE_SOURCE_DIR}/lib_maths/maths.h") -target_include_directories(lib_maths INTERFACE "${CMAKE_SOURCE_DIR}/lib_maths" SYSTEM INTERFACE ${SFML_INCS}) - ## ECM lib file(GLOB_RECURSE SOURCE_FILES lib_ecm/*.cpp lib_ecm/*.h) add_library(lib_ecm STATIC ${SOURCE_FILES}) target_include_directories(lib_ecm INTERFACE "${CMAKE_SOURCE_DIR}/lib_ecm" ) target_link_libraries(lib_ecm PRIVATE lib_maths) +#### Add Practical Projects#### + +#@@- + +## 6 - Platformer +file(GLOB_RECURSE SOURCES lab_7_platformer/*.cpp practical_6_platformer/*.h) +file(GLOB_RECURSE CMPNTS lab_7_platformer/components/*.cpp practical_6_platformer/components/*.h) +file(GLOB_RECURSE SCENES lab_7_platformer/scenes/*.cpp practical_6_platformer/scenes/*.h) +add_executable(PRACTICAL_7_PLATFORMER ${SOURCES} ${RESOURCE_FILES}) +source_group("components" FILES ${CMPNTS}) +source_group("resources" FILES ${RESOURCE_FILES}) + + +target_link_libraries(PRACTICAL_7_PLATFORMER lib_engine) +set(EXECUTABLES ${EXECUTABLES} PRACTICAL_7_PLATFORMER) + + +if (WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(TARGET PRACTICAL_7_PLATFORMER POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS) +endif() + +add_dependencies(PRACTICAL_7_PLATFORMER copy_resources) + + +set_target_properties(PRACTICAL_7_PLATFORMER + PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$(Configuration) +) + +file(GLOB_RECURSE SOURCES game_project/*.cpp game_project/*.h) +file(GLOB_RECURSE CMPNTS game_project/components/*.cpp game_project/components/*.h) +file(GLOB_RECURSE SCENES game_project/scenes/*.cpp game_project/scenes/*.h) +file(GLOB_RECURSE AI game_project/ai/*.cpp game_project/ai/*.h) +add_executable(GAME_PROJECT ${SOURCES} ${RESOURCE_FILES}) +source_group("components" FILES ${CMPNTS}) +source_group("resources" FILES ${RESOURCE_FILES}) +source_group("ai" FILES ${AI}) + +target_link_libraries(GAME_PROJECT lib_engine) +set(EXECUTABLES ${EXECUTABLES} GAME_PROJECT) -add_executable(CMakeSFMLProject src/main.cpp src/platformer.cpp) -target_link_libraries(CMakeSFMLProject PRIVATE sfml-graphics) -target_compile_features(CMakeSFMLProject PRIVATE cxx_std_17) if (WIN32 AND BUILD_SHARED_LIBS) - add_custom_command(TARGET CMakeSFMLProject POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS) + add_custom_command(TARGET GAME_PROJECT POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS) endif() -install(TARGETS CMakeSFMLProject) +add_dependencies(GAME_PROJECT copy_resources) + + +set_target_properties(GAME_PROJECT + PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$(Configuration) +) +#@@- \ No newline at end of file diff --git a/engine/engine.cpp b/engine/engine.cpp index 065ceac..e072480 100644 --- a/engine/engine.cpp +++ b/engine/engine.cpp @@ -81,7 +81,7 @@ void Engine::Render(RenderWindow& window) { void Engine::Start(unsigned int width, unsigned int height, const std::string& gameName, Scene* scn) { - RenderWindow window(VideoMode(width, height), gameName); + RenderWindow window(VideoMode(width,height), gameName); _gameName = gameName; _window = &window; Renderer::initialise(window); @@ -175,6 +175,7 @@ sf::Vector2u Engine::getWindowSize() { return _window->getSize(); } sf::RenderWindow& Engine::GetWindow() { return *_window; } + namespace timing { // Return time since Epoc long long now() { diff --git a/engine/engine.h b/engine/engine.h index 870cbd5..942c96f 100644 --- a/engine/engine.h +++ b/engine/engine.h @@ -45,6 +45,7 @@ class Engine { static void Render(sf::RenderWindow& window); }; + namespace timing { // Return time since Epoc long long now(); diff --git a/game_project/README.md b/game_project/README.md new file mode 100644 index 0000000..60ca927 --- /dev/null +++ b/game_project/README.md @@ -0,0 +1,112 @@ +# Game Design Document + +## Introduction + +This document describes the game Tile Running, which is designed to be a simple, 2D grid game, constructed and designed to study different types of AI in a grid-like environment while playing a 'mouse and rat' game. The game employs a grid environment with different levels, each one with its own objectives, enemies, AI algorithms, collectible items, and memory and scene management. + +## Technology + +The game was developed with PC's in mind, but it can be easily ported to other platforms as it programmed in C++. The Game Engine was built above the SFML library, which provided the foundation for I/O handling. The Game Engine consists of: + +- **System Renderer**: controls the presentation of elements present within a scene; +- **System Resources**: responsable to import external assets, as fonts, sounds and textures. +- **Engine**: handles initialization of the systems and scenes. +- **Level Tile Loader**: builds the level layout based on a text file that represents the map, e.g. walls, objectives, player spawn, enemies spawn etc. +- **Maths library**: extension of SFML namespace, utilized for making mathematical operations with 2D vectors. +- **Entity/Component Library**: the basis for game systems and interactions between different elements by providing a framework in which each *entity* behaves and acts based on its *components*. The Fly-Weight pattern is implemented on this library, as each entity is constructed based on its own characteristics. + +The artwork is entirely generated by simple shapes provided by SFML, that being squares, rectangles and circles. These can be quickly adapted to comport external assets by using the **Resources**+**Renderer** systems. + +## Backstory + +After stealing chemical compounds from a pharmaceutical company in a plain 2D world, **Green Square** (you) need to run away with it avoiding getting caught by the smart **Graph Search Force** dressed in Magenta. Sadly, **Green** is not very good handling hazardous liquids, what can leave **Yellow** squared slimes behind it, who are very toxic! Meanwhile, it is useful to get **Blue Circle** water on the way, so **Green** gets faster. + +## Objective + +- Get all blue collectibles and reach the red square while avoiding getting caught by either the Yellow or Magenta squares. + + +## Gameplay + +The game works above a grid-like environment with top-down view of the tiles, in which every character can only move to the neighbour tile. The movement can be only up, down, left or right at a time. + +All the tiles are passable, the only exception being the walls and enemies. **Green** is not so resistant, so it will instantly die by touching an enemy or hazardous slime. Speaking of, the two different enemies work as it's described bellow: + +- **Magenta**: will go after Green or Water by the best path found on the grid. +- **Yellow**: they will follow Green's pace and path, being always behind it. This type of enemy can kill the Magenta. + +The collectibles are circular, blue tiles that they give more speed to Green and the Magenta, while also unlocking the door to the next level. So it is important to collect as much as you can before the Magenta do! + +## Levels + +There are five levels programmed and they were made on a text file as well as programmed on the source code. + +1. **Introduction**: a straight forward road to the Red tile, enemies around the player and they will start to move after a few seconds. +2. **Labyrinth**: One Magenta lurks behind one of the labyrinth walls. The Red is by the end of it. +3. **Hazardous Smiles and Water**: Just like the first level, but now the enemies are Yellows and Green needs to drink Water. +4. **Another Labyrinth**: In another labyrinth, Green needs to grab water while avoid the Yellow smiles. +5. **Altogether**: The last level and final challenge, the Magenta track down Green and now it needs to deal with both the Magenta as well as the Yellow, while thirsty! + + +## Controls + +The commands are implemented within the player's movement component and additional keys specific to each scene are implemented within it's update function. + +- UP/W: goes north; +- LEFT/A: goes west; +- RIGHT/D: goes east; +- DOWN/S: goes down; +- N: skips level (cheat code if needed). + + +## GUI/In-game menu + +The GUI is implemented only in the main menu with the usage of buttons. These were programmed using a component that controls input and changes from scenes that connects to the Main Menu. + + +## Artwork + +The artwork is generated entirely by mathematical function within the Tile Level Loader, that generated the sprites utilized for drawing the levels. Shapes were based on the SFML library. + +# Development Process + +The beginning of the development process surrounded understanding the "in's and out's" of the framework as well as how the algorithms for the AI worked. For this, the first step was drafting a class diagram that could explain how the classes interacts with each other. It can be seen bellow: + +![image](https://github.com/GameDevCPP/platformer-RysanDeluna/assets/82891214/a509729d-c6e8-4cd7-92cb-7e4a2ffd0472) + +The diagram shows how the framework was first developed to the Platformer game, but it gives a general idea of how the different systems interact with each other, as well as how the entity/component design works. +This was the base for what would become TILE RUNNING. + +## Regarding the AI + +By studying how an pathfinding algorithm would work, it was decided that the game would be better suited in a grid-like map, as the graph implementation would be much easier. +As the tiles are not weighted, the Dijsktra Algorithm or the A* were not considered. + +Furthermore, this type of AI guided the decision for approaching a top-down view of the grid and the construction of the levels, which are made with how the AI works. + +The AI code is mainly inside the components relevant to them, but the graph implementation is found within an AI directory. + +## Additions to the framework + +Some components were added beyond what was given by the framework. These are: + +- BFS_AI: adds to the ActorMovementComponent by implementing the breadth first search and making the movements based on the path found. +- Collectables: component responsable to delete the entity and power other entities with the ActorMovementComponent +- Kill: this component makes the owner entity to kill another determined type of entity, auto-destroying itself in the process. +- Pursuer_AI: when an Entity owns this component, it will mimic the movements made by another entity; a better of making this work is spawning the parent on the same tile as the mimicking target. + +Besides the components development, some changes were made on the already given source codes. + +- Tile Loader: + - Now the tiles possess a outline color and empty tiles have a gray outline; + - A function that returns the grid coordinates of a float position; + - The optimise function is only active to the WALL and END tiles. + +- Text Component: + - It is positioned wherever it's parent is. + +## Levels Development + +The levels follow the rule of first introducing a simple mechanic, followed by a much harder experience with that single mechanic. For example, the first level introduces the Type 1 enemy, while the second level is a maze with that kind of enemy. The same is done for further mechanics. + +The mazes were based on the [dungeon generator made by Donjon](https://donjon.bin.sh/d20/dungeon/) with alterations that would make a better gaming experience. diff --git a/game_project/ai/Graph.cpp b/game_project/ai/Graph.cpp new file mode 100644 index 0000000..38f1fc1 --- /dev/null +++ b/game_project/ai/Graph.cpp @@ -0,0 +1,47 @@ +// +// Created by rysan on 03/12/23. +// + +#include "Graph.h" + +// GRAPH --------------------------------------------------------------- +Graph::Graph(uint w, uint h) : _width(w), _height(h) {} + +std::array Graph::DIRS = { sf::Vector2f{1.f, 0.f}, // Right + sf::Vector2f{-1.f, 0.f}, // Left + sf::Vector2f{0.f, 1.f}, // Down + sf::Vector2f{0.f, -1.f} // UP +}; + +uint Graph::getHeight() { return _height; } +uint Graph::getWidth() { return _width; } + +void Graph::setHeight(uint h) { _height = h; } +void Graph::setWidth(uint w) { _width = w; } + +bool Graph::passable(sf::Vector2f coord) const +{ + return ls::getTile(sf::Vector2ul(coord)) != ls::WALL; +} + +bool Graph::in_bounds(sf::Vector2f coord) const +{ + return coord.x >= 0 && coord.x <= ls::getWidth() + && coord.y >= 0 && coord.y <= ls::getHeight(); +} + +std::vector Graph::neighbors(sf::Vector2ul me) const +{ + std::vector neighbors; + + for (auto dir : DIRS) + { + auto next = sf::Vector2f{me.x + dir.x, me.y + dir.y}; + if (in_bounds(next) && passable(next)) + neighbors.push_back(sf::Vector2ul(next)); + } + if (int(me.x + me.y) % 2 == 0) std::reverse(neighbors.begin(), neighbors.end()); + + return neighbors; +} +// GRAPH --------------------------------------------------------------- diff --git a/game_project/ai/Graph.h b/game_project/ai/Graph.h new file mode 100644 index 0000000..3f15997 --- /dev/null +++ b/game_project/ai/Graph.h @@ -0,0 +1,29 @@ +// +// Created by rysan on 03/12/23. +// +#pragma once + +#include +#include +#include + +struct Graph { + uint _width; + uint _height; + + static std::array DIRS; + + Graph(uint w, uint h); + ~Graph() = default; + + bool passable(sf::Vector2f coord) const; + bool in_bounds(sf::Vector2f coord) const; + std::vector neighbors (sf::Vector2ul me) const; + + uint getHeight(); + uint getWidth(); + void setHeight(uint h); + void setWidth(uint w); +}; + + diff --git a/game_project/components/cmp_actor_movement.cpp b/game_project/components/cmp_actor_movement.cpp new file mode 100644 index 0000000..918166b --- /dev/null +++ b/game_project/components/cmp_actor_movement.cpp @@ -0,0 +1,71 @@ +// +// Created by rysan on 29/11/23. +// + +#include "cmp_actor_movement.h" +#include +#include +#include + +void ActorMovementComponent::update(double dt) {} + +ActorMovementComponent::ActorMovementComponent(Entity *p, float s) + : _speed(s), Component(p) {} + + +// Check if the current movement is valid +bool ActorMovementComponent::validMove(const sf::Vector2f & pos) +{ + return (LevelSystem::getTileAt(pos) != LevelSystem::WALL); +} + +void ActorMovementComponent::setSpeed(float speed) { _speed = speed; } + +/* Actual movement ------------------------------------- */ +// x == +1 -> right | y == +1 -> down +// x == -1 -> left | y == -1 -> up +/* ----------------------------------------------------- */ +bool ActorMovementComponent::move(float x, float y) +{ + return move(sf::Vector2f (x, y)); +} + +bool ActorMovementComponent::move(const sf::Vector2f & pos) +{ + auto pp = _parent->getPosition() + ls::getOffset() + ls::getTileSize() * pos; + bool valid = validMove(pp); + if (valid) + _parent->setPosition(pp); + return valid; +} + +float ActorMovementComponent::getSpeed() const { + return _speed; +} + +void ActorMovementComponent::increaseSpeed(float speed) { + + if (_speed > 0.085 )_speed -= speed; + else _speed = 0.085; +} + +// **** PLAYER **** + +PlayerMoveComponent::PlayerMoveComponent(Entity *p, float s) : + ActorMovementComponent(p, s), _timer(0){} + +void PlayerMoveComponent::update(double dt) +{ + _timer-=dt; + bool moved = false; + if (_timer <= 0) + { + if (sf::Keyboard::isKeyPressed(sf::Keyboard::W) || sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) moved = move(0.f,-1.f); + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) moved = move(0.f, 1.f); + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A) || sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) moved = move(-1.f, 0.f); + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) moved = move(1.f, 0.f); + } + if (moved) _timer=_speed; +} + + diff --git a/game_project/components/cmp_actor_movement.h b/game_project/components/cmp_actor_movement.h new file mode 100644 index 0000000..5fafbce --- /dev/null +++ b/game_project/components/cmp_actor_movement.h @@ -0,0 +1,36 @@ +// +// Created by rysan on 29/11/23. +// +#pragma once +#include +#include +#include "SFML/Window/Keyboard.hpp" + + +class ActorMovementComponent : public Component { +protected: + bool validMove(const sf::Vector2f&); + float _speed; // Limit the amount of moves per second + +public: + void update(double dt) override; + bool move(const sf::Vector2f&); + bool move(float x, float y); + void render() override {} + void setSpeed(float speed); + void increaseSpeed(float speed); + float getSpeed() const; + explicit ActorMovementComponent(Entity* p, float s = 1.f); + ActorMovementComponent() = delete; +}; + +class PlayerMoveComponent : public ActorMovementComponent { +public: + void update(double dt) override; + void render() override {}; + explicit PlayerMoveComponent(Entity *p, float s = 1.f); + PlayerMoveComponent()=delete; + +private: + double _timer; +}; \ No newline at end of file diff --git a/game_project/components/cmp_bfs_ai.cpp b/game_project/components/cmp_bfs_ai.cpp new file mode 100644 index 0000000..ee8f098 --- /dev/null +++ b/game_project/components/cmp_bfs_ai.cpp @@ -0,0 +1,78 @@ +// +// Created by rysan on 04/12/23. +// + +#include "cmp_bfs_ai.h" +#include +#include + +std::unordered_map breadth_first_search(Graph *graph, sf::Vector2ul start, sf::Vector2ul goal) +{ + std::queue frontier; + std::unordered_map came_from; + + frontier.push(start); + // Store the path to the goal for each tile + came_from[start] = start; + + while(!frontier.empty()) + { + sf::Vector2ul current = frontier.front(); + frontier.pop(); + + if(current == goal) { break; } // Found the target + + for(sf::Vector2ul next_dir : graph->neighbors(current)) { + if (came_from.find(next_dir) == came_from.end()) { + frontier.push(next_dir); + came_from[next_dir] = current; + } + } + } + return came_from; +} + +AIBFSComponent::AIBFSComponent(Entity *p, const std::shared_ptr& g, sf::Vector2ul goal) + : ActorMovementComponent(p, 0.8f), G_(g), goal_(goal), _mtimer(2.f), goal_changed(true) {} + +void AIBFSComponent::update(double dt) +{ + _mtimer -= dt; + sf::Vector2ul my_coord = ls::getTileCoord(_parent->getPosition()); + + // Search the target + if(goal_changed) + { + current_path_ = breadth_first_search(G_.get(), my_coord, goal_); + goal_changed = false; + } + // Moves towards it + if(_mtimer <= 0 && my_coord != goal_) + { + sf::Vector2ul next = goal_; + + while(true) + { + if(current_path_[next] != my_coord) + { + next = current_path_[next]; + } + else break; + } + + sf::Vector2f dir(0.f,0.f); + + if(next.y < my_coord.y) dir.y = -1.f; + if(next.y > my_coord.y) dir.y = 1.f; + if(next.x < my_coord.x) dir.x = -1.f; + if(next.x > my_coord.x) dir.x = 1.f; + + move(dir.x, dir.y); + _mtimer = _speed; + } +} + +void AIBFSComponent::setGoal(sf::Vector2ul new_goal) +{ + if (new_goal != goal_) { goal_ = new_goal; goal_changed=true; } +} diff --git a/game_project/components/cmp_bfs_ai.h b/game_project/components/cmp_bfs_ai.h new file mode 100644 index 0000000..090b656 --- /dev/null +++ b/game_project/components/cmp_bfs_ai.h @@ -0,0 +1,28 @@ +// +// Created by rysan on 04/12/23. +// +#pragma once + +#include +#include +#include "../ai/Graph.h" +#include "../components/cmp_actor_movement.h" + +class AIBFSComponent : public ActorMovementComponent { +public: + AIBFSComponent() = delete; + explicit AIBFSComponent(Entity *p, const std::shared_ptr& g, sf::Vector2ul goal); + ~AIBFSComponent() override = default; + + void update(double dt) override; + void setGoal(sf::Vector2ul new_goal); + void render() override {} + +private: + std::shared_ptr G_; + double _mtimer; + sf::Vector2ul goal_; + bool goal_changed; + std::unordered_map current_path_; +}; + diff --git a/game_project/components/cmp_button.cpp b/game_project/components/cmp_button.cpp new file mode 100644 index 0000000..ee0e597 --- /dev/null +++ b/game_project/components/cmp_button.cpp @@ -0,0 +1,94 @@ +// Button component C++ file +#include"cmp_button.h" +#include +#include +#include +#include "engine.h" +#include "../game.h" + +using namespace sf; +using namespace std; + +unique_ptr