Implement ghost movement and scatter behavior

Each ghost is trying to reach a point beyond the
corner of the board, which make them run in circle
This commit is contained in:
Corentin Jabot 2021-06-25 11:06:50 +02:00 committed by Patricia Aas
parent 93186dc8b9
commit c2cfb8c734
5 changed files with 121 additions and 12 deletions

View File

@ -23,9 +23,9 @@ static const uint8_t board[ROWS][COLUMNS] = {
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 10
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 11
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 5, 5, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 12
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 13
{ 3, 2, 2, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 3 }, // 14
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 15
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 13
{ 3, 2, 2, 2, 2, 2, 1, 2, 2, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 3 }, // 14
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 5, 5, 5, 5, 5, 5, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 15
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 16
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 17
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // 18
@ -57,6 +57,19 @@ bool Board::isWalkableForGhost(Position point, float d, Direction direction) con
return isWalkable(point, d, direction, false);
}
bool Board::isWalkable(Position point) const {
return board_state[int(point.y)][int(point.x)] != uint8_t(Cell::wall);
}
bool Board::isWalkableForGost(Position point, Position origin) const
{
return isWalkable(point) && (isInPen(origin) || !isInPen(point));
}
bool Board::isInPen(Position point) const {
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 {
if (point.x <= 0 || point.x >= COLUMNS - 1)
return true;
@ -77,7 +90,7 @@ bool Board::isWalkable(Position point, float position_delta, Direction direction
}
};
Cell cell = Cell(cellAtPosition(point, position_delta, direction));
return pacman ? cell != Cell::wall : cell != Cell::wall && cell != Cell::pen_door;
return pacman ? cell != Cell::wall : cell != Cell::wall && cell != Cell::pen;
}
std::vector<PositionInt> Board::initialPelletPositions() const {

View File

@ -18,7 +18,7 @@ public:
nothing = 2,
door = 3,
power_pellet = 4,
pen_door = 5,
pen = 5,
};
Board();
@ -26,6 +26,10 @@ public:
[[nodiscard]] bool isWalkableForPacMan(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 isWalkable(Position point) const;
[[nodiscard]] bool isInPen(Position point) const;
[[nodiscard]] std::vector<PositionInt> initialPelletPositions() const;
[[nodiscard]] std::vector<PositionInt> initialSuperPelletPositions() const;
@ -33,7 +37,9 @@ public:
static Position initialPacManPosition() { return { 13.5, 23 }; }
static Position initialBlinkyPosition() { return { 13.5, 11 }; }
static Position blinkyScatterTarget() { return { 25, -2 }; }
static Position penDoorPosition() { return { 13, 11 }; }
static Position blinkyScatterTarget() { return { 25, -3 }; }
static Position initialSpeedyPosition() { return { 11.5, 14 }; }
static Position speedyScatterTarget() { return { 3, -2 }; }

View File

@ -7,3 +7,15 @@ enum class Direction {
UP,
DOWN
};
inline Direction oppositeDirection(Direction d)
{
switch (d) {
case Direction::LEFT: return Direction::RIGHT;
case Direction::RIGHT: return Direction::LEFT;
case Direction::UP: return Direction::DOWN;
case Direction::DOWN: return Direction::UP;
case Direction::NONE: return d;
}
return d;
}

View File

@ -17,15 +17,86 @@ Position Ghost::position() 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) {
time += time_delta.count();
if (time >= 250) {
time = 0;
alternate_animation = !alternate_animation;
}
updateAnimation(time_delta);
updatePosition(time_delta, board);
}
void Ghost::updatePosition(std::chrono::milliseconds time_delta, const Board & board) {
updateDirection(board);
float position_delta = (0.004 * time_delta.count()) * 0.75;
switch (direction) {
case Direction::NONE:
break;
case Direction::LEFT:
pos.x -= position_delta;
pos.y = round(pos.y);
break;
case Direction::RIGHT:
pos.x += position_delta;
pos.y = round(pos.y);
break;
case Direction::UP:
pos.x = round(pos.x);
pos.y -= position_delta;
break;
case Direction::DOWN:
pos.x = round(pos.x);
pos.y += position_delta;
break;
}
}
void Ghost::updateDirection(const Board & board) {
auto cell = positionInGrid();
if(cell == lastIntersection)
return;
using P = std::tuple<Direction, Position, double>;
auto [x , y] = cell;
std::array<P, 4> positions = {{
P{Direction::UP, {x, y-1}, 0},
P{Direction::LEFT, {x-1, y}, 0},
P{Direction::DOWN, {x, y+1}, 0},
P{Direction::RIGHT, {x+1, y}, 0}
}};
const Position target = this->target(board);
std::for_each(positions.begin(), positions.end(), [&](P & p) {
get<2>(p) = (get<0>(p) != oppositeDirection(direction) && board.isWalkableForGost(get<1>(p), cell)) ?
std::hypot(get<1>(p).x - target.x, get<1>(p).y - target.y)
: std::numeric_limits<double>::infinity();
});
auto it = std::min_element(positions.begin(), positions.end(), [](const auto & a, const auto &b)
{
return get<2>(a) < get<2>(b);
});
lastIntersection = cell;
direction = std::get<0>(*it);
}
Position Ghost::target(const Board & board) const {
if(board.isInPen(positionInGrid()))
return board.penDoorPosition();
return scatterTarget;
}
void Ghost::updateAnimation(std::chrono::milliseconds time_delta)
{
time += time_delta.count();
if (time >= 250) {
time = 0;
alternate_animation = !alternate_animation;
}
}
Blinky::Blinky(const Board & board)

View File

@ -25,6 +25,12 @@ public:
void update(std::chrono::milliseconds time_delta, const Board & board);
private:
void updateAnimation(std::chrono::milliseconds time_delta);
void updatePosition(std::chrono::milliseconds time_delta, const Board & board);
void updateDirection(const Board & board);
Position target(const Board & board) const;
protected:
Atlas::Ghost spritesSet;
Direction direction = Direction::NONE;
@ -34,6 +40,7 @@ protected:
Position pos;
Position startingPosition;
Position scatterTarget;
Position lastIntersection = {-1, -1};
};
class Blinky : public Ghost {