From 5fb654acc636fed75b1720baf30c6081f3ed5dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=BD=20=D0=94=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=BB=D0=B0=20=D0=9C=D0=B8=D1=85=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=B8=D1=87?= <dmpechenin@edu.hse.ru> Date: Sat, 8 Mar 2025 13:17:31 +0300 Subject: [PATCH] Concrete entities were created + some changes in SelectState's + assets' logic --- CMakeLists.txt | 2 ++ source/Configuration.h | 18 +++++----- .../GameState/Entities/DynamicEntities.cpp | 28 +++++++++++++++ .../GameState/Entities/DynamicEntities.h | 24 +++++++++++++ source/States/GameState/Entities/Pacman.cpp | 19 ++++++++++ source/States/GameState/Entities/Pacman.h | 12 +++++++ .../GameState/Entities/StaticEntities.cpp | 20 +++++++++++ .../GameState/Entities/StaticEntities.h | 17 +++++++++ source/States/GameState/GameContext.cpp | 12 +++++++ source/States/GameState/GameContext.h | 12 +++++++ source/States/GameState/Maze/Room.cpp | 3 +- source/States/GameState/Maze/Room.h | 4 +-- source/States/GameState/Maze/RoomSide.cpp | 33 ++++++++++++++++- source/States/GameState/Maze/RoomSide.h | 2 +- source/States/SelectState/SelectState.cpp | 36 ++++++++++--------- source/States/SelectState/SelectState.h | 2 +- 16 files changed, 212 insertions(+), 32 deletions(-) create mode 100644 source/States/GameState/Entities/DynamicEntities.cpp create mode 100644 source/States/GameState/Entities/DynamicEntities.h create mode 100644 source/States/GameState/Entities/Pacman.cpp create mode 100644 source/States/GameState/Entities/Pacman.h create mode 100644 source/States/GameState/Entities/StaticEntities.cpp create mode 100644 source/States/GameState/Entities/StaticEntities.h create mode 100644 source/States/GameState/GameContext.cpp create mode 100644 source/States/GameState/GameContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fd6f28c..f5380fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,4 +14,6 @@ FetchContent_MakeAvailable(SFML) add_executable(${PROJECT_NAME} ${SOURCES}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/source) +target_compile_definitions(${PROJECT_NAME} PUBLIC ASSETS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/assets/") + target_link_libraries(${PROJECT_NAME} PRIVATE sfml-system sfml-window sfml-graphics) \ No newline at end of file diff --git a/source/Configuration.h b/source/Configuration.h index 4fc5e2d..a1ada6c 100644 --- a/source/Configuration.h +++ b/source/Configuration.h @@ -8,7 +8,7 @@ namespace config { const sf::Vector2f BUTTON_SIZE = { 250, 100 }; const size_t BUTTON_FONT_SIZE = static_cast<size_t>(BUTTON_SIZE.y / 1.5f); constexpr float BUTTON_FRAME_THICKNESS = 2.0f; - constexpr char FONT_FILE[] = "calibril.ttf"; + constexpr char FONT_FILE[] = ASSETS_PATH "calibril.ttf"; constexpr char SELECT_LEVEL_TITLE[] = "Select Level"; const sf::VideoMode SELECT_LEVEL_VIDEO_MODE{ 400, 600 }; constexpr char BUTTON_TEXT_EASY[] = "Easy"; @@ -23,11 +23,11 @@ namespace config { // constexpr float EASY_GAME_ENEMY_RATIO = 0.0f; // constexpr float MEDIUM_GAME_ENEMY_RATIO = 0.03f; // constexpr float HARD_GAME_ENEMY_RATIO = 0.07f; - // constexpr float ROOM_SIZE = 50; - // constexpr float GAME_ENEMY_SIZE = ROOM_SIZE * 0.7; - // constexpr float GAME_FOOD_SIZE = ROOM_SIZE * 0.2; + constexpr float ROOM_SIZE = 50; ///< @todo надо использовать + constexpr float GAME_ENEMY_SIZE = ROOM_SIZE * 0.7; + constexpr float GAME_FOOD_SIZE = ROOM_SIZE * 0.2; // Пакмэн: - // constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.8; + constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.8; // constexpr sf::Keyboard::Key KEY_LEFT = sf::Keyboard::A; // constexpr sf::Keyboard::Key KEY_RIGHT = sf::Keyboard::D; // constexpr sf::Keyboard::Key KEY_UP = sf::Keyboard::W; @@ -41,9 +41,9 @@ namespace config { // const sf::Color GAME_COLOR_BACKGROUND_INGAME{ 230,230,230 }; // const sf::Color GAME_COLOR_BACKGROUND_WIN{ 0, 255, 0 }; // const sf::Color GAME_COLOR_BACKGROUND_LOST{ 255, 0, 0 }; - // const sf::Color GAME_COLOR_PACMAN{ 250, 150, 0 }; + const sf::Color GAME_COLOR_PACMAN{ 250, 150, 0 }; // const sf::Color GAME_COLOR_ROOM{ 255, 255, 255 }; - // const sf::Color GAME_COLOR_WALL{ 0, 0, 0 }; - // const sf::Color GAME_FOOD_COLOR{ 0, 200, 100 }; - // const sf::Color GAME_ENEMY_COLOR{ 255, 50, 0 }; + const sf::Color GAME_COLOR_WALL{ 0, 0, 0 }; + const sf::Color GAME_FOOD_COLOR{ 0, 200, 100 }; + const sf::Color GAME_ENEMY_COLOR{ 255, 50, 0 }; } \ No newline at end of file diff --git a/source/States/GameState/Entities/DynamicEntities.cpp b/source/States/GameState/Entities/DynamicEntities.cpp new file mode 100644 index 0000000..8c4cd83 --- /dev/null +++ b/source/States/GameState/Entities/DynamicEntities.cpp @@ -0,0 +1,28 @@ +#include <Configuration.h> +#include <States/GameState/Entities/DynamicEntities.h> + +Enemy::Enemy() : m_rectangle({config::GAME_ENEMY_SIZE, config::GAME_ENEMY_SIZE}) { + m_rectangle.setFillColor(config::GAME_ENEMY_COLOR); + m_rectangle.setOrigin({config::GAME_ENEMY_SIZE/2, config::GAME_ENEMY_SIZE/2}); +} + +std::unique_ptr<IDynamicEntity> Enemy::clone() const { + return std::make_unique<Enemy>(*this); +} + +void Enemy::action() { + if (const auto miliseconds = static_cast<size_t>(m_stopwatch.getElapsedTime().asMilliseconds()); + miliseconds < m_dist_milliseconds(m_rng)) + return; + const auto direction = static_cast<Room::Direction>(m_dist_direction(m_rng)); + m_ptr_room->get_side(direction)->enter(this); + m_stopwatch.restart(); +} + +void Enemy::draw_into(sf::RenderWindow& window) const { + window.draw(m_rectangle); +} + +void Enemy::prepare_for_drawing() { + m_rectangle.setPosition(m_ptr_room->get_position()); +} diff --git a/source/States/GameState/Entities/DynamicEntities.h b/source/States/GameState/Entities/DynamicEntities.h new file mode 100644 index 0000000..b8be739 --- /dev/null +++ b/source/States/GameState/Entities/DynamicEntities.h @@ -0,0 +1,24 @@ +#pragma once +#include <States/GameState/Entities/IEntity.h> +#include <random> + +struct IDynamicEntity : IEntity { + [[nodiscard]] virtual std::unique_ptr<IDynamicEntity> clone() const = 0; + virtual void action() = 0; + ~IDynamicEntity() override = default; +}; + +class Enemy : public IDynamicEntity { +public: + Enemy(); + [[nodiscard]] std::unique_ptr<IDynamicEntity> clone() const override; + void action() override; + void draw_into(sf::RenderWindow& window) const override; + void prepare_for_drawing() override; +private: + sf::RectangleShape m_rectangle; + sf::Clock m_stopwatch; + std::mt19937 m_rng{std::random_device{}()}; + std::uniform_int_distribution<size_t> m_dist_milliseconds{0, 9999}; + std::uniform_int_distribution<> m_dist_direction{0, 3}; +}; diff --git a/source/States/GameState/Entities/Pacman.cpp b/source/States/GameState/Entities/Pacman.cpp new file mode 100644 index 0000000..e6f04f8 --- /dev/null +++ b/source/States/GameState/Entities/Pacman.cpp @@ -0,0 +1,19 @@ +#include <Configuration.h> +#include <States/GameState/Entities/Pacman.h> + +Pacman::Pacman() : m_circle{ config::GAME_PACMAN_SIZE } { + m_circle.setFillColor(config::GAME_COLOR_PACMAN); + m_circle.setOrigin(config::GAME_PACMAN_SIZE/2, config::GAME_PACMAN_SIZE/2); +} + +void Pacman::move(const Room::Direction direction) { + m_ptr_room->get_side(direction)->enter(this); +} + +void Pacman::draw_into(sf::RenderWindow& window) const { + window.draw(m_circle); +} + +void Pacman::prepare_for_drawing() { + m_circle.setPosition(m_ptr_room->get_position()); +} \ No newline at end of file diff --git a/source/States/GameState/Entities/Pacman.h b/source/States/GameState/Entities/Pacman.h new file mode 100644 index 0000000..827fb79 --- /dev/null +++ b/source/States/GameState/Entities/Pacman.h @@ -0,0 +1,12 @@ +#pragma once +#include <States/GameState/Entities/IEntity.h> + +class Pacman : public IEntity { +public: + Pacman(); + void move(Room::Direction direction); + void draw_into(sf::RenderWindow& window) const override; + void prepare_for_drawing() override; +private: + sf::CircleShape m_circle; +}; \ No newline at end of file diff --git a/source/States/GameState/Entities/StaticEntities.cpp b/source/States/GameState/Entities/StaticEntities.cpp new file mode 100644 index 0000000..5e22de1 --- /dev/null +++ b/source/States/GameState/Entities/StaticEntities.cpp @@ -0,0 +1,20 @@ +#include <States/GameState/Entities/StaticEntities.h> +#include <Configuration.h> + +Food::Food() : m_circle(config::GAME_FOOD_SIZE, 6) { + m_circle.setFillColor(config::GAME_FOOD_COLOR); + m_circle.setOrigin({config::GAME_FOOD_SIZE/2, config::GAME_FOOD_SIZE/2}); +} + +std::unique_ptr<IStaticEntity> Food::clone() const { + return std::make_unique<Food>(*this); +} + +void Food::draw_into(sf::RenderWindow& window) const { + window.draw(m_circle); +} + +void Food::prepare_for_drawing() { + m_circle.setPosition(m_ptr_room->get_position()); +} + diff --git a/source/States/GameState/Entities/StaticEntities.h b/source/States/GameState/Entities/StaticEntities.h new file mode 100644 index 0000000..d005d7d --- /dev/null +++ b/source/States/GameState/Entities/StaticEntities.h @@ -0,0 +1,17 @@ +#pragma once +#include <States/GameState/Entities/IEntity.h> + +struct IStaticEntity : IEntity { + [[nodiscard]] virtual std::unique_ptr<IStaticEntity> clone() const = 0; + ~IStaticEntity() override = default; +}; + +class Food : public IStaticEntity { +public: + Food(); + [[nodiscard]] std::unique_ptr<IStaticEntity> clone() const override; + void draw_into(sf::RenderWindow& window) const override; + void prepare_for_drawing() override; +private: + sf::CircleShape m_circle; +}; \ No newline at end of file diff --git a/source/States/GameState/GameContext.cpp b/source/States/GameState/GameContext.cpp new file mode 100644 index 0000000..e6aac1a --- /dev/null +++ b/source/States/GameState/GameContext.cpp @@ -0,0 +1,12 @@ +#include <States/GameState/GameContext.h> + +GameContext GameContext::clone() const { + GameContext new_game_context; + new_game_context.pacman = pacman; + new_game_context.state = state; + for (const auto& obj : static_objects) + new_game_context.static_objects.push_back(obj->clone()); + for (const auto& obj : dynamic_objects) + new_game_context.dynamic_objects.push_back(obj->clone()); + return new_game_context; +} diff --git a/source/States/GameState/GameContext.h b/source/States/GameState/GameContext.h new file mode 100644 index 0000000..8c295d2 --- /dev/null +++ b/source/States/GameState/GameContext.h @@ -0,0 +1,12 @@ +#pragma once +#include <States/GameState/Entities/Pacman.h> +#include <States/GameState/Entities/StaticEntities.h> +#include <States/GameState/Entities/DynamicEntities.h> + +struct GameContext { + [[nodiscard]] GameContext clone() const; + Pacman pacman; + std::vector<std::unique_ptr<IStaticEntity>> static_objects; + std::vector<std::unique_ptr<IDynamicEntity>> dynamic_objects; + enum State{ INGAME, WIN, LOST } state = INGAME; +}; \ No newline at end of file diff --git a/source/States/GameState/Maze/Room.cpp b/source/States/GameState/Maze/Room.cpp index 4f8964a..aa62c09 100644 --- a/source/States/GameState/Maze/Room.cpp +++ b/source/States/GameState/Maze/Room.cpp @@ -4,9 +4,10 @@ Room::Room(float size) : m_rectangle({size, size}) { m_rectangle.setOrigin(size/2, size/2); } -void Room::set_side(const Direction side, std::unique_ptr<IRoomSide>&& ptr_side) { +void Room::set_side(const Direction side, std::shared_ptr<IRoomSide>&& ptr_side) { if (side == INVALID) throw std::invalid_argument("Invalid direction"); m_sides[side] = std::move(ptr_side); + ptr_side->prepare_for_drawing(); } Room::Direction Room::get_direction(const IRoomSide* ptr_side) const { diff --git a/source/States/GameState/Maze/Room.h b/source/States/GameState/Maze/Room.h index db04902..9520cbf 100644 --- a/source/States/GameState/Maze/Room.h +++ b/source/States/GameState/Maze/Room.h @@ -14,12 +14,12 @@ public: float get_size() const { return m_rectangle.getSize().x; } void set_position(const sf::Vector2f pos) { m_rectangle.setPosition(pos); } sf::Vector2f get_position() const { return m_rectangle.getPosition(); } - void set_side(Direction side, std::unique_ptr<IRoomSide>&& ptr_side); + void set_side(Direction side, std::shared_ptr<IRoomSide>&& ptr_side); IRoomSide* get_side(const Direction side) const { return m_sides[side].get(); } Direction get_direction(const IRoomSide* ptr_side) const; void draw_into(sf::RenderWindow& window) const override; private: sf::RectangleShape m_rectangle; - std::array<std::unique_ptr<IRoomSide>, 4> m_sides; + std::array<std::shared_ptr<IRoomSide>, 4> m_sides; ///< shared_ptr? Две комнаты владеют одним IRoomSide }; diff --git a/source/States/GameState/Maze/RoomSide.cpp b/source/States/GameState/Maze/RoomSide.cpp index 48f6d5c..4a443b5 100644 --- a/source/States/GameState/Maze/RoomSide.cpp +++ b/source/States/GameState/Maze/RoomSide.cpp @@ -1,7 +1,38 @@ +#include <Configuration.h> #include <States/GameState/Entities/IEntity.h> void Pass::enter(IEntity* entity) const { if (entity->get_location() == &m_room1) entity->set_location(&m_room2); else entity->set_location(&m_room1); -} \ No newline at end of file +} + +void Wall::prepare_for_drawing() { + const sf::Vector2 pos = m_room.get_position(); + const float size = m_room.get_size(); + const std::array<sf::Vector2f, 4> corners = { sf::Vector2f{pos.x - size/2, pos.y - size/2}, + sf::Vector2f{pos.x + size/2, pos.y - size/2}, + sf::Vector2f{pos.x - size/2, pos.y + size/2}, + sf::Vector2f{pos.x + size/2, pos.y + size/2}}; + + switch (Room::Direction direction = m_room.get_direction(this)) { + case Room::Direction::UP: + m_line[0] = sf::Vertex(corners[0], config::GAME_COLOR_WALL); + m_line[1] = sf::Vertex(corners[1], config::GAME_COLOR_WALL); + break; + case Room::Direction::DOWN: + m_line[0] = sf::Vertex(corners[2], config::GAME_COLOR_WALL); + m_line[1] = sf::Vertex(corners[3], config::GAME_COLOR_WALL); + break; + case Room::Direction::LEFT: + m_line[0] = sf::Vertex(corners[0], config::GAME_COLOR_WALL); + m_line[1] = sf::Vertex(corners[2], config::GAME_COLOR_WALL); + break; + case Room::Direction::RIGHT: + m_line[0] = sf::Vertex(corners[1], config::GAME_COLOR_WALL); + m_line[1] = sf::Vertex(corners[3], config::GAME_COLOR_WALL); + break; + case Room::Direction::INVALID: + throw std::invalid_argument("Invalid direction"); + } +} diff --git a/source/States/GameState/Maze/RoomSide.h b/source/States/GameState/Maze/RoomSide.h index 0c2dca7..f730f21 100644 --- a/source/States/GameState/Maze/RoomSide.h +++ b/source/States/GameState/Maze/RoomSide.h @@ -25,7 +25,7 @@ public: explicit Wall(Room& room) : m_room(room) {} void enter(IEntity* entity) const override {} void draw_into(sf::RenderWindow& window) const override { window.draw(m_line, 2, sf::Lines); } - void prepare_for_drawing() override {} /// @todo разобраться, что тут делать + void prepare_for_drawing() override; private: Room& m_room; sf::Vertex m_line[2]; diff --git a/source/States/SelectState/SelectState.cpp b/source/States/SelectState/SelectState.cpp index 2661e9d..4b64ef3 100644 --- a/source/States/SelectState/SelectState.cpp +++ b/source/States/SelectState/SelectState.cpp @@ -1,4 +1,4 @@ -#include "SelectState.h" +#include <States/SelectState/SelectState.h> #include <States/ChangeStateCommand.h> #include <BasicAbstractions/Font.h> @@ -58,18 +58,18 @@ Menu::Menu(IStateManager& state_manager) { config::BUTTON_TEXT_EXIT, config::BUTTON_FONT_SIZE, std::make_unique<GameCommand>(state_manager, std::make_unique<GameBuilderDirector>())); } -bool Menu::process_mouse(const sf::Vector2f pos, const bool is_pressed) { +void Menu::process_mouse(const sf::Vector2f pos, const bool is_pressed) { for (auto& button : m_buttons) { - if (button.is_position_in(pos)) { + if (!button.is_position_in(pos)) { + button.unselect(); + continue; + } + if (!is_pressed) { button.select(); - if (is_pressed) { - button.push(); - return false; - } + continue; } - else button.unselect(); + button.push(); } - return true; } void Menu::draw_into(sf::RenderWindow& window) const { @@ -85,13 +85,17 @@ void SelectState::event_handling() { m_state_manager.set_next_state(std::make_unique<ExitState>(m_state_manager)); m_window.close(); } + if (event.type == sf::Event::Resized) { + sf::View view = m_window.getView(); + view.setSize(event.size.width, event.size.height); + m_window.setView(view); + } } } void SelectState::update() { - if (!m_menu.process_mouse(m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window)), - sf::Mouse::isButtonPressed(sf::Mouse::Left))) - m_window.close(); + m_menu.process_mouse(m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window)), + sf::Mouse::isButtonPressed(sf::Mouse::Left)); } void SelectState::render() { @@ -101,10 +105,8 @@ void SelectState::render() { } bool SelectState::do_step() { - while (m_window.isOpen()) { - event_handling(); - update(); - render(); - } + event_handling(); + update(); + render(); return true; } \ No newline at end of file diff --git a/source/States/SelectState/SelectState.h b/source/States/SelectState/SelectState.h index 4b0dc6a..cea4897 100644 --- a/source/States/SelectState/SelectState.h +++ b/source/States/SelectState/SelectState.h @@ -22,7 +22,7 @@ private: class Menu: public IDrawable { public: explicit Menu(IStateManager& state_manager); - bool process_mouse(sf::Vector2f pos, bool is_pressed); + void process_mouse(sf::Vector2f pos, bool is_pressed); void draw_into(sf::RenderWindow& window) const override; private: std::array<Button, 4> m_buttons; -- GitLab