Commit d55b9527 authored by Печенин Данила Михайлович's avatar Печенин Данила Михайлович
Browse files

Support continues moving of entities

parent 59bc8fb1
No related merge requests found
Showing with 149 additions and 26 deletions
+149 -26
......@@ -21,12 +21,15 @@ namespace config {
constexpr char EASY_GAME_TITLE[] = "Level: Easy";
constexpr char MEDIUM_GAME_TITLE[] = "Level: Medium";
constexpr char HARD_GAME_TITLE[] = "Level: Hard";
constexpr char EXTREME_GAME_TITLE[] = "Level: Extreme";
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.1;
constexpr float ENEMY_SPEED = 80;
constexpr float PACMAN_SPEED = 300;
// Пакмэн:
constexpr float GAME_PACMAN_SIZE = ROOM_SIZE * 0.8;
constexpr sf::Keyboard::Key KEY_LEFT = sf::Keyboard::A;
......
......@@ -4,6 +4,7 @@
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;
}
std::unique_ptr<IDynamicEntity> Enemy::clone() const {
......@@ -11,11 +12,34 @@ std::unique_ptr<IDynamicEntity> Enemy::clone() const {
}
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);
if (m_is_moving) {
const sf::Vector2f cur_pos = m_rectangle.getPosition();
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_is_moving = false;
return;
}
const float frame_duration = m_stopwatch.getElapsedTime().asSeconds();
const sf::Vector2f direction = config::ENEMY_SPEED * frame_duration * displacement / distance;
float delta_x, delta_y;
if (direction.x > 0)
delta_x = std::min(direction.x, displacement.x);
else
delta_x = std::max(direction.x, displacement.x);
if (direction.y > 0)
delta_y = std::min(direction.y, displacement.y);
else
delta_y = std::max(direction.y, displacement.y);
m_rectangle.move(delta_x, delta_y);
}
else {
if (static_cast<size_t>(m_stopwatch.getElapsedTime().asMilliseconds()) < 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();
}
......@@ -23,6 +47,12 @@ void Enemy::draw_into(sf::RenderWindow& window) const {
window.draw(m_rectangle);
}
void Enemy::prepare_for_drawing() {
void Enemy::prepare_for_first_drawing() {
m_rectangle.setPosition(m_ptr_room->get_position());
}
void Enemy::prepare_for_drawing() {
m_estimated_position = m_ptr_room->get_position();
m_is_moving = true;
action();
}
......@@ -15,12 +15,17 @@ public:
[[nodiscard]] std::unique_ptr<IDynamicEntity> clone() const override;
void action() override;
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(); }
std::unique_ptr<IGameEvent> accept(IVisitor* ptr_visitor) override { return ptr_visitor->visit(this); }
private:
sf::RectangleShape m_rectangle;
sf::Vector2f m_estimated_position;
bool m_is_moving = false;
float m_distance_limit;
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<size_t> m_dist_milliseconds{300, 10000};
std::uniform_int_distribution<> m_dist_direction{0, 3};
};
......@@ -4,7 +4,12 @@
class IEntity : public IPreparable {
public:
void set_location(Room* ptr_room) noexcept {
virtual void prepare_for_first_drawing() = 0;
void set_initial_location(Room* ptr_room) {
m_ptr_room = ptr_room;
prepare_for_first_drawing();
}
void set_location(Room* ptr_room) {
m_ptr_room = ptr_room;
prepare_for_drawing();
}
......
......@@ -6,28 +6,61 @@
Pacman::Pacman() : m_circle{ config::GAME_PACMAN_SIZE/2 } {
m_circle.setFillColor(config::GAME_COLOR_PACMAN);
m_circle.setOrigin(config::GAME_PACMAN_SIZE/2, config::GAME_PACMAN_SIZE/2);
m_distance_limit = config::PACMAN_SPEED/config::ROOM_SIZE;
}
void Pacman::move(const Room::Direction direction) {
void Pacman::replace(const Room::Direction direction) {
if (m_is_moving) return;
m_ptr_room->get_side(direction)->enter(this);
}
void Pacman::move() {
const float frame_duration = m_stopwatch.getElapsedTime().asSeconds();
m_stopwatch.restart();
if (!m_is_moving) return;
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;
float delta_x, delta_y;
if (direction.x > 0)
delta_x = std::min(direction.x, displacement.x);
else
delta_x = std::max(direction.x, displacement.x);
if (direction.y > 0)
delta_y = std::min(direction.y, displacement.y);
else
delta_y = std::max(direction.y, displacement.y);
m_circle.move(delta_x, delta_y);
}
void Pacman::draw_into(sf::RenderWindow& window) const {
window.draw(m_circle);
}
void Pacman::prepare_for_drawing() {
void Pacman::prepare_for_first_drawing() {
m_circle.setPosition(m_ptr_room->get_position());
}
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) {
if (ptr_food->get_location() != this->get_location())
if (!this->getBounds().intersects(ptr_food->getBounds()))
return {};
return std::make_unique<DeleteStaticEntity>(ptr_food);
}
std::unique_ptr<IGameEvent> Pacman::visit(Enemy* ptr_enemy) {
if (ptr_enemy->get_location() != this->get_location())
if (!this->getBounds().intersects(ptr_enemy->getBounds()))
return {};
return std::make_unique<LostGame>();
}
......@@ -5,11 +5,18 @@
class Pacman final : public IEntity, public IVisitor {
public:
Pacman();
void move(Room::Direction direction);
void replace(Room::Direction direction);
void move();
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_circle.getGlobalBounds(); }
std::unique_ptr<IGameEvent> visit(Food* ptr_food) override;
std::unique_ptr<IGameEvent> visit(Enemy* ptr_enemy) override;
private:
sf::CircleShape m_circle;
sf::Vector2f m_estimated_position;
float m_distance_limit;
bool m_is_moving = false;
sf::Clock m_stopwatch;
};
\ No newline at end of file
......@@ -14,6 +14,10 @@ void Food::draw_into(sf::RenderWindow& window) const {
window.draw(m_circle);
}
void Food::prepare_for_first_drawing() {
prepare_for_drawing();
}
void Food::prepare_for_drawing() {
m_circle.setPosition(m_ptr_room->get_position());
}
......
......@@ -12,7 +12,9 @@ public:
Food();
[[nodiscard]] std::unique_ptr<IStaticEntity> clone() const override;
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_circle.getGlobalBounds(); }
std::unique_ptr<IGameEvent> accept(IVisitor* ptr_visitor) override { return ptr_visitor->visit(this); }
private:
sf::CircleShape m_circle;
......
......@@ -18,22 +18,30 @@ void CommonBuilder::create_context(const float dynamic_objets_ratio) {
std::uniform_int_distribution distrib(0 , static_cast<int>(buf_rooms.size()-1));
auto pos = buf_rooms.begin() + distrib(gen);
m_context.pacman.set_location((*pos)->get());
const sf::Vector2f pacman_pos = (*pos)->get()->get_position();
m_context.pacman.set_initial_location((*pos)->get());
buf_rooms.erase(pos);
if (dynamic_objets_ratio > 1) throw std::invalid_argument("GAME_ENEMY_RATIO should be <= 1");
const auto number_of_dynamic_entities = static_cast<size_t>(static_cast<float>(buf_rooms.size()) * dynamic_objets_ratio);
for (size_t i = 0; i < number_of_dynamic_entities; ++i) {
m_context.dynamic_objects.emplace_back(std::move(std::make_unique<Enemy>()));
distrib.param(std::uniform_int_distribution<>::param_type(0, static_cast<int>(buf_rooms.size()) - 1));
pos = buf_rooms.begin() + distrib(gen);
m_context.dynamic_objects.back()->set_location((*pos)->get());
if (float pos_x = (*pos)->get()->get_position().x, pos_y = (*pos)->get()->get_position().y;
std::sqrt((pos_x-pacman_pos.x) * (pos_x-pacman_pos.x) + (pos_y-pacman_pos.y) * (pos_y-pacman_pos.y)) <= m_room_size * 2) {
m_context.static_objects.emplace_back(std::move(std::make_unique<Food>()));
m_context.static_objects.back()->set_initial_location((*pos)->get());
}
else {
m_context.dynamic_objects.emplace_back(std::move(std::make_unique<Enemy>()));
m_context.dynamic_objects.back()->set_initial_location((*pos)->get());
}
buf_rooms.erase(pos);
}
for (const auto& it_room : buf_rooms) {
m_context.static_objects.emplace_back(std::move(std::make_unique<Food>()));
m_context.static_objects.back()->set_location(it_room->get());
m_context.static_objects.back()->set_initial_location(it_room->get());
}
}
......
#include <BasicAbstractions/Font.h>
#include <States/GameState/GameState.h>
#include <States/SelectState/SelectState.h>
......@@ -34,27 +35,51 @@ void GameState::update() {
events.emplace_back(std::make_unique<WinGame>());
}
m_context_manager.get_current_context().pacman.move();
for (const auto& event : events)
event->handle(m_context_manager.get_current_context());
}
void GameState::render() {
sf::Text text;
sf::RectangleShape rect;
switch (m_context_manager.get_current_context().state) {
case GameContext::INGAME:
m_window.clear(config::GAME_COLOR_BACKGROUND_INGAME);
break;
case GameContext::LOST:
text.setFont(MyFont::Instance());
text.setFillColor({255, 0, 0, 220});
text.setString("LOST!");
text.setCharacterSize(300);
text.setStyle(sf::Text::Bold);
text.setPosition(
round((m_window.getSize().x - text.getLocalBounds().width) / 2 - text.getLocalBounds().left),
round((m_window.getSize().y - text.getLocalBounds().height) / 2 - text.getLocalBounds().top)
);
rect.setSize(sf::Vector2f(text.getLocalBounds().width + 100, text.getLocalBounds().height + 100));
rect.setFillColor(sf::Color(240, 0, 0, 50));
rect.setOutlineThickness(5);
rect.setOutlineColor(sf::Color::White);
rect.setPosition(
round((m_window.getSize().x - rect.getSize().x) / 2),
round((m_window.getSize().y - rect.getSize().y) / 2)
);
m_window.clear(config::GAME_COLOR_BACKGROUND_INGAME);
break;
case GameContext::WIN:
m_window.clear(config::GAME_COLOR_BACKGROUND_WIN);
break;
case GameContext::LOST:
m_window.clear(config::GAME_COLOR_BACKGROUND_LOST);
}
m_maze.draw_into(m_window);
for (const auto& obj : m_context_manager.get_current_context().static_objects)
obj->draw_into(m_window);
m_context_manager.get_current_context().pacman.draw_into(m_window);
for (const auto& obj : m_context_manager.get_current_context().dynamic_objects)
obj->draw_into(m_window);
m_context_manager.get_current_context().pacman.draw_into(m_window);
m_window.draw(rect);
m_window.draw(text);
m_window.display();
}
......@@ -62,19 +87,19 @@ void GameState::process_key_pressed(const sf::Keyboard::Key key) {
switch (key) {
case config::KEY_UP:
m_context_manager.save_context();
m_context_manager.get_current_context().pacman.move(Room::UP);
m_context_manager.get_current_context().pacman.replace(Room::UP);
break;
case config::KEY_DOWN:
m_context_manager.save_context();
m_context_manager.get_current_context().pacman.move(Room::DOWN);
m_context_manager.get_current_context().pacman.replace(Room::DOWN);
break;
case config::KEY_LEFT:
m_context_manager.save_context();
m_context_manager.get_current_context().pacman.move(Room::LEFT);
m_context_manager.get_current_context().pacman.replace(Room::LEFT);
break;
case config::KEY_RIGHT:
m_context_manager.save_context();
m_context_manager.get_current_context().pacman.move(Room::RIGHT);
m_context_manager.get_current_context().pacman.replace(Room::RIGHT);
break;
default: break;
}
......
......@@ -10,6 +10,7 @@ public:
explicit GameState(IStateManager& state_manager, const sf::VideoMode& video_mode, const sf::String& window_title) :
IState(state_manager), IWindowKeeper(video_mode, window_title) {
m_window.setFramerateLimit(config::FRAME_RATE_LIMIT);
m_window.setKeyRepeatEnabled(false);
}
bool do_step() override;
void set_maze(Maze&& maze) { m_maze = std::move(maze); }
......
......@@ -6,7 +6,7 @@ Room::Room(float size) : m_rectangle({size, size}) {
void Room::set_side(const Direction side, const std::shared_ptr<IRoomSide>& ptr_side) {
if (side == INVALID) throw std::invalid_argument("Invalid direction");
m_sides[side] = std::move(ptr_side);
m_sides[side] = ptr_side;
m_sides[side]->prepare_for_drawing();
}
......
......@@ -36,7 +36,7 @@ Menu::Menu(IStateManager& state_manager) {
std::make_unique<GameBuilderDirector>(
std::make_unique<ComplexRandomBuilder>(config::GAME_VIDEO_MODE.width, config::GAME_VIDEO_MODE.height, config::ROOM_SIZE, config::GAME_COLOR_ROOM),
config::GAME_VIDEO_MODE,
config::HARD_GAME_TITLE,
config::EXTREME_GAME_TITLE,
config::HARD_GAME_ENEMY_RATIO));
m_buttons[0].set(sf::Vector2f{pos_left, pos_top}, config::BUTTON_SIZE, config::BUTTON_TEXT_EASY,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment