mirror of
https://github.com/ProgramSnail/snake_2024.git
synced 2025-12-20 21:28:45 +00:00
Better code structure: snake game object, player game object, bot game object
This commit is contained in:
parent
a92c96f91e
commit
421574ab40
6 changed files with 155 additions and 100 deletions
|
|
@ -5,29 +5,27 @@
|
||||||
#include "Map.hpp"
|
#include "Map.hpp"
|
||||||
#include "Snake.hpp"
|
#include "Snake.hpp"
|
||||||
|
|
||||||
class Bot : public Snake, public GameObject {
|
class Bot : public Snake {
|
||||||
public:
|
public:
|
||||||
struct Config {
|
struct Config {
|
||||||
float time_between_targets;
|
float time_between_targets;
|
||||||
double speed;
|
|
||||||
float move_interval;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Bot(canvas::SnakeObject obj, Config config, Map &map)
|
Bot(canvas::SnakeObject::Config canvas_config, Snake::Config snake_config,
|
||||||
: Snake(obj), config_(config), map_(map), real_pos_(obj.pos) {}
|
Config bot_config, Map &map)
|
||||||
|
: Snake(canvas_config, snake_config, map), bot_config_(bot_config) {}
|
||||||
|
|
||||||
void act(float dt) override {
|
protected:
|
||||||
|
void change_direction(float dt) override {
|
||||||
time_from_target_set_ += dt;
|
time_from_target_set_ += dt;
|
||||||
|
|
||||||
int eaten = map_.eat(utils::to_world_coord(pos), 20);
|
|
||||||
inc_length(eaten);
|
|
||||||
|
|
||||||
if (not target_.has_value() or
|
if (not target_.has_value() or
|
||||||
time_from_target_set_ >= config_.time_between_targets or
|
time_from_target_set_ >= bot_config_.time_between_targets or
|
||||||
(target_.value() - pos).len_sq() < radius * radius) {
|
(target_.value() - get_pos()).len_sq() <
|
||||||
|
canvas_config_.radius * canvas_config_.radius) {
|
||||||
time_from_target_set_ = 0;
|
time_from_target_set_ = 0;
|
||||||
|
|
||||||
target_ = map_.find_nearest_food(pos);
|
target_ = map_.find_nearest_food(get_pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for case when no food found
|
// check for case when no food found
|
||||||
|
|
@ -35,29 +33,11 @@ public:
|
||||||
direction_ = (Vecf(target_.value()) - real_pos_).norm();
|
direction_ = (Vecf(target_.value()) - real_pos_).norm();
|
||||||
// TODO: manually change direction
|
// TODO: manually change direction
|
||||||
}
|
}
|
||||||
|
|
||||||
move(dt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Veci offset) override { Snake::draw(offset); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void move(float dt) {
|
const Config bot_config_;
|
||||||
real_pos_ += direction_ * dt * config_.speed;
|
|
||||||
move_time_delta_ += dt;
|
|
||||||
if (move_time_delta_ > config_.move_interval) {
|
|
||||||
move_time_delta_ -= config_.move_interval;
|
|
||||||
add(Veci(real_pos_));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Config config_;
|
|
||||||
Map &map_;
|
|
||||||
|
|
||||||
Vecf real_pos_;
|
|
||||||
Vecf direction_ = {};
|
|
||||||
std::optional<Veci> target_ = {};
|
std::optional<Veci> target_ = {};
|
||||||
float time_from_target_set_ = 0;
|
float time_from_target_set_ = 0;
|
||||||
float move_time_delta_ = 0;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,5 @@
|
||||||
#include "Vec.hpp"
|
#include "Vec.hpp"
|
||||||
|
|
||||||
constexpr Veci WORLD_SIZE = {.x = 10000, .y = 10000};
|
constexpr Veci WORLD_SIZE = {.x = 10000, .y = 10000};
|
||||||
|
|
||||||
|
constexpr double MIN_CONTROL_DISTANCE = 10;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,48 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Vec.hpp"
|
#include "Snake.hpp"
|
||||||
|
|
||||||
struct Player {
|
// struct Player {
|
||||||
Vecf pos;
|
// Vecf pos;
|
||||||
Vecf direction;
|
// Vecf direction;
|
||||||
double speed;
|
// double speed;
|
||||||
double move_interval;
|
// double move_interval;
|
||||||
double move_time_delta;
|
// double move_time_delta;
|
||||||
|
// };
|
||||||
|
|
||||||
|
class Player : public Snake {
|
||||||
|
public:
|
||||||
|
using Snake::Snake;
|
||||||
|
|
||||||
|
void act(float dt) override {
|
||||||
|
int eaten = map_.eat(
|
||||||
|
utils::to_world_coord(get_pos() + utils::get_screen_center()), 20);
|
||||||
|
inc_length(eaten);
|
||||||
|
|
||||||
|
change_direction(dt);
|
||||||
|
|
||||||
|
move(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Veci offset) override {
|
||||||
|
Snake::draw(offset - utils::get_screen_center());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void change_direction(float) override {
|
||||||
|
Veci cursor = utils::get_cursor();
|
||||||
|
|
||||||
|
if (cursor != prev_cursor_ and utils::is_on_screen(cursor)) {
|
||||||
|
Vecf diff(cursor - utils::get_screen_center()); // - pos;
|
||||||
|
|
||||||
|
if (diff.len() > MIN_CONTROL_DISTANCE) {
|
||||||
|
direction_ = diff.norm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_cursor_ = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Veci prev_cursor_ = {};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,21 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
#include "Canvas.hpp"
|
#include "Canvas.hpp"
|
||||||
|
#include "Map.hpp"
|
||||||
|
|
||||||
namespace canvas {
|
namespace canvas {
|
||||||
|
|
||||||
struct SnakeObject : public Object {
|
class SnakeObject {
|
||||||
size_t length;
|
|
||||||
int radius;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace canvas
|
|
||||||
|
|
||||||
class Snake : protected canvas::SnakeObject {
|
|
||||||
public:
|
public:
|
||||||
Snake(canvas::SnakeObject obj) : SnakeObject(obj), track_{pos} {}
|
struct Config {
|
||||||
|
Object obj;
|
||||||
|
size_t initial_length;
|
||||||
|
int radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
SnakeObject(Config config)
|
||||||
|
: canvas_config_(config), length_{config.initial_length},
|
||||||
|
track_{config.obj.pos} {}
|
||||||
|
|
||||||
void add(Veci pos);
|
void add(Veci pos);
|
||||||
|
|
||||||
|
|
@ -23,26 +25,80 @@ public:
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
Veci get_pos() { return pos; }
|
Veci get_pos() {
|
||||||
|
if (track_.empty()) {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
return track_.back();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
size_t get_length() { return length; }
|
size_t get_length() { return length_; }
|
||||||
|
|
||||||
void set_length(size_t length) { this->length = length; }
|
void set_length(size_t length) { length_ = length; }
|
||||||
|
|
||||||
void inc_length(int inc) {
|
void inc_length(int inc) {
|
||||||
if (-inc > static_cast<int>(length)) {
|
if (-inc > static_cast<int>(length_)) {
|
||||||
length = 0;
|
length_ = 1;
|
||||||
} else {
|
} else {
|
||||||
length += inc;
|
length_ += inc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
bool touches(const Snake &other);
|
bool touches(const SnakeObject &other);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
const Config canvas_config_;
|
||||||
|
|
||||||
|
size_t length_;
|
||||||
std::deque<Veci> track_;
|
std::deque<Veci> track_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
|
||||||
|
class Snake : public canvas::SnakeObject, public GameObject {
|
||||||
|
public:
|
||||||
|
struct Config {
|
||||||
|
double speed;
|
||||||
|
float move_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
Snake(canvas::SnakeObject::Config canvas_config, Config snake_config,
|
||||||
|
Map &map)
|
||||||
|
: SnakeObject(canvas_config), snake_config_(snake_config), map_(map),
|
||||||
|
real_pos_(canvas_config.obj.pos) {}
|
||||||
|
|
||||||
|
void act(float dt) override {
|
||||||
|
int eaten = map_.eat(utils::to_world_coord(get_pos()), 20);
|
||||||
|
inc_length(eaten);
|
||||||
|
|
||||||
|
change_direction(dt);
|
||||||
|
|
||||||
|
move(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(Veci offset) override { SnakeObject::draw(offset); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void change_direction(float dt) = 0;
|
||||||
|
|
||||||
|
virtual void move(float dt) {
|
||||||
|
real_pos_ += direction_ * dt * snake_config_.speed;
|
||||||
|
move_time_delta_ += dt;
|
||||||
|
if (move_time_delta_ > snake_config_.move_interval) {
|
||||||
|
move_time_delta_ -= snake_config_.move_interval;
|
||||||
|
add(Veci(real_pos_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const Config snake_config_;
|
||||||
|
Map &map_;
|
||||||
|
|
||||||
|
Vecf real_pos_;
|
||||||
|
Vecf direction_ = {};
|
||||||
|
float move_time_delta_ = 0;
|
||||||
|
};
|
||||||
|
|
|
||||||
52
src/Game.cpp
52
src/Game.cpp
|
|
@ -12,8 +12,6 @@
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "World.hpp"
|
#include "World.hpp"
|
||||||
|
|
||||||
constexpr double MIN_CONTROL_DISTANCE = 10;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// You are free to modify this file
|
// You are free to modify this file
|
||||||
//
|
//
|
||||||
|
|
@ -31,14 +29,6 @@ void initialize() { std::srand(std::time(0)); }
|
||||||
|
|
||||||
float game_time = 0.0f;
|
float game_time = 0.0f;
|
||||||
|
|
||||||
Player player{
|
|
||||||
.pos = {.x = 20, .y = 20},
|
|
||||||
.direction = {.x = 1.0, .y = 0.0},
|
|
||||||
.speed = 200.0,
|
|
||||||
.move_interval = 0.01,
|
|
||||||
.move_time_delta = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
|
|
||||||
Map map{{
|
Map map{{
|
||||||
|
|
@ -51,13 +41,18 @@ Map map{{
|
||||||
.food_color = {color::RED},
|
.food_color = {color::RED},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// std::vector<Worm> bots; // TODO
|
Player player{canvas::SnakeObject::Config{
|
||||||
|
.obj = {.pos = {}, .color = {color::GREEN}},
|
||||||
|
.initial_length = 10,
|
||||||
|
.radius = 10,
|
||||||
|
},
|
||||||
|
Snake::Config{
|
||||||
|
.speed = 200.0,
|
||||||
|
.move_interval = 0.01,
|
||||||
|
},
|
||||||
|
map};
|
||||||
|
|
||||||
auto snake = Snake(canvas::SnakeObject{
|
// std::vector<Bot> bots; // TODO
|
||||||
{.pos = Veci(player.pos), .color = {color::GREEN}},
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
});
|
|
||||||
|
|
||||||
// this function is called to update game data,
|
// this function is called to update game data,
|
||||||
// dt - time elapsed since the previous update (in seconds)
|
// dt - time elapsed since the previous update (in seconds)
|
||||||
|
|
@ -68,30 +63,11 @@ void act(float dt) {
|
||||||
|
|
||||||
map.act(dt);
|
map.act(dt);
|
||||||
|
|
||||||
world.cursor = utils::get_cursor();
|
player.act(dt);
|
||||||
if (world.cursor != world.prev_cursor and utils::is_on_screen(world.cursor)) {
|
|
||||||
Vecf diff(world.cursor - utils::get_screen_center()); // - pos;
|
|
||||||
|
|
||||||
if (diff.len() > MIN_CONTROL_DISTANCE) {
|
|
||||||
player.direction = diff.norm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
player.pos += player.direction * dt * player.speed;
|
|
||||||
|
|
||||||
game_time += dt;
|
game_time += dt;
|
||||||
world.prev_cursor = world.cursor;
|
world.prev_cursor = world.cursor;
|
||||||
|
|
||||||
player.move_time_delta += dt;
|
|
||||||
if (player.move_time_delta > player.move_interval) {
|
|
||||||
player.move_time_delta -= player.move_interval;
|
|
||||||
snake.add(Veci(player.pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
int eaten = map.eat(
|
|
||||||
utils::to_world_coord(Veci(player.pos) + utils::get_screen_center()), 20);
|
|
||||||
snake.inc_length(eaten);
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// for (const auto &bot : bots) {
|
// for (const auto &bot : bots) {
|
||||||
// if (snake.touches(bot)) { // GAME OVER
|
// if (snake.touches(bot)) { // GAME OVER
|
||||||
|
|
@ -110,9 +86,9 @@ void draw() {
|
||||||
// clear backbuffer
|
// clear backbuffer
|
||||||
memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
memset(buffer, 0, SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint32_t));
|
||||||
|
|
||||||
Veci map_offset = Veci(snake.get_pos());
|
Veci map_offset = Veci(player.get_pos());
|
||||||
|
|
||||||
snake.draw(map_offset - utils::get_screen_center());
|
player.draw(map_offset);
|
||||||
map.draw(map_offset);
|
map.draw(map_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
#include "Snake.hpp"
|
#include "Snake.hpp"
|
||||||
|
|
||||||
void Snake::add(Veci pos) {
|
namespace canvas {
|
||||||
this->pos = pos;
|
|
||||||
|
void SnakeObject::add(Veci pos) {
|
||||||
track_.push_back(pos);
|
track_.push_back(pos);
|
||||||
if (track_.size() > length) {
|
if (track_.size() > length_) {
|
||||||
track_.pop_front();
|
track_.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snake::draw(Veci offset) const {
|
void SnakeObject::draw(Veci offset) const {
|
||||||
for (const auto &pos : track_) {
|
for (const auto &pos : track_) {
|
||||||
paint::circle({{.pos = pos - offset, .color = color}, radius});
|
paint::circle({{.pos = pos - offset, .color = canvas_config_.obj.color},
|
||||||
|
canvas_config_.radius});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Snake::touches(const Snake &other) {
|
bool SnakeObject::touches(const SnakeObject &other) {
|
||||||
int dist = radius + other.radius;
|
int dist = canvas_config_.radius + other.canvas_config_.radius;
|
||||||
for (const auto &elem : other.track_) {
|
for (const auto &elem : other.track_) {
|
||||||
if ((pos - elem).len_sq() < dist * dist) {
|
if ((get_pos() - elem).len_sq() < dist * dist) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace canvas
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue