diff --git a/assets/enemy_blue.png b/assets/enemy_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c9ee5c0734dc2bc006b9af7f0facc6a49d46c5 Binary files /dev/null and b/assets/enemy_blue.png differ diff --git a/assets/enemy_pink.png b/assets/enemy_pink.png new file mode 100644 index 0000000000000000000000000000000000000000..27e30bf41f5d49b6212e9a6e569e3be86e78e379 Binary files /dev/null and b/assets/enemy_pink.png differ diff --git a/assets/enemy_red.png b/assets/enemy_red.png new file mode 100644 index 0000000000000000000000000000000000000000..423708fd3cce0594dff5b8e6dde7f71e86cb22ce Binary files /dev/null and b/assets/enemy_red.png differ diff --git a/assets/enemy_yellow.png b/assets/enemy_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..028c7b2d74a31c20c4cc5aefcc3dc9e9e61fee05 Binary files /dev/null and b/assets/enemy_yellow.png differ diff --git a/assets/pacman.png b/assets/pacman.png new file mode 100644 index 0000000000000000000000000000000000000000..91b0b9fff9063be5b604449df17a1348b6558320 Binary files /dev/null and b/assets/pacman.png differ diff --git a/source/BasicAbstractions/Assets.cpp b/source/BasicAbstractions/Assets.cpp new file mode 100644 index 0000000000000000000000000000000000000000..297ec8920f279a4eb54b094adaf77526a24c856e --- /dev/null +++ b/source/BasicAbstractions/Assets.cpp @@ -0,0 +1,53 @@ +#include "Assets.h" + +sf::Font& Assets::GetFont() { + static sf::Font font; + if (!font.getInfo().family.empty()) return font; + if (!font.loadFromFile(config::FONT_FILE)) + throw std::runtime_error("Failed to load font"); + return font; +} + +sf::Texture& Assets::GetPacmanTexture() { + static sf::Texture texture; + if (texture.getSize() != sf::Vector2u{0, 0}) return texture; + if (!texture.loadFromFile(config::PACMAN_FILE)) + throw std::runtime_error("Failed to load texture: pacman"); + return texture; +} + +sf::Texture& Assets::GetEnemyRedTexture() { + static sf::Texture texture; + if (texture.getSize() != sf::Vector2u{0, 0}) + return texture; + if (!texture.loadFromFile(config::ENEMY_RED_FILE)) + throw std::runtime_error("Failed to load texture: enemy red"); + return texture; +} + +sf::Texture& Assets::GetEnemyPinkTexture() { + static sf::Texture texture; + if (texture.getSize() != sf::Vector2u{0, 0}) + return texture; + if (!texture.loadFromFile(config::ENEMY_PINK_FILE)) + throw std::runtime_error("Failed to load texture: enemy pink"); + return texture; +} + +sf::Texture& Assets::GetEnemyYellowTexture() { + static sf::Texture texture; + if (texture.getSize() != sf::Vector2u{0, 0}) + return texture; + if (!texture.loadFromFile(config::ENEMY_YELLOW_FILE)) + throw std::runtime_error("Failed to load texture: enemy yellow"); + return texture; +} + +sf::Texture& Assets::GetEnemyBlueTexture() { + static sf::Texture texture; + if (texture.getSize() != sf::Vector2u{0, 0}) + return texture; + if (!texture.loadFromFile(config::ENEMY_BLUE_FILE)) + throw std::runtime_error("Failed to load texture: enemy blue"); + return texture; +} \ No newline at end of file diff --git a/source/BasicAbstractions/Assets.h b/source/BasicAbstractions/Assets.h new file mode 100644 index 0000000000000000000000000000000000000000..b716fc47e0101fd5848acbeab3e7b7acae861a5b --- /dev/null +++ b/source/BasicAbstractions/Assets.h @@ -0,0 +1,12 @@ +#pragma once +#include <Configuration.h> + +struct Assets final { + Assets() = delete; + static sf::Font& GetFont(); + static sf::Texture& GetPacmanTexture(); + static sf::Texture& GetEnemyRedTexture(); + static sf::Texture& GetEnemyYellowTexture(); + static sf::Texture& GetEnemyBlueTexture(); + static sf::Texture& GetEnemyPinkTexture(); +}; diff --git a/source/BasicAbstractions/Button/Button.cpp b/source/BasicAbstractions/Button/Button.cpp index 503297936a4f6e120771d4345090acfc144000af..64e7f4ef9abab66e81e75959698d89023e2bb0ea 100644 --- a/source/BasicAbstractions/Button/Button.cpp +++ b/source/BasicAbstractions/Button/Button.cpp @@ -1,6 +1,6 @@ #include <BasicAbstractions/Button/Button.h> #include <Configuration.h> -#include <BasicAbstractions/Font.h> +#include <BasicAbstractions/Assets.h> void Button::set(const sf::Vector2f pos, const sf::Vector2f size, const std::string& text, const size_t font_size, std::unique_ptr<ISelectCommand> ptr_command) { m_rectangle.setSize(size); @@ -8,7 +8,7 @@ void Button::set(const sf::Vector2f pos, const sf::Vector2f size, const std::str m_rectangle.setFillColor(config::BUTTON_COLOR_FILL); m_rectangle.setOutlineThickness(config::BUTTON_FRAME_THICKNESS); m_rectangle.setOutlineColor(config::BUTTON_COLOR_FRAME); - m_text.setFont(MyFont::Instance()); + m_text.setFont(Assets::GetFont()); m_text.setCharacterSize(font_size); m_text.setFillColor(config::BUTTON_COLOR_TEXT); m_text.setString(text); diff --git a/source/BasicAbstractions/Font.h b/source/BasicAbstractions/Font.h deleted file mode 100644 index d77f0278ab1391834d30f19972f3244bab1cbe0f..0000000000000000000000000000000000000000 --- a/source/BasicAbstractions/Font.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include <Configuration.h> - -class MyFont final { -public: - static sf::Font& Instance() { - static MyFont instance; - return instance.m_font; - } - MyFont(const MyFont&) = delete; - MyFont& operator=(const MyFont&) = delete; -private: - MyFont() { - if (!m_font.loadFromFile(config::FONT_FILE)) - throw std::runtime_error("Failed to load font"); - } - ~MyFont() = default; - sf::Font m_font; -}; diff --git a/source/Configuration.h b/source/Configuration.h index 759b57d68867373933d12eca34219636651d1946..d8829d0ce8311d39d39407aaba761b8ea702842a 100644 --- a/source/Configuration.h +++ b/source/Configuration.h @@ -3,7 +3,7 @@ namespace config { // Общее: - constexpr unsigned int FRAME_RATE_LIMIT = 60; + constexpr unsigned int FRAME_RATE_LIMIT = 10; // Меню: const sf::Vector2f BUTTON_SIZE = { 250, 80 }; const size_t BUTTON_FONT_SIZE = static_cast<size_t>(BUTTON_SIZE.y / 1.5f); @@ -18,6 +18,12 @@ namespace config { constexpr char BUTTON_TEXT_EXIT[] = "Exit"; // РРіСЂР°: const sf::VideoMode GAME_VIDEO_MODE{ 1080, 720 }; + constexpr char PACMAN_FILE[] = ASSETS_PATH "pacman.png"; + constexpr char ENEMY_PINK_FILE[] = ASSETS_PATH "enemy_pink.png"; + constexpr char ENEMY_RED_FILE[] = ASSETS_PATH "enemy_red.png"; + constexpr char ENEMY_BLUE_FILE[] = ASSETS_PATH "enemy_blue.png"; + constexpr char ENEMY_YELLOW_FILE[] = ASSETS_PATH "enemy_yellow.png"; + constexpr size_t ENEMY_ON_SPRITE_COUNT = 8; constexpr char EASY_GAME_TITLE[] = "Level: Easy"; constexpr char MEDIUM_GAME_TITLE[] = "Level: Medium"; constexpr char HARD_GAME_TITLE[] = "Level: Hard"; @@ -26,12 +32,12 @@ namespace config { 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_ENEMY_SIZE = ROOM_SIZE * 0.8; constexpr float GAME_FOOD_SIZE = ROOM_SIZE * 0.1; constexpr float ENEMY_SPEED = 80; - constexpr float PACMAN_SPEED = 300; + constexpr float PACMAN_SPEED = 400; // Пакмэн: - constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.8; + constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.9; 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; @@ -46,11 +52,9 @@ namespace config { const sf::Color BUTTON_COLOR_SELECTION{ 255, 180, 180 }; const sf::Color BUTTON_COLOR_FRAME{ 0, 0, 0 }; const sf::Color SELECT_LEVEL_BACKGROUND_COLOR{ 230,230,230 }; - 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_BACKGROUND_INGAME{ 230,230,230 }; const sf::Color TEXT_COLOR_NOTIFICATION{ 0, 0, 0, 255}; const sf::Color NOTIFICATION_BANNER_COLOR_PLAY{ 0, 100, 255, 200 }; diff --git a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.cpp b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.cpp index 1e228fa3b580e378afdcbf50156d527dea8ff522..a47c0a6722d8982576652c6cba39cddc61275af0 100644 --- a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.cpp +++ b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.cpp @@ -1,10 +1,24 @@ #include <Configuration.h> +#include <BasicAbstractions/Assets.h> #include <States/GameState/Entities/DynamicEntities/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}); - m_distance_limit = config::ENEMY_SPEED/config::ROOM_SIZE; +Enemy::Enemy() { + std::mt19937 gen{std::random_device{}()}; + std::uniform_int_distribution<size_t> dist{0, 3}; + const sf::Texture* texture; + switch (dist(gen)) { + case 0: texture = &Assets::GetEnemyRedTexture(); break; + case 1: texture = &Assets::GetEnemyYellowTexture(); break; + case 2: texture = &Assets::GetEnemyBlueTexture(); break; + case 3: texture = &Assets::GetEnemyPinkTexture(); break; + default: texture = &Assets::GetEnemyRedTexture(); break;; + } + + m_sprite.setTexture(*texture); + m_sprite.setTextureRect(sf::IntRect(0, 0, + static_cast<int>(texture->getSize().x / config::ENEMY_ON_SPRITE_COUNT), static_cast<int>(texture->getSize().y))); + m_sprite.setOrigin(static_cast<float>(texture->getSize().x) / (2.f * config::ENEMY_ON_SPRITE_COUNT), static_cast<float>(texture->getSize().y) / 2.f); + m_distance_limit = config::ENEMY_SPEED / config::ROOM_SIZE; } std::unique_ptr<IDynamicEntity> Enemy::clone() const { @@ -13,11 +27,11 @@ std::unique_ptr<IDynamicEntity> Enemy::clone() const { void Enemy::action() { if (m_is_moving) { - const sf::Vector2f cur_pos = m_rectangle.getPosition(); - sf::Vector2f displacement = m_estimated_position - cur_pos; + const sf::Vector2f cur_pos = m_sprite.getPosition(); + const sf::Vector2f displacement = m_estimated_position - cur_pos; const float distance = std::sqrt(displacement.x * displacement.x + displacement.y * displacement.y); if (distance < m_distance_limit) { - m_rectangle.setPosition(m_estimated_position); + m_sprite.setPosition(m_estimated_position); m_is_moving = false; return; } @@ -32,7 +46,14 @@ void Enemy::action() { delta_y = std::min(direction.y, displacement.y); else delta_y = std::max(direction.y, displacement.y); - m_rectangle.move(delta_x, delta_y); + m_sprite.move(delta_x, delta_y); + m_time_count += frame_duration; + if(m_time_count > 1) { + sf::IntRect rect = m_sprite.getTextureRect(); + rect.left = static_cast<int>((rect.left / rect.width + 4) % config::ENEMY_ON_SPRITE_COUNT) * rect.width; + m_sprite.setTextureRect(rect); + m_time_count = 0; + } } else { if (static_cast<size_t>(m_stopwatch.getElapsedTime().asMilliseconds()) < m_dist_milliseconds(m_rng)) @@ -44,15 +65,14 @@ void Enemy::action() { } void Enemy::draw_into(sf::RenderWindow& window) const { - window.draw(m_rectangle); + window.draw(m_sprite); } void Enemy::prepare_for_first_drawing() { - m_rectangle.setPosition(m_ptr_room->get_position()); + m_sprite.setPosition(m_ptr_room->get_position()); } void Enemy::prepare_for_drawing() { m_estimated_position = m_ptr_room->get_position(); m_is_moving = true; - action(); -} +} \ No newline at end of file diff --git a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h index ceb115fbde90e0221a417f84ecfae54b02bf2d43..74572ba657cc6c499506206faa3d4d050ac20e76 100644 --- a/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h +++ b/source/States/GameState/Entities/DynamicEntities/DynamicEntities.h @@ -17,11 +17,12 @@ public: void draw_into(sf::RenderWindow& window) const override; void prepare_for_first_drawing() override; void prepare_for_drawing() override; - sf::FloatRect getBounds() const { return m_rectangle.getGlobalBounds(); } + sf::FloatRect getBounds() const { return m_sprite.getGlobalBounds(); } std::unique_ptr<IGameEvent> accept(IVisitor* ptr_visitor) override { return ptr_visitor->visit(this); } private: - sf::RectangleShape m_rectangle; + sf::Sprite m_sprite; sf::Vector2f m_estimated_position; + float m_time_count = 0; bool m_is_moving = false; float m_distance_limit; sf::Clock m_stopwatch; diff --git a/source/States/GameState/Entities/Pacman/Pacman.cpp b/source/States/GameState/Entities/Pacman/Pacman.cpp index 5a4fad2f7f6222a78aa3d9698749ac737cf1c493..696e78d8d35c16b937bc717a922a1f31123692e1 100644 --- a/source/States/GameState/Entities/Pacman/Pacman.cpp +++ b/source/States/GameState/Entities/Pacman/Pacman.cpp @@ -1,10 +1,11 @@ #include <Configuration.h> +#include <BasicAbstractions/Assets.h> #include <States/GameState/Entities/DynamicEntities/DynamicEntities.h> #include <States/GameState/Entities/Pacman/Pacman.h> #include <States/GameState/Entities/StaticEntities/StaticEntities.h> Pacman::Pacman() : m_circle{ config::GAME_PACMAN_SIZE/2 } { - m_circle.setFillColor(config::GAME_COLOR_PACMAN); + m_circle.setTexture(&Assets::GetPacmanTexture()); m_circle.setOrigin(config::GAME_PACMAN_SIZE/2, config::GAME_PACMAN_SIZE/2); m_distance_limit = config::PACMAN_SPEED/config::ROOM_SIZE; } @@ -21,12 +22,22 @@ void Pacman::move() { const sf::Vector2f cur_pos = m_circle.getPosition(); const sf::Vector2f displacement = m_estimated_position - cur_pos; const float distance = std::sqrt(displacement.x * displacement.x + displacement.y * displacement.y); + if (distance < m_distance_limit) { m_circle.setPosition(m_estimated_position); m_is_moving = false; return; } - const sf::Vector2f direction = config::PACMAN_SPEED * frame_duration * displacement / distance; + + sf::Vector2f direction = displacement / distance; + float target_angle = 0; + if (std::abs(direction.x) > std::abs(direction.y)) + target_angle = (direction.x > 0) ? 0 : 180; + else + target_angle = (direction.y > 0) ? 90 : 270; + m_circle.setRotation(target_angle); + + direction = config::PACMAN_SPEED * frame_duration * displacement / distance; float delta_x, delta_y; if (direction.x > 0) delta_x = std::min(direction.x, displacement.x); @@ -50,7 +61,6 @@ void Pacman::prepare_for_first_drawing() { void Pacman::prepare_for_drawing() { m_estimated_position = m_ptr_room->get_position(); m_is_moving = true; - move(); } std::unique_ptr<IGameEvent> Pacman::visit(Food* ptr_food) { diff --git a/source/States/GameState/GameState.cpp b/source/States/GameState/GameState.cpp index 33c41d1b68ee917bfb2fd4d9fb5ab67d7456c565..d24164e61db07a23044d5eac0d3f2e3da99742b2 100644 --- a/source/States/GameState/GameState.cpp +++ b/source/States/GameState/GameState.cpp @@ -1,4 +1,4 @@ -#include <BasicAbstractions/Font.h> +#include <BasicAbstractions/Assets.h> #include <States/GameState/GameState.h> #include <States/SelectState/SelectState.h> @@ -12,7 +12,7 @@ GameState::GameState(IStateManager& state_manager, const sf::VideoMode& video_mo round((static_cast<float>(m_window.getSize().x) - m_rect.getSize().x) / 2.0f), round((static_cast<float>(m_window.getSize().y) - m_rect.getSize().y) / 2.0f) ); - m_text.setFont(MyFont::Instance()); + m_text.setFont(Assets::GetFont()); m_text.setCharacterSize(config::NOTIFICATION_FONT_SIZE); m_text.setStyle(sf::Text::Bold); m_text.setFillColor(config::TEXT_COLOR_NOTIFICATION); @@ -34,7 +34,7 @@ void GameState::event_handling() { case GameContext::INGAME: context.state = GameContext::PAUSE; break; - default: + default: break; } } if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Z && event.key.control) diff --git a/source/States/GameState/GameState.h b/source/States/GameState/GameState.h index c83d6bc3af2dbe901659a236a14c248ed8ea8aeb..d4624bdd58259d08c147359b1905e165f902e209 100644 --- a/source/States/GameState/GameState.h +++ b/source/States/GameState/GameState.h @@ -1,5 +1,4 @@ #pragma once -#include <Configuration.h> #include <BasicAbstractions/IState.h> #include <BasicAbstractions/IWindowKeeper.h> #include <States/GameState/Maze/Maze.h>