Pacman and ghosts can eat each others
This commit is contained in:
parent
ced6cd829c
commit
5b0e561afc
13 changed files with 278 additions and 99 deletions
|
@ -22,6 +22,33 @@ namespace Atlas {
|
||||||
constexpr PositionInt pacman_down_wide = { 7, 0 };
|
constexpr PositionInt pacman_down_wide = { 7, 0 };
|
||||||
constexpr PositionInt pacman_down_narrow = { 8, 0 };
|
constexpr PositionInt pacman_down_narrow = { 8, 0 };
|
||||||
|
|
||||||
|
constexpr PositionInt ghost_frightened1 = { 0, 7 };
|
||||||
|
constexpr PositionInt ghost_frightened2 = { 1, 7 };
|
||||||
|
constexpr PositionInt ghost_frightened3 = { 2, 7 };
|
||||||
|
constexpr PositionInt ghost_frightened4 = { 3, 7 };
|
||||||
|
|
||||||
|
constexpr PositionInt eyeSprite(Direction direction) {
|
||||||
|
int x = 0;
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::RIGHT:
|
||||||
|
x = 0;
|
||||||
|
break;
|
||||||
|
case Direction::DOWN:
|
||||||
|
x = 2;
|
||||||
|
break;
|
||||||
|
case Direction::LEFT:
|
||||||
|
x = 4;
|
||||||
|
break;
|
||||||
|
case Direction::UP:
|
||||||
|
x = 6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
x = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return { x, 6 };
|
||||||
|
}
|
||||||
|
|
||||||
constexpr PositionInt ghostSprite(Ghost ghost, Direction direction, bool alternative) {
|
constexpr PositionInt ghostSprite(Ghost ghost, Direction direction, bool alternative) {
|
||||||
assert(ghost >= Ghost::blinky && ghost <= Ghost::clyde && "Invalid Ghost");
|
assert(ghost >= Ghost::blinky && ghost <= Ghost::clyde && "Invalid Ghost");
|
||||||
int y = static_cast<int>(ghost);
|
int y = static_cast<int>(ghost);
|
||||||
|
|
|
@ -58,16 +58,15 @@ bool Board::isWalkableForGhost(Position point, float d, Direction direction) con
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Board::isWalkable(Position point) const {
|
bool Board::isWalkable(Position point) const {
|
||||||
return board_state[int(point.y)][int(point.x)] != uint8_t(Cell::wall);
|
return board_state[int(point.y)][int(point.x)] != uint8_t(Cell::wall);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Board::isWalkableForGost(Position point, Position origin) const
|
bool Board::isWalkableForGost(Position point, Position origin, bool isEyes) const {
|
||||||
{
|
return isWalkable(point) && (isEyes || (isInPen(origin) || !isInPen(point)));
|
||||||
return isWalkable(point) && (isInPen(origin) || !isInPen(point));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Board::isInPen(Position point) const {
|
bool Board::isInPen(Position point) const {
|
||||||
return board_state[int(point.y)][int(point.x)] == uint8_t(Cell::pen);
|
return board_state[int(point.y)][int(point.x)] == uint8_t(Cell::pen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Board::isWalkable(Position point, float position_delta, Direction direction, bool pacman) const {
|
bool Board::isWalkable(Position point, float position_delta, Direction direction, bool pacman) const {
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
[[nodiscard]] bool isWalkableForPacMan(Position point, float d, Direction direction) const;
|
[[nodiscard]] bool isWalkableForPacMan(Position point, float d, Direction direction) const;
|
||||||
[[nodiscard]] bool isWalkableForGhost(Position point, float d, Direction direction) const;
|
[[nodiscard]] bool isWalkableForGhost(Position point, float d, Direction direction) const;
|
||||||
|
|
||||||
[[nodiscard]] bool isWalkableForGost(Position point, Position origin) const;
|
[[nodiscard]] bool isWalkableForGost(Position point, Position origin, bool isEyes) const;
|
||||||
[[nodiscard]] bool isWalkable(Position point) const;
|
[[nodiscard]] bool isWalkable(Position point) const;
|
||||||
[[nodiscard]] bool isInPen(Position point) const;
|
[[nodiscard]] bool isInPen(Position point) const;
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,17 @@ void Canvas::update(const Game & game) {
|
||||||
renderMaze();
|
renderMaze();
|
||||||
renderPellets(game.pellets);
|
renderPellets(game.pellets);
|
||||||
renderSuperPellets(game.superPellets);
|
renderSuperPellets(game.superPellets);
|
||||||
renderPacMan(game.pacMan);
|
|
||||||
|
|
||||||
std::apply([&](const auto&... ghost) {
|
std::apply([&](const auto &... ghost) {
|
||||||
(renderGhost(ghost),...);
|
(renderGhost(ghost), ...);
|
||||||
}, game.ghosts);
|
},
|
||||||
|
game.ghosts);
|
||||||
|
|
||||||
renderScore(game.score.points);
|
renderScore(game.score.points);
|
||||||
renderLives(game.score.lives);
|
renderLives(game.score.lives);
|
||||||
|
|
||||||
|
renderPacMan(game.pacMan);
|
||||||
|
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,18 @@ enum class Direction {
|
||||||
DOWN
|
DOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Direction oppositeDirection(Direction d)
|
inline Direction oppositeDirection(Direction d) {
|
||||||
{
|
switch (d) {
|
||||||
switch (d) {
|
case Direction::LEFT:
|
||||||
case Direction::LEFT: return Direction::RIGHT;
|
return Direction::RIGHT;
|
||||||
case Direction::RIGHT: return Direction::LEFT;
|
case Direction::RIGHT:
|
||||||
case Direction::UP: return Direction::DOWN;
|
return Direction::LEFT;
|
||||||
case Direction::DOWN: return Direction::UP;
|
case Direction::UP:
|
||||||
case Direction::NONE: return d;
|
return Direction::DOWN;
|
||||||
}
|
case Direction::DOWN:
|
||||||
return d;
|
return Direction::UP;
|
||||||
|
case Direction::NONE:
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
|
|
50
lib/Game.cpp
50
lib/Game.cpp
|
@ -5,7 +5,7 @@
|
||||||
constexpr int DEFAULT_LIVES = 3;
|
constexpr int DEFAULT_LIVES = 3;
|
||||||
constexpr int NORMAL_PELLET_POINTS = 10;
|
constexpr int NORMAL_PELLET_POINTS = 10;
|
||||||
constexpr int POWER_PELLET_POINTS = 50;
|
constexpr int POWER_PELLET_POINTS = 50;
|
||||||
//constexpr int GHOST_POINTS[] = {200, 400, 800, 1600};
|
constexpr int GHOST_POINTS = 200;
|
||||||
|
|
||||||
Game::Game()
|
Game::Game()
|
||||||
: pacMan(board),
|
: pacMan(board),
|
||||||
|
@ -15,7 +15,6 @@ Game::Game()
|
||||||
score.lives = DEFAULT_LIVES;
|
score.lives = DEFAULT_LIVES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto Game::now() {
|
auto Game::now() {
|
||||||
return std::chrono::system_clock::now();
|
return std::chrono::system_clock::now();
|
||||||
}
|
}
|
||||||
|
@ -48,15 +47,54 @@ void Game::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::step(std::chrono::milliseconds delta, InputState inputState) {
|
void Game::step(std::chrono::milliseconds delta, InputState inputState) {
|
||||||
|
|
||||||
pacMan.update(delta, inputState, board);
|
pacMan.update(delta, inputState, board);
|
||||||
|
|
||||||
|
if (timeSinceDeath.count() != 0) {
|
||||||
|
timeSinceDeath += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeSinceDeath.count() > 1000) {
|
||||||
|
std::apply([&](auto &... ghost) {
|
||||||
|
(ghost.reset(), ...);
|
||||||
|
},
|
||||||
|
ghosts);
|
||||||
|
pacMan.reset(board);
|
||||||
|
timeSinceDeath = std::chrono::milliseconds(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeSinceDeath.count())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!pacMan.onTheMove())
|
||||||
|
return;
|
||||||
|
|
||||||
std::apply([&](auto &... ghost) {
|
std::apply([&](auto &... ghost) {
|
||||||
(ghost.update(delta, board),...);
|
(ghost.update(delta, board), ...);
|
||||||
}, ghosts);
|
(checkCollision(ghost), ...);
|
||||||
|
},
|
||||||
|
ghosts);
|
||||||
|
|
||||||
eatPellets();
|
eatPellets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Game::checkCollision(Ghost & g) {
|
||||||
|
if (timeSinceDeath.count() || g.isEyes())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g.positionInGrid() != pacMan.positionInGrid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g.isFrightened()) {
|
||||||
|
g.eat();
|
||||||
|
score.points += GHOST_POINTS;
|
||||||
|
} else {
|
||||||
|
pacMan.eat();
|
||||||
|
score.lives--;
|
||||||
|
timeSinceDeath = std::chrono::milliseconds(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Game::eatPellets() {
|
void Game::eatPellets() {
|
||||||
const auto pos = pacMan.positionInGrid();
|
const auto pos = pacMan.positionInGrid();
|
||||||
if (pellets.eatPelletAtPosition(pos)) {
|
if (pellets.eatPelletAtPosition(pos)) {
|
||||||
|
@ -67,6 +105,10 @@ void Game::eatPellets() {
|
||||||
if (superPellets.eatPelletAtPosition(pos)) {
|
if (superPellets.eatPelletAtPosition(pos)) {
|
||||||
score.eatenPellets++;
|
score.eatenPellets++;
|
||||||
score.points += POWER_PELLET_POINTS;
|
score.points += POWER_PELLET_POINTS;
|
||||||
|
std::apply([&](auto &... ghost) {
|
||||||
|
(ghost.frighten(), ...);
|
||||||
|
},
|
||||||
|
ghosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,12 @@ private:
|
||||||
SuperPellets superPellets;
|
SuperPellets superPellets;
|
||||||
std::tuple<Blinky, Speedy, Inky, Clyde> ghosts;
|
std::tuple<Blinky, Speedy, Inky, Clyde> ghosts;
|
||||||
Score score;
|
Score score;
|
||||||
|
std::chrono::milliseconds timeSinceDeath;
|
||||||
|
|
||||||
void step(std::chrono::milliseconds delta, InputState inputState);
|
void step(std::chrono::milliseconds delta, InputState inputState);
|
||||||
void eatPellets();
|
void eatPellets();
|
||||||
void processEvents(InputState & inputState);
|
void processEvents(InputState & inputState);
|
||||||
|
void checkCollision(Ghost & g);
|
||||||
|
|
||||||
[[nodiscard]] static auto now();
|
[[nodiscard]] static auto now();
|
||||||
};
|
};
|
||||||
|
|
184
lib/Ghost.cpp
184
lib/Ghost.cpp
|
@ -8,8 +8,50 @@ Ghost::Ghost(Atlas::Ghost spritesSet, Position startingPosition, Position scatte
|
||||||
scatterTarget(scatterTarget) {
|
scatterTarget(scatterTarget) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Ghost::frighten() {
|
||||||
|
if (state > State::Scatter)
|
||||||
|
return;
|
||||||
|
direction = oppositeDirection(direction);
|
||||||
|
state = State::Frightened;
|
||||||
|
timeFrighten = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ghost::isFrightened() const {
|
||||||
|
return state == State::Frightened;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ghost::isEyes() const {
|
||||||
|
return state == State::Eyes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ghost::eat() {
|
||||||
|
if (state == State::Eyes)
|
||||||
|
return;
|
||||||
|
direction = oppositeDirection(direction);
|
||||||
|
state = State::Eyes;
|
||||||
|
timeFrighten = 0;
|
||||||
|
timeChase = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ghost::reset() {
|
||||||
|
pos = startingPosition;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] PositionInt Ghost::currentSprite() const {
|
[[nodiscard]] PositionInt Ghost::currentSprite() const {
|
||||||
return Atlas::ghostSprite(spritesSet, direction, alternate_animation);
|
switch (state) {
|
||||||
|
default:
|
||||||
|
return Atlas::ghostSprite(spritesSet, direction, (animationIndex % 2) == 0);
|
||||||
|
case State::Frightened:
|
||||||
|
if (timeFrighten < 3500)
|
||||||
|
return (animationIndex % 2) == 0 ? Atlas::ghost_frightened2 : Atlas::ghost_frightened1;
|
||||||
|
return std::array{ Atlas::ghost_frightened1,
|
||||||
|
Atlas::ghost_frightened2,
|
||||||
|
Atlas::ghost_frightened3,
|
||||||
|
Atlas::ghost_frightened4 }[animationIndex];
|
||||||
|
case State::Eyes:
|
||||||
|
return Atlas::eyeSprite(direction);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Ghost::position() const {
|
Position Ghost::position() const {
|
||||||
|
@ -17,89 +59,107 @@ Position Ghost::position() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Ghost::positionInGrid() const {
|
Position Ghost::positionInGrid() const {
|
||||||
return { std::round(pos.x), std::round(pos.y) };
|
return { std::round(pos.x), std::round(pos.y) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::update(std::chrono::milliseconds time_delta, const Board & board) {
|
void Ghost::update(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
updateAnimation(time_delta);
|
if (state == State::Eyes && board.isInPen(positionInGrid()))
|
||||||
updatePosition(time_delta, board);
|
state = State::Scatter;
|
||||||
|
|
||||||
|
if (state == State::Frightened) {
|
||||||
|
timeFrighten += time_delta.count();
|
||||||
|
if (timeFrighten > 6000)
|
||||||
|
state = State::Scatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnimation(time_delta);
|
||||||
|
updatePosition(time_delta, board);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::updatePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
void Ghost::updatePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
updateDirection(board);
|
updateDirection(board);
|
||||||
|
|
||||||
float position_delta = (0.004 * time_delta.count()) * 0.75;
|
float position_delta = (0.004 * time_delta.count()) * speed();
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case Direction::NONE:
|
case Direction::NONE:
|
||||||
break;
|
break;
|
||||||
case Direction::LEFT:
|
case Direction::LEFT:
|
||||||
pos.x -= position_delta;
|
pos.x -= position_delta;
|
||||||
pos.y = round(pos.y);
|
pos.y = round(pos.y);
|
||||||
break;
|
break;
|
||||||
case Direction::RIGHT:
|
case Direction::RIGHT:
|
||||||
pos.x += position_delta;
|
pos.x += position_delta;
|
||||||
pos.y = round(pos.y);
|
pos.y = round(pos.y);
|
||||||
break;
|
break;
|
||||||
case Direction::UP:
|
case Direction::UP:
|
||||||
pos.x = round(pos.x);
|
pos.x = round(pos.x);
|
||||||
pos.y -= position_delta;
|
pos.y -= position_delta;
|
||||||
break;
|
break;
|
||||||
case Direction::DOWN:
|
case Direction::DOWN:
|
||||||
pos.x = round(pos.x);
|
pos.x = round(pos.x);
|
||||||
pos.y += position_delta;
|
pos.y += position_delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double Ghost::speed() const {
|
||||||
|
if (state == State::Eyes)
|
||||||
|
return 2;
|
||||||
|
if (state == State::Frightened)
|
||||||
|
return 0.5;
|
||||||
|
return 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::updateDirection(const Board & board) {
|
void Ghost::updateDirection(const Board & board) {
|
||||||
auto cell = positionInGrid();
|
auto cell = positionInGrid();
|
||||||
if(cell == lastIntersection)
|
if (cell == lastIntersection)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct NewDirection {
|
struct NewDirection {
|
||||||
Direction direction;
|
Direction direction;
|
||||||
Position position;
|
Position position;
|
||||||
double distance;
|
double distance;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto [x , y] = cell;
|
auto [x, y] = cell;
|
||||||
std::array directions = {
|
std::array directions = {
|
||||||
NewDirection{Direction::UP, {x, y-1}, 0},
|
NewDirection{ Direction::UP, { x, y - 1 }, 0 },
|
||||||
NewDirection{Direction::LEFT, {x-1, y}, 0},
|
NewDirection{ Direction::LEFT, { x - 1, y }, 0 },
|
||||||
NewDirection{Direction::DOWN, {x, y+1}, 0},
|
NewDirection{ Direction::DOWN, { x, y + 1 }, 0 },
|
||||||
NewDirection{Direction::RIGHT, {x+1, y}, 0}
|
NewDirection{ Direction::RIGHT, { x + 1, y }, 0 }
|
||||||
};
|
};
|
||||||
const Position target = this->target(board);
|
const Position target = this->target(board);
|
||||||
|
|
||||||
for(auto && d : directions) {
|
for (auto && d : directions) {
|
||||||
d.distance = (d.direction != oppositeDirection(direction) && board.isWalkableForGost(d.position, cell)) ?
|
d.distance = (d.direction != oppositeDirection(direction) && board.isWalkableForGost(d.position, cell, state == State::Eyes)) ? std::hypot(d.position.x - target.x, d.position.y - target.y)
|
||||||
std::hypot(d.position.x - target.x, d.position.y - target.y)
|
: std::numeric_limits<double>::infinity();
|
||||||
: std::numeric_limits<double>::infinity();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto it = std::min_element(directions.begin(), directions.end(), [](const auto & a, const auto &b) {
|
auto it = std::min_element(directions.begin(), directions.end(), [](const auto & a, const auto & b) {
|
||||||
return a.distance < b.distance;
|
return a.distance < b.distance;
|
||||||
});
|
});
|
||||||
|
|
||||||
lastIntersection = cell;
|
lastIntersection = cell;
|
||||||
direction = it->direction;
|
direction = it->direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Ghost::target(const Board & board) const {
|
Position Ghost::target(const Board & board) const {
|
||||||
if(board.isInPen(positionInGrid()))
|
if (state == State::Eyes)
|
||||||
return board.penDoorPosition();
|
return startingPosition;
|
||||||
|
|
||||||
return scatterTarget;
|
if (board.isInPen(positionInGrid()))
|
||||||
|
return board.penDoorPosition();
|
||||||
|
|
||||||
|
return scatterTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::updateAnimation(std::chrono::milliseconds time_delta)
|
void Ghost::updateAnimation(std::chrono::milliseconds time_delta) {
|
||||||
{
|
timeForAnimation += time_delta.count();
|
||||||
time += time_delta.count();
|
if (timeForAnimation >= 250) {
|
||||||
if (time >= 250) {
|
timeForAnimation = 0;
|
||||||
time = 0;
|
animationIndex = (animationIndex + 1) % 4;
|
||||||
alternate_animation = !alternate_animation;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Blinky::Blinky(const Board & board)
|
Blinky::Blinky(const Board & board)
|
||||||
|
|
|
@ -11,7 +11,7 @@ public:
|
||||||
enum class State {
|
enum class State {
|
||||||
Chase,
|
Chase,
|
||||||
Scatter,
|
Scatter,
|
||||||
Freightened,
|
Frightened,
|
||||||
Eyes,
|
Eyes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,8 +24,14 @@ public:
|
||||||
[[nodiscard]] Position positionInGrid() const;
|
[[nodiscard]] Position positionInGrid() const;
|
||||||
|
|
||||||
void update(std::chrono::milliseconds time_delta, const Board & board);
|
void update(std::chrono::milliseconds time_delta, const Board & board);
|
||||||
|
void frighten();
|
||||||
|
void eat();
|
||||||
|
bool isFrightened() const;
|
||||||
|
bool isEyes() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
double speed() const;
|
||||||
void updateAnimation(std::chrono::milliseconds time_delta);
|
void updateAnimation(std::chrono::milliseconds time_delta);
|
||||||
void updatePosition(std::chrono::milliseconds time_delta, const Board & board);
|
void updatePosition(std::chrono::milliseconds time_delta, const Board & board);
|
||||||
void updateDirection(const Board & board);
|
void updateDirection(const Board & board);
|
||||||
|
@ -34,13 +40,15 @@ private:
|
||||||
protected:
|
protected:
|
||||||
Atlas::Ghost spritesSet;
|
Atlas::Ghost spritesSet;
|
||||||
Direction direction = Direction::NONE;
|
Direction direction = Direction::NONE;
|
||||||
double time = 0;
|
double timeForAnimation = 0;
|
||||||
bool alternate_animation = false;
|
int animationIndex = 0;
|
||||||
State state = State::Chase;
|
State state = State::Chase;
|
||||||
|
int timeFrighten = 0;
|
||||||
|
int timeChase = 0;
|
||||||
Position pos;
|
Position pos;
|
||||||
Position startingPosition;
|
Position startingPosition;
|
||||||
Position scatterTarget;
|
Position scatterTarget;
|
||||||
Position lastIntersection = {-1, -1};
|
Position lastIntersection = { -1, -1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class Blinky : public Ghost {
|
class Blinky : public Ghost {
|
||||||
|
|
|
@ -5,7 +5,7 @@ PacMan::PacMan(const Board & board)
|
||||||
: pos(Board::initialPacManPosition()) {}
|
: pos(Board::initialPacManPosition()) {}
|
||||||
|
|
||||||
PositionInt PacMan::currentSprite() const {
|
PositionInt PacMan::currentSprite() const {
|
||||||
return pacManAnimation.animationFrame(direction);
|
return eaten ? pacManAnimation.deathAnimationFrame(direction) : pacManAnimation.animationFrame(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
Position PacMan::position() const {
|
Position PacMan::position() const {
|
||||||
|
@ -16,11 +16,27 @@ Position PacMan::positionInGrid() const {
|
||||||
return { std::round(pos.x), std::round(pos.y) };
|
return { std::round(pos.x), std::round(pos.y) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacMan::update(std::chrono::milliseconds time_delta, InputState state, const Board & board) {
|
void PacMan::eat() {
|
||||||
setDirection(state);
|
if (eaten)
|
||||||
const auto old = pos;
|
return;
|
||||||
updateMazePosition(time_delta, board);
|
eaten = true;
|
||||||
|
direction = Direction::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::reset(const Board & b) {
|
||||||
|
eaten = false;
|
||||||
|
direction = Direction::NONE;
|
||||||
|
pos = b.initialPacManPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::update(std::chrono::milliseconds time_delta, InputState state, const Board & board) {
|
||||||
|
if (eaten) {
|
||||||
|
updateAnimationPosition(time_delta, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto old = pos;
|
||||||
|
setDirection(state);
|
||||||
|
updateMazePosition(time_delta, board);
|
||||||
const bool paused = pos == old;
|
const bool paused = pos == old;
|
||||||
updateAnimationPosition(time_delta, paused);
|
updateAnimationPosition(time_delta, paused);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +56,7 @@ void PacMan::updateAnimationPosition(std::chrono::milliseconds time_delta, bool
|
||||||
if (paused) {
|
if (paused) {
|
||||||
pacManAnimation.pause();
|
pacManAnimation.pause();
|
||||||
} else {
|
} else {
|
||||||
pacManAnimation.updateAnimationPosition(time_delta);
|
pacManAnimation.updateAnimationPosition(time_delta, eaten);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,18 @@ public:
|
||||||
|
|
||||||
void update(std::chrono::milliseconds time_delta, InputState state, const Board & board);
|
void update(std::chrono::milliseconds time_delta, InputState state, const Board & board);
|
||||||
|
|
||||||
|
void eat();
|
||||||
|
void reset(const Board & b);
|
||||||
|
bool onTheMove() const {
|
||||||
|
return direction != Direction::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Direction direction = Direction::NONE;
|
Direction direction = Direction::NONE;
|
||||||
Direction desired_direction = Direction::NONE;
|
Direction desired_direction = Direction::NONE;
|
||||||
Position pos;
|
Position pos;
|
||||||
PacManAnimation pacManAnimation;
|
PacManAnimation pacManAnimation;
|
||||||
|
bool eaten = false;
|
||||||
|
|
||||||
void setDirection(const InputState & state);
|
void setDirection(const InputState & state);
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,20 @@ PositionInt PacManAnimation::animationFrame(Direction direction) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PacManAnimation::updateAnimationPosition(std::chrono::milliseconds time_delta) {
|
[[nodiscard]] PositionInt PacManAnimation::deathAnimationFrame(Direction direction) const {
|
||||||
|
return PositionInt{ animation_position, 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacManAnimation::updateAnimationPosition(std::chrono::milliseconds time_delta, bool dead) {
|
||||||
|
if (dead && animation_position >= 11)
|
||||||
|
return;
|
||||||
|
|
||||||
animation_position_delta += (0.02) * float(time_delta.count());
|
animation_position_delta += (0.02) * float(time_delta.count());
|
||||||
animation_position = int(animation_position + animation_position_delta) % 4;
|
animation_position = int(animation_position + animation_position_delta);
|
||||||
|
|
||||||
|
if (!dead)
|
||||||
|
animation_position = animation_position % 4;
|
||||||
|
|
||||||
animation_position_delta = (animation_position_delta < 1) ? animation_position_delta : (animation_position_delta - 1);
|
animation_position_delta = (animation_position_delta < 1) ? animation_position_delta : (animation_position_delta - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,9 @@
|
||||||
class PacManAnimation {
|
class PacManAnimation {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] PositionInt animationFrame(Direction direction) const;
|
[[nodiscard]] PositionInt animationFrame(Direction direction) const;
|
||||||
|
[[nodiscard]] PositionInt deathAnimationFrame(Direction direction) const;
|
||||||
|
|
||||||
void updateAnimationPosition(std::chrono::milliseconds time_delta);
|
void updateAnimationPosition(std::chrono::milliseconds time_delta, bool dead);
|
||||||
void pause();
|
void pause();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in a new issue