Implement ghost movements
This commit is contained in:
parent
43ccaae3ef
commit
17ac48f713
7 changed files with 124 additions and 14 deletions
|
@ -1,4 +1,5 @@
|
||||||
#include "Blinky.hpp"
|
#include "Blinky.hpp"
|
||||||
|
#include "GameState.hpp"
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ Position Blinky::target(const GameState & gameState) const {
|
||||||
if (isInPen())
|
if (isInPen())
|
||||||
return penDoorPosition();
|
return penDoorPosition();
|
||||||
|
|
||||||
return scatterTarget();
|
return state == State::Chase ? gridPositionToPosition(gameState.pacMan.positionInGrid()) : scatterTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Blinky::initialPosition() const {
|
Position Blinky::initialPosition() const {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Clyde.hpp"
|
#include "Clyde.hpp"
|
||||||
|
#include "GameState.hpp"
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
|
|
||||||
|
@ -22,7 +23,15 @@ Position Clyde::target(const GameState & gameState) const {
|
||||||
if (isInPen())
|
if (isInPen())
|
||||||
return penDoorPosition();
|
return penDoorPosition();
|
||||||
|
|
||||||
return scatterTarget();
|
// Clyde always target its scatter target, unless pacman is further than 8 tiles away
|
||||||
|
auto targetPosition = scatterTarget();
|
||||||
|
|
||||||
|
const auto & pacmanPosition = gameState.pacMan.positionInGrid();
|
||||||
|
auto distanceFomPacMan = std::hypot(pos.x - pacmanPosition.x, pos.y - pacmanPosition.y);
|
||||||
|
if (state == State::Chase && distanceFomPacMan > 8)
|
||||||
|
targetPosition = gridPositionToPosition(pacmanPosition);
|
||||||
|
|
||||||
|
return targetPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Clyde::initialPosition() const {
|
Position Clyde::initialPosition() const {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Ghost.hpp"
|
#include "Ghost.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ void Ghost::frighten() {
|
||||||
return;
|
return;
|
||||||
direction = oppositeDirection(direction);
|
direction = oppositeDirection(direction);
|
||||||
state = State::Frightened;
|
state = State::Frightened;
|
||||||
timeFrighten = 0;
|
timeFrighten = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Ghost::isFrightened() const {
|
bool Ghost::isFrightened() const {
|
||||||
|
@ -29,15 +30,15 @@ void Ghost::die() {
|
||||||
return;
|
return;
|
||||||
direction = oppositeDirection(direction);
|
direction = oppositeDirection(direction);
|
||||||
state = State::Eyes;
|
state = State::Eyes;
|
||||||
timeFrighten = 0;
|
timeFrighten = {};
|
||||||
timeChase = 0;
|
timeChase = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ghost::reset() {
|
void Ghost::reset() {
|
||||||
pos = initialPosition();
|
pos = initialPosition();
|
||||||
state = State::Scatter;
|
state = State::Scatter;
|
||||||
timeFrighten = 0;
|
timeFrighten = {};
|
||||||
timeChase = 0;
|
timeChase = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] GridPosition Ghost::currentSprite() const {
|
[[nodiscard]] GridPosition Ghost::currentSprite() const {
|
||||||
|
@ -47,7 +48,7 @@ void Ghost::reset() {
|
||||||
case State::Eyes:
|
case State::Eyes:
|
||||||
return Atlas::eyeSprite(direction);
|
return Atlas::eyeSprite(direction);
|
||||||
case State::Frightened:
|
case State::Frightened:
|
||||||
if (timeFrighten < 3500)
|
if (timeFrighten.count() < 3500)
|
||||||
return Atlas::initialFrightened(animationIndex);
|
return Atlas::initialFrightened(animationIndex);
|
||||||
else
|
else
|
||||||
return Atlas::endingFrightened(animationIndex);
|
return Atlas::endingFrightened(animationIndex);
|
||||||
|
@ -67,11 +68,20 @@ void Ghost::update(std::chrono::milliseconds time_delta, const GameState & gameS
|
||||||
state = State::Scatter;
|
state = State::Scatter;
|
||||||
|
|
||||||
if (state == State::Frightened) {
|
if (state == State::Frightened) {
|
||||||
timeFrighten += time_delta.count();
|
timeFrighten += time_delta;
|
||||||
if (timeFrighten > 6000)
|
if (timeFrighten.count() > 6000)
|
||||||
state = State::Scatter;
|
state = State::Scatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state == State::Scatter || state == State::Chase) {
|
||||||
|
timeChase += time_delta;
|
||||||
|
auto newState = defaultStateAtDuration(std::chrono::duration_cast<std::chrono::seconds>(timeChase));
|
||||||
|
if (newState != state) {
|
||||||
|
direction = oppositeDirection(direction);
|
||||||
|
state = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateAnimation(time_delta);
|
updateAnimation(time_delta);
|
||||||
updatePosition(time_delta, gameState);
|
updatePosition(time_delta, gameState);
|
||||||
}
|
}
|
||||||
|
@ -105,6 +115,10 @@ void Ghost::updatePosition(std::chrono::milliseconds time_delta, const GameState
|
||||||
pos.y += position_delta;
|
pos.y += position_delta;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPortal(positionInGrid(), direction)) {
|
||||||
|
pos = gridPositionToPosition(teleport(positionInGrid()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -144,6 +158,10 @@ void Ghost::updateDirection(const GameState & gameState) {
|
||||||
const Position target_position = target(gameState);
|
const Position target_position = target(gameState);
|
||||||
|
|
||||||
for (auto & move : possible_moves) {
|
for (auto & move : possible_moves) {
|
||||||
|
|
||||||
|
if (isPortal(current_grid_position, move.direction))
|
||||||
|
move.position = gridPositionToPosition(teleport(current_grid_position));
|
||||||
|
|
||||||
const bool invalid_position = (move.position.x < 0 || move.position.y < 0);
|
const bool invalid_position = (move.position.x < 0 || move.position.y < 0);
|
||||||
if (invalid_position)
|
if (invalid_position)
|
||||||
continue;
|
continue;
|
||||||
|
@ -177,4 +195,23 @@ void Ghost::updateAnimation(std::chrono::milliseconds time_delta) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ghosts alternate between the scatter and chase states at
|
||||||
|
* specific intervals
|
||||||
|
*/
|
||||||
|
Ghost::State Ghost::defaultStateAtDuration(std::chrono::seconds s) {
|
||||||
|
// This array denotes the duration of each state, alternating between scatter and chase
|
||||||
|
std::array changes = { /*scatter*/ 7, 20, 7, 20, 5, 20, 5 };
|
||||||
|
// To know the current state we first compute the cumulative time using std::partial_sum
|
||||||
|
// This gives us {7, 27, 34, 54, 59, 79, 84}
|
||||||
|
std::partial_sum(std::begin(changes), std::end(changes), std::begin(changes));
|
||||||
|
// Then we look for the first value in the array greater than the time spend in chase/scatter states
|
||||||
|
auto it = std::upper_bound(std::begin(changes), std::end(changes), s.count());
|
||||||
|
// We get the position of that iterator in the array
|
||||||
|
auto count = std::distance(std::begin(changes), it);
|
||||||
|
// Because the first positition is scatter, all the even positions will be scatter
|
||||||
|
// all the odd positions will be chase
|
||||||
|
return count % 2 == 0 ? State::Scatter : State::Chase;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pacman
|
} // namespace pacman
|
||||||
|
|
36
lib/Inky.cpp
36
lib/Inky.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "Inky.hpp"
|
#include "Inky.hpp"
|
||||||
|
#include "GameState.hpp"
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
|
|
||||||
|
@ -22,7 +23,40 @@ Position Inky::target(const GameState & gameState) const {
|
||||||
if (isInPen())
|
if (isInPen())
|
||||||
return penDoorPosition();
|
return penDoorPosition();
|
||||||
|
|
||||||
return scatterTarget();
|
if (state == State::Scatter)
|
||||||
|
return scatterTarget();
|
||||||
|
|
||||||
|
// Inky first selects a position 2 cell away from pacman in his direction.
|
||||||
|
GridPosition targetPosition = gameState.pacMan.positionInGrid();
|
||||||
|
switch (gameState.pacMan.currentDirection()) {
|
||||||
|
case Direction::LEFT:
|
||||||
|
targetPosition.x -= 2;
|
||||||
|
break;
|
||||||
|
case Direction::RIGHT:
|
||||||
|
targetPosition.x += 2;
|
||||||
|
break;
|
||||||
|
case Direction::UP:
|
||||||
|
targetPosition.y -= 2;
|
||||||
|
targetPosition.x -= 2;
|
||||||
|
break;
|
||||||
|
case Direction::DOWN:
|
||||||
|
targetPosition.y += 2;
|
||||||
|
break;
|
||||||
|
case Direction::NONE:
|
||||||
|
assert("Pacman should be moving!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then it calculates the distance between Blinky and this position
|
||||||
|
const auto & blinkyPosition = gameState.blinky.positionInGrid();
|
||||||
|
auto distanceBetweenBlinkyAndTarget = std::hypot(blinkyPosition.x - targetPosition.x, blinkyPosition.y - targetPosition.y);
|
||||||
|
|
||||||
|
// And selects a point on the line crossing blinky and this position that is at twice that distance
|
||||||
|
// away from blinky
|
||||||
|
targetPosition.x += ((targetPosition.x - blinkyPosition.x) / distanceBetweenBlinkyAndTarget) * 2;
|
||||||
|
targetPosition.y += ((targetPosition.y - blinkyPosition.y) / distanceBetweenBlinkyAndTarget) * 2;
|
||||||
|
|
||||||
|
return gridPositionToPosition(targetPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Inky::initialPosition() const {
|
Position Inky::initialPosition() const {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Pinky.hpp"
|
#include "Pinky.hpp"
|
||||||
|
#include "GameState.hpp"
|
||||||
|
|
||||||
namespace pacman {
|
namespace pacman {
|
||||||
|
|
||||||
|
@ -22,7 +23,30 @@ Position Pinky::target(const GameState & gameState) const {
|
||||||
if (isInPen())
|
if (isInPen())
|
||||||
return penDoorPosition();
|
return penDoorPosition();
|
||||||
|
|
||||||
return scatterTarget();
|
if (state == State::Scatter)
|
||||||
|
return scatterTarget();
|
||||||
|
|
||||||
|
// Inky first selects a position 2 cell away from pacman in his direction.
|
||||||
|
GridPosition targetPosition = gameState.pacMan.positionInGrid();
|
||||||
|
switch (gameState.pacMan.currentDirection()) {
|
||||||
|
case Direction::LEFT:
|
||||||
|
targetPosition.x -= 4;
|
||||||
|
break;
|
||||||
|
case Direction::RIGHT:
|
||||||
|
targetPosition.x += 4;
|
||||||
|
break;
|
||||||
|
case Direction::UP:
|
||||||
|
targetPosition.y -= 4;
|
||||||
|
targetPosition.x -= 4;
|
||||||
|
break;
|
||||||
|
case Direction::DOWN:
|
||||||
|
targetPosition.y += 4;
|
||||||
|
break;
|
||||||
|
case Direction::NONE:
|
||||||
|
assert("Pacman should be moving!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return gridPositionToPosition(targetPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Pinky::initialPosition() const {
|
Position Pinky::initialPosition() const {
|
||||||
|
|
|
@ -41,6 +41,7 @@ private:
|
||||||
void updateDirection(const GameState & gameState);
|
void updateDirection(const GameState & gameState);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
State defaultStateAtDuration(std::chrono::seconds s);
|
||||||
|
|
||||||
virtual double speed(const GameState & gameState) const = 0;
|
virtual double speed(const GameState & gameState) const = 0;
|
||||||
virtual Position target(const GameState & gameState) const = 0;
|
virtual Position target(const GameState & gameState) const = 0;
|
||||||
|
@ -51,8 +52,8 @@ protected:
|
||||||
double timeForAnimation = 0;
|
double timeForAnimation = 0;
|
||||||
int animationIndex = 0;
|
int animationIndex = 0;
|
||||||
State state = State::Chase;
|
State state = State::Chase;
|
||||||
int timeFrighten = 0;
|
std::chrono::milliseconds timeFrighten = {};
|
||||||
int timeChase = 0;
|
std::chrono::milliseconds timeChase = {};
|
||||||
Position pos;
|
Position pos;
|
||||||
GridPosition last_grid_position = { 0, 0 };
|
GridPosition last_grid_position = { 0, 0 };
|
||||||
[[nodiscard]] bool isInPen() const;
|
[[nodiscard]] bool isInPen() const;
|
||||||
|
|
|
@ -27,6 +27,10 @@ public:
|
||||||
return direction != Direction::NONE;
|
return direction != Direction::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Direction currentDirection() const {
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Direction direction = Direction::NONE;
|
Direction direction = Direction::NONE;
|
||||||
Direction desired_direction = Direction::NONE;
|
Direction desired_direction = Direction::NONE;
|
||||||
|
|
Loading…
Reference in a new issue