Add ghosts
No movement for now!
This commit is contained in:
parent
79506d218e
commit
93186dc8b9
13 changed files with 295 additions and 101 deletions
50
lib/Atlas.hpp
Normal file
50
lib/Atlas.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include "Direction.hpp"
|
||||
#include "Position.hpp"
|
||||
#include "assert.h"
|
||||
|
||||
namespace Atlas {
|
||||
|
||||
enum class Ghost {
|
||||
blinky = 2,
|
||||
speedy = 3,
|
||||
inky = 4,
|
||||
clyde = 5,
|
||||
};
|
||||
|
||||
constexpr PositionInt pacman_right_wide = { 0, 0 };
|
||||
constexpr PositionInt pacman_right_narrow = { 1, 0 };
|
||||
constexpr PositionInt pacman_closed = { 2, 0 };
|
||||
constexpr PositionInt pacman_left_narrow = { 3, 0 };
|
||||
constexpr PositionInt pacman_left_wide = { 4, 0 };
|
||||
constexpr PositionInt pacman_up_wide = { 5, 0 };
|
||||
constexpr PositionInt pacman_up_narrow = { 6, 0 };
|
||||
constexpr PositionInt pacman_down_wide = { 7, 0 };
|
||||
constexpr PositionInt pacman_down_narrow = { 8, 0 };
|
||||
|
||||
constexpr PositionInt ghostSprite(Ghost ghost, Direction direction, bool alternative) {
|
||||
assert(ghost >= Ghost::blinky && ghost <= Ghost::clyde && "Invalid Ghost");
|
||||
int y = static_cast<int>(ghost);
|
||||
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;
|
||||
}
|
||||
if (alternative)
|
||||
x++;
|
||||
return { x, y };
|
||||
}
|
||||
}
|
|
@ -73,18 +73,18 @@ bool Board::isWalkable(Position point, float position_delta, Direction direction
|
|||
return board_state[int(point.y) + 1][int(point.x)];
|
||||
case Direction::NONE:
|
||||
default:
|
||||
return uint8_t(0);
|
||||
return uint8_t(Cell::wall);
|
||||
}
|
||||
};
|
||||
auto cell = cellAtPosition(point, position_delta, direction);
|
||||
return pacman ? cell != 0 : cell != 0 && cell != 5;
|
||||
Cell cell = Cell(cellAtPosition(point, position_delta, direction));
|
||||
return pacman ? cell != Cell::wall : cell != Cell::wall && cell != Cell::pen_door;
|
||||
}
|
||||
|
||||
std::vector<PositionInt> Board::initialPelletPositions() const {
|
||||
std::vector<PositionInt> positions;
|
||||
for (uint8_t row = 0; row < ROWS; row++) {
|
||||
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||
if (board_state[row][column] == 1)
|
||||
if (board_state[row][column] == uint8_t(Cell::pellet))
|
||||
positions.push_back({ column, row });
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ std::vector<PositionInt> Board::initialSuperPelletPositions() const {
|
|||
std::vector<PositionInt> positions;
|
||||
for (uint8_t row = 0; row < ROWS; row++) {
|
||||
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||
if (board_state[row][column] == 4)
|
||||
if (board_state[row][column] == uint8_t(Cell::power_pellet))
|
||||
positions.push_back({ column, row });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,15 @@ const uint8_t COLUMNS = 28;
|
|||
|
||||
class Board {
|
||||
public:
|
||||
enum class Cell {
|
||||
wall = 0,
|
||||
pellet = 1,
|
||||
nothing = 2,
|
||||
door = 3,
|
||||
power_pellet = 4,
|
||||
pen_door = 5,
|
||||
};
|
||||
|
||||
Board();
|
||||
|
||||
[[nodiscard]] bool isWalkableForPacMan(Position point, float d, Direction direction) const;
|
||||
|
@ -21,7 +30,19 @@ public:
|
|||
|
||||
[[nodiscard]] std::vector<PositionInt> initialSuperPelletPositions() const;
|
||||
|
||||
static Position initialPacManPosition() { return { 14, 23 }; }
|
||||
static Position initialPacManPosition() { return { 13.5, 23 }; }
|
||||
|
||||
static Position initialBlinkyPosition() { return { 13.5, 11 }; }
|
||||
static Position blinkyScatterTarget() { return { 25, -2 }; }
|
||||
|
||||
static Position initialSpeedyPosition() { return { 11.5, 14 }; }
|
||||
static Position speedyScatterTarget() { return { 3, -2 }; }
|
||||
|
||||
static Position initialInkyPosition() { return { 13.5, 14 }; }
|
||||
static Position inkyScatterTarget() { return { 27, 30 }; }
|
||||
|
||||
static Position initialClydePosition() { return { 15.5, 14 }; }
|
||||
static Position clydeScatterTarget() { return { 0, 30 }; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool isWalkable(Position point, float d, Direction direction, bool pacman) const;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Canvas.hpp"
|
||||
|
||||
#include "Game.hpp"
|
||||
#include "Ghost.hpp"
|
||||
#include "PacMan.hpp"
|
||||
#include "Pellets.hpp"
|
||||
#include "SuperPellets.hpp"
|
||||
|
@ -17,15 +18,21 @@ Canvas::Canvas()
|
|||
game_font = loadFont("joystix.ttf");
|
||||
}
|
||||
|
||||
void Canvas::update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets, const Score & score) {
|
||||
void Canvas::update(const Game & game) {
|
||||
clear();
|
||||
|
||||
renderMaze();
|
||||
renderPellets(pellets);
|
||||
renderSuperPellets(superPellets);
|
||||
renderPacMan(pacMan);
|
||||
renderScore(score.points);
|
||||
renderLives(score.lives);
|
||||
renderPellets(game.pellets);
|
||||
renderSuperPellets(game.superPellets);
|
||||
renderPacMan(game.pacMan);
|
||||
|
||||
renderGhost(game.blinky);
|
||||
renderGhost(game.speedy);
|
||||
renderGhost(game.inky);
|
||||
renderGhost(game.clyde);
|
||||
|
||||
renderScore(game.score.points);
|
||||
renderLives(game.score.lives);
|
||||
|
||||
render();
|
||||
}
|
||||
|
@ -73,6 +80,12 @@ void Canvas::renderPacMan(const PacMan & pac_man) {
|
|||
renderSprite(pacmanSprite, pos);
|
||||
}
|
||||
|
||||
void Canvas::renderGhost(const Ghost & ghost) {
|
||||
Sprite sprite = getSprite(ghost.currentSprite());
|
||||
const auto & pos = ghost.position();
|
||||
renderSprite(sprite, pos);
|
||||
}
|
||||
|
||||
void Canvas::renderScore(int score) {
|
||||
const int x = LEFT_MARGIN + MAZE_WIDTH + LEFT_MARGIN;
|
||||
const int y = TOP_MARGIN * 2;
|
||||
|
@ -87,13 +100,13 @@ void Canvas::renderScore(int score) {
|
|||
}
|
||||
|
||||
void Canvas::renderLives(int lives) {
|
||||
constexpr PositionInt liveSprite{3, 0};
|
||||
constexpr PositionInt liveSprite = Atlas::pacman_left_narrow;
|
||||
const int x = LEFT_MARGIN + MAZE_WIDTH + LEFT_MARGIN;
|
||||
const int y = maze_texture.getSize().y;
|
||||
|
||||
Sprite pacmanSprite = getSprite(liveSprite);
|
||||
for(int i = 0; i < lives - 1; i++) {
|
||||
PositionInt pos{x + i * pacmanSprite.getTextureRect().width, y};
|
||||
for (int i = 0; i < lives - 1; i++) {
|
||||
PositionInt pos{ x + i * pacmanSprite.getTextureRect().width, y };
|
||||
pacmanSprite.setPosition(pos.x, pos.y);
|
||||
window.draw(pacmanSprite);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "Score.hpp"
|
||||
#include <optional>
|
||||
|
||||
class Game;
|
||||
class Ghost;
|
||||
class PacMan;
|
||||
class Pellets;
|
||||
class SuperPellets;
|
||||
|
@ -11,7 +13,7 @@ class SuperPellets;
|
|||
class Canvas {
|
||||
public:
|
||||
Canvas();
|
||||
void update(const PacMan & pacMan, const Pellets & pellets, const SuperPellets & superPellets, const Score &);
|
||||
void update(const Game & game);
|
||||
std::optional<sf::Event> pollEvent();
|
||||
|
||||
private:
|
||||
|
@ -28,6 +30,7 @@ private:
|
|||
void render();
|
||||
void renderMaze();
|
||||
void renderPacMan(const PacMan & pac_man);
|
||||
void renderGhost(const Ghost & ghost);
|
||||
void renderPellets(const Pellets & pellets);
|
||||
void renderSuperPellets(const SuperPellets & superPellets);
|
||||
void renderSprite(Sprite sprite, Position pos);
|
||||
|
|
37
lib/Game.cpp
37
lib/Game.cpp
|
@ -7,14 +7,14 @@ constexpr int NORMAL_PELLET_POINTS = 10;
|
|||
constexpr int POWER_PELLET_POINTS = 50;
|
||||
//constexpr int GHOST_POINTS[] = {200, 400, 800, 1600};
|
||||
|
||||
|
||||
|
||||
|
||||
Game::Game()
|
||||
: pacMan(board),
|
||||
pellets(board),
|
||||
superPellets(board)
|
||||
{
|
||||
superPellets(board),
|
||||
blinky(board),
|
||||
speedy(board),
|
||||
inky(board),
|
||||
clyde(board) {
|
||||
score.lives = DEFAULT_LIVES;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ auto Game::now() {
|
|||
|
||||
void Game::run() {
|
||||
|
||||
const std::chrono::milliseconds delta_time (1000/60);
|
||||
const std::chrono::milliseconds delta_time(1000 / 60);
|
||||
|
||||
std::chrono::milliseconds t(0);
|
||||
std::chrono::milliseconds accumulator(0);
|
||||
|
@ -38,30 +38,39 @@ void Game::run() {
|
|||
current_time = newTime;
|
||||
accumulator += frameTime;
|
||||
processEvents(inputState);
|
||||
if(inputState.close)
|
||||
if (inputState.close)
|
||||
return;
|
||||
while ( accumulator >= delta_time ) {
|
||||
pacMan.update(delta_time, inputState, board);
|
||||
eatPellets();
|
||||
while (accumulator >= delta_time) {
|
||||
step(delta_time, inputState);
|
||||
accumulator -= delta_time;
|
||||
t += delta_time;
|
||||
}
|
||||
canvas.update(pacMan, pellets, superPellets, score);
|
||||
canvas.update(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::step(std::chrono::milliseconds delta, InputState inputState) {
|
||||
pacMan.update(delta, inputState, board);
|
||||
|
||||
blinky.update(delta, board);
|
||||
speedy.update(delta, board);
|
||||
inky.update(delta, board);
|
||||
clyde.update(delta, board);
|
||||
|
||||
eatPellets();
|
||||
}
|
||||
|
||||
void Game::eatPellets() {
|
||||
const auto pos = pacMan.positionInGrid();
|
||||
if(pellets.eatPelletAtPosition(pos)) {
|
||||
if (pellets.eatPelletAtPosition(pos)) {
|
||||
score.eatenPellets++;
|
||||
score.points += NORMAL_PELLET_POINTS;
|
||||
}
|
||||
|
||||
if(superPellets.eatPelletAtPosition(pos)) {
|
||||
if (superPellets.eatPelletAtPosition(pos)) {
|
||||
score.eatenPellets++;
|
||||
score.points += POWER_PELLET_POINTS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Game::processEvents(InputState & inputState) {
|
||||
|
|
12
lib/Game.hpp
12
lib/Game.hpp
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "Score.hpp"
|
||||
#include "Board.hpp"
|
||||
#include "Canvas.hpp"
|
||||
#include "Ghost.hpp"
|
||||
#include "PacMan.hpp"
|
||||
#include "Pellets.hpp"
|
||||
#include "Score.hpp"
|
||||
#include "SuperPellets.hpp"
|
||||
|
||||
class InputState;
|
||||
|
@ -12,19 +13,24 @@ class InputState;
|
|||
class Game {
|
||||
public:
|
||||
Game();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
friend class Canvas;
|
||||
|
||||
Canvas canvas;
|
||||
Board board;
|
||||
PacMan pacMan;
|
||||
Pellets pellets;
|
||||
SuperPellets superPellets;
|
||||
Blinky blinky;
|
||||
Speedy speedy;
|
||||
Inky inky;
|
||||
Clyde clyde;
|
||||
Score score;
|
||||
|
||||
void step(std::chrono::milliseconds delta, InputState inputState);
|
||||
void eatPellets();
|
||||
|
||||
void processEvents(InputState & inputState);
|
||||
|
||||
[[nodiscard]] static auto now();
|
||||
|
|
45
lib/Ghost.cpp
Normal file
45
lib/Ghost.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include "Ghost.hpp"
|
||||
#include <cmath>
|
||||
|
||||
Ghost::Ghost(Atlas::Ghost spritesSet, Position startingPosition, Position scatterTarget)
|
||||
: spritesSet(spritesSet),
|
||||
pos(startingPosition),
|
||||
startingPosition(startingPosition),
|
||||
scatterTarget(scatterTarget) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PositionInt Ghost::currentSprite() const {
|
||||
return Atlas::ghostSprite(spritesSet, direction, alternate_animation);
|
||||
}
|
||||
|
||||
Position Ghost::position() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Position Ghost::positionInGrid() const {
|
||||
return { std::round(pos.x), std::round(pos.y) };
|
||||
}
|
||||
|
||||
void Ghost::update(std::chrono::milliseconds time_delta, const Board & board) {
|
||||
time += time_delta.count();
|
||||
if (time >= 250) {
|
||||
time = 0;
|
||||
alternate_animation = !alternate_animation;
|
||||
}
|
||||
}
|
||||
|
||||
Blinky::Blinky(const Board & board)
|
||||
: Ghost(Atlas::Ghost::blinky, board.initialBlinkyPosition(), board.blinkyScatterTarget()) {
|
||||
}
|
||||
|
||||
Speedy::Speedy(const Board & board)
|
||||
: Ghost(Atlas::Ghost::speedy, board.initialSpeedyPosition(), board.speedyScatterTarget()) {
|
||||
}
|
||||
|
||||
Inky::Inky(const Board & board)
|
||||
: Ghost(Atlas::Ghost::inky, board.initialInkyPosition(), board.inkyScatterTarget()) {
|
||||
}
|
||||
|
||||
Clyde::Clyde(const Board & board)
|
||||
: Ghost(Atlas::Ghost::clyde, board.initialClydePosition(), board.clydeScatterTarget()) {
|
||||
}
|
57
lib/Ghost.hpp
Normal file
57
lib/Ghost.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "Atlas.hpp"
|
||||
#include "Board.hpp"
|
||||
#include "Position.hpp"
|
||||
|
||||
class Ghost {
|
||||
public:
|
||||
enum class State {
|
||||
Chase,
|
||||
Scatter,
|
||||
Freightened,
|
||||
Eyes,
|
||||
};
|
||||
|
||||
explicit Ghost(Atlas::Ghost spritesSet, Position startingPosition, Position scatterTarget);
|
||||
|
||||
[[nodiscard]] PositionInt currentSprite() const;
|
||||
|
||||
[[nodiscard]] Position position() const;
|
||||
|
||||
[[nodiscard]] Position positionInGrid() const;
|
||||
|
||||
void update(std::chrono::milliseconds time_delta, const Board & board);
|
||||
|
||||
protected:
|
||||
Atlas::Ghost spritesSet;
|
||||
Direction direction = Direction::NONE;
|
||||
double time = 0;
|
||||
bool alternate_animation = false;
|
||||
State state = State::Chase;
|
||||
Position pos;
|
||||
Position startingPosition;
|
||||
Position scatterTarget;
|
||||
};
|
||||
|
||||
class Blinky : public Ghost {
|
||||
public:
|
||||
explicit Blinky(const Board & board);
|
||||
};
|
||||
|
||||
class Speedy : public Ghost {
|
||||
public:
|
||||
explicit Speedy(const Board & board);
|
||||
};
|
||||
|
||||
class Inky : public Ghost {
|
||||
public:
|
||||
explicit Inky(const Board & board);
|
||||
};
|
||||
|
||||
class Clyde : public Ghost {
|
||||
public:
|
||||
explicit Clyde(const Board & board);
|
||||
};
|
|
@ -37,10 +37,9 @@ void PacMan::setDirection(const InputState & state) {
|
|||
}
|
||||
|
||||
void PacMan::updateAnimationPosition(std::chrono::milliseconds time_delta, bool paused) {
|
||||
if(paused) {
|
||||
if (paused) {
|
||||
pacManAnimation.pause();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pacManAnimation.updateAnimationPosition(time_delta);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ PositionInt PacManAnimation::animationFrame(Direction direction) const {
|
|||
return down_animation[animation_position];
|
||||
case Direction::NONE:
|
||||
default:
|
||||
return closed;
|
||||
return Atlas::pacman_closed;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Atlas.hpp"
|
||||
#include "Board.hpp"
|
||||
#include "Direction.hpp"
|
||||
#include "InputState.hpp"
|
||||
|
@ -14,21 +15,11 @@ public:
|
|||
void updateAnimationPosition(std::chrono::milliseconds time_delta);
|
||||
void pause();
|
||||
|
||||
|
||||
private:
|
||||
uint8_t animation_position = 0;
|
||||
float animation_position_delta = 0.0;
|
||||
const PositionInt right_wide = { 0, 0 };
|
||||
const PositionInt right_narrow = { 1, 0 };
|
||||
const PositionInt closed = { 2, 0 };
|
||||
const PositionInt left_narrow = { 3, 0 };
|
||||
const PositionInt left_wide = { 4, 0 };
|
||||
const PositionInt up_wide = { 5, 0 };
|
||||
const PositionInt up_narrow = { 6, 0 };
|
||||
const PositionInt down_wide = { 7, 0 };
|
||||
const PositionInt down_narrow = { 8, 0 };
|
||||
const PositionInt down_animation[4]{ down_wide, down_narrow, closed, down_narrow };
|
||||
const PositionInt left_animation[4]{ left_wide, left_narrow, closed, left_narrow };
|
||||
const PositionInt right_animation[4]{ right_wide, right_narrow, closed, right_narrow };
|
||||
const PositionInt up_animation[4]{ up_wide, up_narrow, closed, up_narrow };
|
||||
const PositionInt down_animation[4]{ Atlas::pacman_down_wide, Atlas::pacman_down_narrow, Atlas::pacman_closed, Atlas::pacman_down_narrow };
|
||||
const PositionInt left_animation[4]{ Atlas::pacman_left_wide, Atlas::pacman_left_narrow, Atlas::pacman_closed, Atlas::pacman_left_narrow };
|
||||
const PositionInt right_animation[4]{ Atlas::pacman_right_wide, Atlas::pacman_right_narrow, Atlas::pacman_closed, Atlas::pacman_right_narrow };
|
||||
const PositionInt up_animation[4]{ Atlas::pacman_up_wide, Atlas::pacman_up_narrow, Atlas::pacman_closed, Atlas::pacman_up_narrow };
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue