Reformat and add some text to make ghost
This commit is contained in:
parent
69271ac36e
commit
0c12878753
15 changed files with 99 additions and 37 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
In this exercise we will write some helper functions for the game board.
|
||||
|
||||
The file [`Board.cpp`](../../lib/Board.cpp) defines functions to manipulate the game Board, for example finding where
|
||||
The file [`Board.cpp`](../../../lib/Board.cpp) defines functions to manipulate the game Board, for example finding where
|
||||
the walls and the portals are.
|
||||
|
||||
## Board.cpp
|
7
exercises/17/make_ghost/17-create-ghost.md
Normal file
7
exercises/17/make_ghost/17-create-ghost.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Exercise: Add Clyde as a Ghost
|
||||
|
||||
We have three ghosts within the project. Blinky, Inky and Pinky. But Clyde is missing. Implement Clyde and their
|
||||
behavior.
|
||||
|
||||
Clyde always chases PacMan so the only thing you should need is where PacMan is currently located. But Clyde also has
|
||||
the scatter behavior as the other ghosts.
|
|
@ -2,22 +2,29 @@
|
|||
|
||||
# Exercise: Game Over
|
||||
|
||||
In this exercise you will add a game over state to the game. After PacMan has lost all of his lives the game will be over. PacMan should not respawn and the ghosts should stop.
|
||||
In this exercise you will add a game over state to the game. After PacMan has lost all of his lives the game will be
|
||||
over. PacMan should not respawn and the ghosts should stop.
|
||||
|
||||
The GameState class has the number of lives and they are already drawn on screen so that information can be used to indicate the game over state itself.
|
||||
The GameState class has the number of lives and they are already drawn on screen so that information can be used to
|
||||
indicate the game over state itself.
|
||||
|
||||
If the game is over, the text "Game Over" should be drawn over the game board. You do not need to implement a new game, for this exercise restarting the application is enough.
|
||||
If the game is over, the text "Game Over" should be drawn over the game board. You do not need to implement a new game,
|
||||
for this exercise restarting the application is enough.
|
||||
|
||||
# Exercise: New Level
|
||||
|
||||
In this exercise you will need to reset the game board but keep score and lives the same. This should happen when PacMan eats the last pellet or super pellet.
|
||||
In this exercise you will need to reset the game board but keep score and lives the same. This should happen when PacMan
|
||||
eats the last pellet or super pellet.
|
||||
|
||||
The ghosts and pacman should go back to their original position and all of the pellets should respawn. This can go on forever, but you can look at the next exercise for more information.
|
||||
The ghosts and pacman should go back to their original position and all of the pellets should respawn. This can go on
|
||||
forever, but you can look at the next exercise for more information.
|
||||
|
||||
# Exercise: Victory
|
||||
|
||||
As a continuation of the Game Over and New Level exercises. If PacMan beats 3 levels, the game has been beaten and a victory screen should be shown.
|
||||
As a continuation of the Game Over and New Level exercises. If PacMan beats 3 levels, the game has been beaten and a
|
||||
victory screen should be shown.
|
||||
|
||||
A count of how many levels have been beaten needs to be stored and this should be checked when transitioning between levels.
|
||||
A count of how many levels have been beaten needs to be stored and this should be checked when transitioning between
|
||||
levels.
|
||||
|
||||
This should function similarly to the Game Over state but used to indicate that the player won the game.
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
# Exercise: Vertical Teleport
|
||||
|
||||
The teleporters are programmed to work only on the horizontal axis. Change the board to add teleporters at the top and bottom row. This also requires a code change when teleporting happens. These new teleporters only need to function like the existing teleporters and you do not need to add more than one on each row.
|
||||
The teleporters are programmed to work only on the horizontal axis. Change the board to add teleporters at the top and
|
||||
bottom row. This also requires a code change when teleporting happens. These new teleporters only need to function like
|
||||
the existing teleporters and you do not need to add more than one on each row.
|
||||
|
||||
# Exercise: Generic Teleporting
|
||||
|
||||
In the game and the exercise above, the teleports are linked to each other. These systems are not very generic. Update the teleporting system so that two teleport doors are linked with each other.
|
||||
In the game and the exercise above, the teleports are linked to each other. These systems are not very generic. Update
|
||||
the teleporting system so that two teleport doors are linked with each other.
|
||||
|
||||
For example so you can enter a teleport from somewhere on the bottom row and exit from somewhere on the right column.
|
||||
|
||||
|
|
|
@ -2,23 +2,22 @@
|
|||
|
||||
# Exercise: Increased Ghost Speed
|
||||
|
||||
Currently the ghosts move at a fixed rate. But in the original game, the ghosts would move slightly faster and faster as the game went on.
|
||||
Currently the ghosts move at a fixed rate. But in the original game, the ghosts would move slightly faster and faster as
|
||||
the game went on.
|
||||
|
||||
Implement a way to incrementally increase the ghost speed over time. The speed function takes in the current game state so that can be used. The speed should not slowly increase for each update, so something like adding onto the speed value over time is not correct behavior.
|
||||
Implement a way to incrementally increase the ghost speed over time. The speed function takes in the current game state
|
||||
so that can be used. The speed should not slowly increase for each update, so something like adding onto the speed value
|
||||
over time is not correct behavior.
|
||||
|
||||
You could increase the speed after each time the ghost is eaten for example, or increment the speed by a certain amount after N seconds.
|
||||
You could increase the speed after each time the ghost is eaten for example, or increment the speed by a certain amount
|
||||
after N seconds.
|
||||
|
||||
Also ask yourself, how would you test this?
|
||||
|
||||
# Exercise: Add Blinky as a Ghost
|
||||
|
||||
We have three ghosts within the project. Clyde, Inky and Pinky. But Blinky is missing. Implement Blinky and their behavior.
|
||||
|
||||
Blinky always chases PacMan so the only thing you should need is where PacMan is currently located. But Blinky also has the scatter behavior as the other ghosts.
|
||||
|
||||
# Exercise: ?
|
||||
|
||||
Extra
|
||||
|
||||
# TODO Changes
|
||||
|
||||
Pinky as main exercise and Clyde as Extra.
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
# Exercise: PacMan Bot
|
||||
|
||||
PacMan can be controlled with the keyboard, but those inputs can be automated. The only thing that needs to change is the value within the InputState class.
|
||||
PacMan can be controlled with the keyboard, but those inputs can be automated. The only thing that needs to change is
|
||||
the value within the InputState class.
|
||||
|
||||
Start by giving InputState random values on each update and then program in a fixed set of movement. For example "Go Right for 3 seconds, then down, right, up, right". This should pickup the first super pellet.
|
||||
Start by giving InputState random values on each update and then program in a fixed set of movement. For example "Go
|
||||
Right for 3 seconds, then down, right, up, right". This should pickup the first super pellet.
|
||||
|
||||
In this exercise, the input code in processEvents is not needed.
|
||||
|
||||
|
@ -12,9 +14,12 @@ In this exercise, the input code in processEvents is not needed.
|
|||
|
||||
The exercise above is fixed based on the layout of the board and is hard to make generic.
|
||||
|
||||
Let's implement a naive AI for PacMan. At each intersection, check if there is a ghost directly inline with that path. If the path is free of ghosts, you are allowed to turn there. And if PacMan is moving in a direction, and a ghost enters his path, then pacman will reverse.
|
||||
Let's implement a naive AI for PacMan. At each intersection, check if there is a ghost directly inline with that path.
|
||||
If the path is free of ghosts, you are allowed to turn there. And if PacMan is moving in a direction, and a ghost enters
|
||||
his path, then pacman will reverse.
|
||||
|
||||
For example if pacman is at an intersection and can go either right or up, and there is a ghost in the path going right, then pacman will go up. Then while pacman is going up, a ghost enters that path, pacman will go back.
|
||||
For example if pacman is at an intersection and can go either right or up, and there is a ghost in the path going right,
|
||||
then pacman will go up. Then while pacman is going up, a ghost enters that path, pacman will go back.
|
||||
|
||||
You only need to worry about the grid itself and any ghosts on the North/South/East/West axis of PacMan.
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
The project is split into three parts (solutions).
|
||||
|
||||
- **libpacman** - This is where the entire game lives
|
||||
- **pacman** - This is the project that generates the executable, the only thing it does is to create the game instance and call the `run()` function
|
||||
- **pacman** - This is the project that generates the executable, the only thing it does is to create the game instance
|
||||
and call the `run()` function
|
||||
- **pacman_tests** - All of the unit tests for the game
|
||||
|
||||
## libpacman
|
||||
|
@ -12,26 +13,40 @@ The libpacman solution has 3 main parts.
|
|||
|
||||
- **Game** - A class that holds the Canvas and GameState objects
|
||||
- **Canvas** - A class that uses the SFML library to draw objects to the screen. Also manages the game window
|
||||
- **GameState** - A class that holds all of the state for Pac-Man. Contains classes like the Ghosts, PacMan and the Pellets PacMan eats
|
||||
- **GameState** - A class that holds all of the state for Pac-Man. Contains classes like the Ghosts, PacMan and the
|
||||
Pellets PacMan eats
|
||||
|
||||
## Updating the game state
|
||||
|
||||
Within the `Game.cpp` file there is a function called `run()`, this function draws the current game state to screen `canvas.update(gameState);` but it only calls the game state update function at a fixed interval, it also sends this interval to the game state update function `gameState.step(delta_time);`
|
||||
Within the `Game.cpp` file there is a function called `run()`, this function draws the current game state to
|
||||
screen `canvas.update(gameState);` but it only calls the game state update function at a fixed interval, it also sends
|
||||
this interval to the game state update function `gameState.step(delta_time);`
|
||||
|
||||
Then in the `step()` function in `GameState.cpp` the magic happens. Each object in the game world is updated and the state of the game world is checked. Did Pac-Man just eat a pellet? Are the ghosts touching Pac-Man? Which direction is Pac-Man moving?
|
||||
Then in the `step()` function in `GameState.cpp` the magic happens. Each object in the game world is updated and the
|
||||
state of the game world is checked. Did Pac-Man just eat a pellet? Are the ghosts touching Pac-Man? Which direction is
|
||||
Pac-Man moving?
|
||||
|
||||
## The game board
|
||||
|
||||
The game board is stored as a two dimensional array of integers in the `Board.cpp` file. The array itself is only accessible within that file, which is why we export helper functions that use the array as a global variable. This exporting happens in `Board.hpp`
|
||||
The game board is stored as a two dimensional array of integers in the `Board.cpp` file. The array itself is only
|
||||
accessible within that file, which is why we export helper functions that use the array as a global variable. This
|
||||
exporting happens in `Board.hpp`
|
||||
|
||||
## Ghosts
|
||||
|
||||
All ghosts inherit from the parent Ghost class defined in `Ghost.hpp/cpp`, there are a couple of pure virtual functions those ghosts need to implement, otherwise you are not able to create an instance of them. A ghost stores its current location, its current target and other state regarding if it is frightened or not. At each `update()` call a ghost needs to decide what its target is and which direction it should turn next.
|
||||
All ghosts inherit from the parent Ghost class defined in `Ghost.hpp/cpp`, there are a couple of pure virtual functions
|
||||
those ghosts need to implement, otherwise you are not able to create an instance of them. A ghost stores its current
|
||||
location, its current target and other state regarding if it is frightened or not. At each `update()` call a ghost needs
|
||||
to decide what its target is and which direction it should turn next.
|
||||
|
||||
## Pac-Man
|
||||
|
||||
Pac-Man has similar state to a Ghost, like direction and position. Pac-Man's behavior is also similar in `update()` as we need to determine where to place Pac-Man in the next game state. But the information on which direction to turn comes from the player (or a bot).
|
||||
Pac-Man has similar state to a Ghost, like direction and position. Pac-Man's behavior is also similar in `update()` as
|
||||
we need to determine where to place Pac-Man in the next game state. But the information on which direction to turn comes
|
||||
from the player (or a bot).
|
||||
|
||||
## Unit tests
|
||||
|
||||
Because updates of game state and drawing the game on screen are completely separate, we can create game state within unit tests and give it arbitrary delta time and call whatever functions we want to simulate the game being played. Most unit tests only focus on one single class but some (like `testFruits`) use the `GameState` class.
|
||||
Because updates of game state and drawing the game on screen are completely separate, we can create game state within
|
||||
unit tests and give it arbitrary delta time and call whatever functions we want to simulate the game being played. Most
|
||||
unit tests only focus on one single class but some (like `testFruits`) use the `GameState` class.
|
||||
|
|
|
@ -1,32 +1,58 @@
|
|||
# Functions and Parameter Passing
|
||||
In Board.cpp we have functions to detect the state of the game board. Let's add a few helper functions and create unit tests for them.
|
||||
|
||||
Add a function that will check if a cell on the board is a wall. Then use that function in "isWalkableForPacMan" and "isWalkableForGhost". Remember you will need to add the function into the Board.hpp header file to be able to use it in the unit tests.
|
||||
In Board.cpp we have functions to detect the state of the game board. Let's add a few helper functions and create unit
|
||||
tests for them.
|
||||
|
||||
Add a function that will check if a cell on the board is a wall. Then use that function in "isWalkableForPacMan" and "
|
||||
isWalkableForGhost". Remember you will need to add the function into the Board.hpp header file to be able to use it in
|
||||
the unit tests.
|
||||
|
||||
# Game - Foundation
|
||||
|
||||
?
|
||||
|
||||
# Compilation, Linking and Assets
|
||||
One of the PacMan ghosts is missing and we will implement this ghost later. But for now, let's add the cpp and hpp files and run CMake to see the file added to our project. Then let's create an empty class called Blinky that looks like the other Ghost classes but it can be empty at this time. Where are the graphics for Blinky in the assets file and how can we make sure the ghost will be correctly rendered?
|
||||
|
||||
One of the PacMan ghosts is missing and we will implement this ghost later. But for now, let's add the cpp and hpp files
|
||||
and run CMake to see the file added to our project. Then let's create an empty class called Blinky that looks like the
|
||||
other Ghost classes but it can be empty at this time. Where are the graphics for Blinky in the assets file and how can
|
||||
we make sure the ghost will be correctly rendered?
|
||||
|
||||
# Memory and RAII
|
||||
|
||||
(corentin: maybe something about } that destroys things)
|
||||
We are going to play with using std::unique_ptr as we discussed in the slides. Since we will be reverting these changes back, do take a copy of the GameState files before we start. We will change PacMan within GameState into a std::unique_ptr<PacMan>, once this has been done the code will not compile, because there are further changes needed within the GameState class. Go through and figure out what needs to be updated. Can we change one of the ghosts into a std::unique_ptr? Are there any current design problems we will run into? What do these design issues say about ownership of data?
|
||||
We are going to play with using std::unique_ptr as we discussed in the slides. Since we will be reverting these changes
|
||||
back, do take a copy of the GameState files before we start. We will change PacMan within GameState into a std::
|
||||
unique_ptr<PacMan>, once this has been done the code will not compile, because there are further changes needed within
|
||||
the GameState class. Go through and figure out what needs to be updated. Can we change one of the ghosts into a std::
|
||||
unique_ptr? Are there any current design problems we will run into? What do these design issues say about ownership of
|
||||
data?
|
||||
|
||||
# Classes and Structs
|
||||
Within the GameState class, we often call all of the ghost functionality at the same time. So lets create a class that will contain all the ghosts. It can be called GhostState and the only member variables will be the ghosts. This class then needs to be contained within GameState instead of the ghosts. Then you need to write the functions for this GhostState class that are called within the GameState class.
|
||||
|
||||
Within the GameState class, we often call all of the ghost functionality at the same time. So lets create a class that
|
||||
will contain all the ghosts. It can be called GhostState and the only member variables will be the ghosts. This class
|
||||
then needs to be contained within GameState instead of the ghosts. Then you need to write the functions for this
|
||||
GhostState class that are called within the GameState class.
|
||||
|
||||
# std::array and ranged for
|
||||
waage: Maybe the "isIntersection" for the AI. Create an array of GridPosition and array of bool and loop through to check "isWalkableForPacMan" and then return the different kinds of intersections (top/right, right/bottom, bottom/left, left/top)
|
||||
|
||||
waage: Maybe the "isIntersection" for the AI. Create an array of GridPosition and array of bool and loop through to
|
||||
check "isWalkableForPacMan" and then return the different kinds of intersections (top/right, right/bottom, bottom/left,
|
||||
left/top)
|
||||
|
||||
# std::vector
|
||||
|
||||
?
|
||||
|
||||
# Iterators and algorithms
|
||||
|
||||
Use algorithms in Pellet and SuperPellet
|
||||
|
||||
# Lambdas and Function Templates
|
||||
|
||||
?
|
||||
|
||||
# Algorithmic Thinking
|
||||
|
||||
?
|
Loading…
Reference in a new issue