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:
parent
93186dc8b9
commit
c2cfb8c734
5 changed files with 121 additions and 12 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 }; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue