Unity 2D Pac-Man Tutorial
link project: https://drive.google.com/file/d/0B8-j62SXiaNsVFktb2lMUWxGeWc/view?usp=sharing
Foreword
Let's make a Pac-Man inspired game in Unity. The original Pac-Man game was released in October 1980 and soon became the most famous arcade game of all time. The game got so popular that even Unity included a tiny part of it in their game engine:
In this Tutorial we will make a Pac-Man clone with only 62 lines of code by using Unity's powerful 2D features. We will keep things as simple as possible and focus on the maze, the ghosts, the food and of course Pac-Man.
As usual, everything will be explained as easy as possible so everyone can understand it.
Here is a preview of the final game in action:
Requirements
Knowledge
Our Tutorial does not require any special skills. If you know your way around Unity and heard about GameObjects, Prefabs and Transforms before, then you are ready to go. And if you didn't, don't worry about it too much.
Feel free to read our easier Unity Tutorials like Unity 2D Pong Game to get a feeling for the engine first.
Unity Version
Our Pac-Man Tutorial will be developed with Unity 5.0.0f4. Newer versions should work fine as well, older versions may or may not work. The free version of Unity 5 now comes with all the engine features, which makes it the recommended version.
Project Setup
Let's get to it. We will start Unity and select New Project:
We will name it pacman, select any location like C:\, select 2D and clickCreate Project:
We will select the Main Camera in the Hierarchy and then set the Background Color to black. We will also adjust the Size and the Positionlike shown in the following image:
The Maze
The Maze Sprite
Let's create the Pac-Man typical maze. We will draw one that is inspired by the original one, but not completely the same:
Note: right click on the image, select Save As..., navigate to the project'sAssets folder and save it in a new Sprites folder.
Note: right click on the image, select Save As..., navigate to the project'sAssets folder and save it in a new Sprites folder.
After saving it in our Project directory we can select it in the Project Area:
And then modify the Import Settings in the Inspector:
Note: a Pixels Per Unit value of 8 means that 8 x 8 pixels will fit into one unit in the game world. We will use this value for all our textures. We selected the value 8 because the distance between two Pac-Dots (the food) is always 8 px and we want that distance to be 1 Unit in our game. We selected Top-Left for thePivot because it makes the alignment easier later on.
Note: a Pixels Per Unit value of 8 means that 8 x 8 pixels will fit into one unit in the game world. We will use this value for all our textures. We selected the value 8 because the distance between two Pac-Dots (the food) is always 8 px and we want that distance to be 1 Unit in our game. We selected Top-Left for thePivot because it makes the alignment easier later on.
Now we can drag the maze Sprite from our Project Area into the Scene:
Let's take a look at the Inspector and position the maze at (0, 0) in order to keep things clean:
Maze Physics
Right now the maze is only an image, nothing more. It's not part of the physics world, things won't collide with it and Pac-Man could walk right through the walls. Let's change that by adding a Collider for each wall in the maze.
We will select Add Component->Physics 2D->Box Collider 2D in theInspector:
If we take a look in the Scene then we can see that Unity wrapped the Collider around the whole maze, which is not exactly what we want:
What we really want is to have a Collider around each wall of the maze. There are two ways to do this. We could either create an algorithm that reads the maze image and generates Colliders based on it, or we could just keep it simple and add all the Colliders manually.
Let's click on the Edit Collider button in the Inspector:
Note: this button is new in Unity 4.6, so make sure to use the latest version if you don't see a button there.
Note: this button is new in Unity 4.6, so make sure to use the latest version if you don't see a button there.
This allows us to modify the Collider in the Scene by using the green dots:
We will repeat this process for every wall in our maze. All we have to do is select Add Component->Physics 2D->Box Collider 2D, press the Edit Collider button and then modify it in the Scene until it fits the next wall.
It is very important that each Collider is perfectly exact. For example if we zoom in then the green line should still perfectly wrap around the blue box:
The trick is to choose the Collider's Center and Size properties so that they are always like 1.25 or 1.5 or 1.75 or 2.00, and never like 1.24687 or1.25788. Here are some examples:
Note: if later on your Pac-Man get's stuck in the maze or has trouble moving, then it's because the Maze Colliders are not perfectly exact.
Note: if later on your Pac-Man get's stuck in the maze or has trouble moving, then it's because the Maze Colliders are not perfectly exact.
Here is the final result:
Note: adjusting all the Colliders may take a few minutes of time.
Note: adjusting all the Colliders may take a few minutes of time.
Adding Pac-Man
The Pac-Man Sprite
Now it's time for the most important part of our game: Pac-Man. We will need one animation for each movement direction:
- right
- left
- up
- down
- right
- left
- up
- down
Let's draw all the animations in one image, where there is one animation per row:
Note: right click on the image, select Save As... and save it in the project'sAssets/Sprites folder.
Note: right click on the image, select Save As... and save it in the project'sAssets/Sprites folder.
We will use the following Import Settings for it:
Slicing the Pac-Man Sprite
It's important to set the Sprite Mode to Multiple, which tells Unity that there is more than one Pac-Man in our Sprite. Let's open the Sprite Editor by clicking the button:
Now we can tell Unity where each Pac-Man animation slice is located in our Sprite. We will select Slice and then slice it as 16 x 16 Grid and press the Slice button afterwards:
Once the Sprite was sliced, we can close the Sprite Editor again. If Unity asks us about Unapplied Import Settings then we will click on Apply.
As a result we now have 12 slices under our Pac-Man Sprite in theProject Area:
Creating the Pac-Man Animations
Okay so now that we have the animation slices, we can create our 4 animations from it. As a reminder, here are the animations that we will need:
- right (Slice 0, 1 and 2)
- left (Slice 3, 4 and 5)
- up (Slice 6, 7 and 8)
- down (Slice 9, 10 and 11)
Let's create the right animation. We will begin by selecting the first three slices in the Project Area:
And then dragging them into the Scene:
Now whenever we drag several slices into the Scene, Unity will know that we want to create an animation from them, hence why it automatically asks us where to save the animation. Let's save it asright.anim in a new PacmanAnimation folder. Unity just added apacman_0 GameObject to the Scene and two files to the Project Area:
The first file is the animation state machine that specifies things like the animation speed and blend trees. The second one is the animation itself.
We will repeat this process for the rest of the animations (Slice 3, 4, 5 for left; Slice 6, 7, 8 for up and Slice 9, 10, 11 for down).
Here is what our Hierarchy looks like afterwards:
Cleaning up after Unity
Unity created one GameObject for each animation, but we only need the first one as we will see soon. Let's select the other 3 and then right clickand delete them:
A similar thing happened in our Project Area. We now have 4animations and 4 animation state machines:
Again we only need one animation state machine, so let's delete the other three:
The Pac-Man Animation State Machine
Right now we have 4 animation files, but Unity doesn't know when to play which animation yet. The solution to our problem is part of Unity's unbelievably powerful Mecanim animation system. We will need a animation state machine that has 4 states:
- right
- left
- up
- down
We will also add Transitions so Unity knows when to switch from one animation state to another.
Note: Unity will play the right animation over and over again while in rightstate. It will use Transitions to know when to switch to another state. Unity does all of that automatically, all we have to do is notify it about Pac-Man's movement direction from within a Script later on.
Okay so let's double click the pacman_0 animation state machine file in our Project Area:
Now we can see the state machine in the Animator:
Creating the Other States
The right state is already in the Animator, so let's add the left state by simply dragging the left.anim file from the ShipAnimation folder from our Project Area into the Animator:
This process can be repeated for the remaining two states:
The big deal about Mecanim is that it will take care of the animation states on its own, fully automatically. All we have to do is tell Mecanim to watch out for our Pac-Man's movement direction, nothing more. Mecanim will then switch between animation states on its own, without us having to do anything.
Creating the Parameters
So let's tell Mecanim that it should watch out for Pac Man's movement direction. A movement direction is of type Vector2, the following image shows some possible movement directions:
Our Pac-Man will only have 4 movement directions (up, down, left, right). Which means that we only need the following 4 Transitions in our animation state machine:
- If DirY > 0 then go to up (like DirY=1, DirY=2, DirY=3 and so on)
- If DirY < 0 then go to down (like DirY=-1, DirY=-2, DirY=-3 and so on)
- If DirX > 0 then go to right (like DirX=1, DirX=2, DirX=3 and so on)
- If DirX < 0 then go to left (like DirX=-1, DirX=-2, DirX=-3 and so on)
Let's take a look at the left area of the Animator and select theParameters tab:
Here we will click on the + at the right and then add one Parameter of type Float with the name DirX and another Parameter of type Float with the name DirY:
Later on we can set those parameters from within a Script by writing:
GetComponent<Animator>().SetFloat("DirX", 0);
GetComponent<Animator>().SetFloat("DirY", 0);
Creating the Transitions
We want Unity to automatically switch to different animation states based on those parameters. Transitions are used to achieve just that. For example, we could add a Transition from left to right with the Condition that DirX > 0. However it's considered best practice to have a small error tolerance because floating point comparison is not always perfect, hence why we will use DirX > 0.1 instead.
Now we would also have to use a DirX > 0.1 Transition from every other state to right. To save us from doing all this work, we can use Unity'sAny State.
The Any State stands for literally any state. So if we create a Transition from Any State to right then it's the same as creating a Transition fromleft, up and down to right.
Let's right click the Any State and select Make Transition. Afterwards we can drag the white arrow onto the right state:
Now we can click on the white arrow and take a look at the Inspectorwhere we can modify the Transition's Condition (or in other words, when it should switch). We will set it to DirX > 0.1:
Note: press the + at the bottom right to add a Condition.
Note: press the + at the bottom right to add a Condition.
Let's also disable Can Transition To Self in the Settings:
Note: this avoids weird situations where an animation would be restarted all the time while holding down a movement key.
Note: this avoids weird situations where an animation would be restarted all the time while holding down a movement key.
As result, whenever Pac-Man walks to the right (DirX > 0.1), the animator will switch to the right state and play the animation.
We will add 3 more Transitions:
- Any State to left with the Condition DirX < -0.1
- Any State to up with the Condition DirY > 0.1
- Any State to down with the Condition DirY < -0.1
Here is how it looks in the Animator now:
We are almost done with our animation state machine. There is one last adjustment to be made here, so let's select all states and then modify their Speed in the Inspector so that the animation doesn't look too fast:
If we press Play then we can already see the right animation:
Pac-Man Naming and Positioning
We want to keep everything nice and clean, so let's select the pacman_0GameObject in the Hierarchy and then rename it to pacman (right click it and select rename, or press F2) :
We will also change its position to (14, 14) so it's not outside the maze anymore:
Pac-Man Physics
Right now Pac-Man is only an image, nothing more. He is not part of the physics world, things won't collide with him and he can't move or anything else. We will need to add a Collider to make him part of the physics world, which means that things will collide with Pac-Man instead of walking right through him.
Pac-Man is also supposed to move around. A Rigidbody takes care of stuff like gravity, velocity and other forces that make things move. As a rule of thumb, everything in the physics world that is supposed to move around needs a Rigidbody.
Let's select the pacman GameObject in the Hierarchy, select Add Component->Physics 2D->Circle Collider 2D as well as Add Component->Physics 2D->Rigidbody 2D. It's important that we use exactly the same settings as shown in the following image:
Alright, Pac-Man is now part of the physics world. He will collide with other things and other things will collide with him. This will also cause the OnCollisionEnter2D function to be called in any Script that is attached to Pac-Man.
The Movement Script
Alright now there are several ways to make Pac-Man move. The easiest way would be to create a Script that simply checks for Arrow-Key presses and then move Pac-Man a bit up/down/left/right when needed. This would work, but it wouldn't feel very good.
Instead we will try to be more exact. Whenever the player presses one of the Arrow-Keys, Pac-Man should move exactly 1 unit into the desired direction. The Pac-Dots (the food) will also be positioned with a 1 unit distance between them, so it only makes sense that Pac-Man always moves exactly one unit.
Let's select pacman in the Hierarchy and press Add Component->New Script, name it PacmanMove and select CSharp as language. We will also move the Script into a new Scripts folder in the Project Area (just to keep things clean):
Alright let's double click the Script so it opens:
using UnityEngine;
using System.Collections;
public class PacmanMove : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
The Start function is automatically called by Unity when starting the game. The Update function is automatically called over and over again, roughly 60 times per second (this depends on the current frame rate, it can be lower if there are a lot of things on the screen).
There is yet another type of Update function, which is FixedUpdate. It's also called over and over again, but in a fixed time interval. Unity's Physics are calculated in the exact same time interval, so it's always a good idea to use FixedUpdate when doing Physics stuff:
using UnityEngine;
using System.Collections;
public class PacmanMove : MonoBehaviour {
void Start() {
}
void FixedUpdate() {
}
}
We will need a public variable so that we can modify the movement speed in the Inspector later on:
using UnityEngine;
using System.Collections;
public class PacmanMove : MonoBehaviour {
public float speed = 0.4f;
void Start() {
}
void FixedUpdate() {
}
}
We will also need a way to find out if Pac-Man can move into a certain direction or if there is a wall. So for example if we would want to find out if there is a wall at the top of Pac-Man, we could simply cast a Linefrom one unit above of Pac-Man to Pac-Man and see if it hit anything. If it hit Pac-Man himself then there was nothing in-between, otherwise there must have been a wall.
Here is the function:
bool valid(Vector2 dir) {
// Cast Line from 'next to Pac-Man' to 'Pac-Man'
Vector2 pos = transform.position;
RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);
return (hit.collider == collider2D);
}
Note: we simply casted the Line from the point next to Pac-Man (pos + dir) to Pac-Man himself (pos).
We will also need a function that makes Pac-Man move 1 unit into the desired direction. Of course we could just do:
transform.position += dir;
But this would look too abrupt because it's such a huge step. Instead we want him to go there smoothly, one small step at the time. Let's store the movement destination in a variable and then use FixedUpdate to go towards that destination step by step:
using UnityEngine;
using System.Collections;
public class PacmanMove : MonoBehaviour {
public float speed = 0.4f;
Vector2 dest = Vector2.zero;
void Start() {
dest = transform.position;
}
void FixedUpdate() {
// Move closer to Destination
Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
GetComponent<Rigidbody2D>().MovePosition(p);
}
bool valid(Vector2 dir) {
// Cast Line from 'next to Pac-Man' to 'Pac-Man'
Vector2 pos = transform.position;
RaycastHit2D hit = Physics2D.Linecast(pos + dir, pos);
return (hit.collider == collider2D);
}
}
Note: we used GetComponent to access Pac-Man's Rigidbody component. We then use it to do the movement (we should never use transform.position to move GameObjects that have Rigidbodies).
Let's also watch out for arrow key presses whenever we are not moving.
Note: we are not moving if the current position equals the destination.
Note: we are not moving if the current position equals the destination.
Here is our FixedUpdate function with Input checks:
void FixedUpdate() {
// Move closer to Destination
Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
GetComponent<Rigidbody2D>().MovePosition(p);
// Check for Input if not moving
if ((Vector2)transform.position == dest) {
if (Input.GetKey(KeyCode.UpArrow) && valid(Vector2.up))
dest = (Vector2)transform.position + Vector2.up;
if (Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right))
dest = (Vector2)transform.position + Vector2.right;
if (Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up))
dest = (Vector2)transform.position - Vector2.up;
if (Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right))
dest = (Vector2)transform.position - Vector2.right;
}
}
Note: transform.position is casted to Vector2 because this is the only way to compare or add another Vector2. Also -Vector2.up means left and -Vector2.right means down.
If we save the Script and press Play then we can now move Pac-Man with the arrow keys:
Setting the Animation Parameters
Right now we can perfectly move Pac-Man around the maze, but the animator doesn't play all the animations yet. No problem, let's just modify our code to calculate the current movement direction and then set the animator parameters:
void FixedUpdate() {
// Move closer to Destination
Vector2 p = Vector2.MoveTowards(transform.position, dest, speed);
GetComponent<Rigidbody2D>().MovePosition(p);
// Check for Input if not moving
if ((Vector2)transform.position == dest) {
if (Input.GetKey(KeyCode.UpArrow) && valid(Vector2.up))
dest = (Vector2)transform.position + Vector2.up;
if (Input.GetKey(KeyCode.RightArrow) && valid(Vector2.right))
dest = (Vector2)transform.position + Vector2.right;
if (Input.GetKey(KeyCode.DownArrow) && valid(-Vector2.up))
dest = (Vector2)transform.position - Vector2.up;
if (Input.GetKey(KeyCode.LeftArrow) && valid(-Vector2.right))
dest = (Vector2)transform.position - Vector2.right;
}
// Animation Parameters
Vector2 dir = dest - (Vector2)transform.position;
GetComponent<Animator>().SetFloat("DirX", dir.x);
GetComponent<Animator>().SetFloat("DirY", dir.y);
}
Note: we calculated the current movement direction with basic vector math. All we had to do was subtract the current position from the destination.
And that's all there is to it. If we save the Script and press Play then we can now see the proper animations:
Drawing Pac-Man in the Foreground
Before we start to work on the Pac-Dots, we should make sure that Pac-Man is always drawn in front of them. We are making a 2D game, so there isn't really any Z order like in 3D games. This means that Unity just draws objects as it pleases. Let's make sure that Unity always draws Pac-Man in front of everything else.
There are two ways to do this. We could either change the Sprite Renderer's Sorting Layer property or we could change the Order in Layer property. The Sorting Layer is important for bigger games with far more objects. For us it's enough to simply change the Order in Layerto 1:
Note: Unity draws objects sorted by their order. It starts with the lowest order and continues with higher orders. So if Pac-Man has the order 1 then he's always drawn after the maze and the food and anything else with order 0. And because he's drawn after everything else, he's automatically in front of everything else.
Note: Unity draws objects sorted by their order. It starts with the lowest order and continues with higher orders. So if Pac-Man has the order 1 then he's always drawn after the maze and the food and anything else with order 0. And because he's drawn after everything else, he's automatically in front of everything else.
The Pac-Dots
The little dots that Pac-Man can eat are called Pac-Dots. Some might now them as 'fruits' or just 'food'. We will begin by drawing a small 2x2px image of a Pac-Dot:
- pacdot.png
Note: right click on the link, select Save As... and save it in the project'sAssets/Sprites folder.
- pacdot.png
Note: right click on the link, select Save As... and save it in the project'sAssets/Sprites folder.
We will use the following Import Settings for it:
Afterwards we can drag it into the Scene. We want to be notified when Pac-Man walks over a Pac-Dot. All we have to do is select Add Component->Physics 2D->Box Collider 2D in the Inspector and then select Is Trigger:
Note: a Collider with IsTrigger enabled only receives collision information, it does not physically collide with other things.
Note: a Collider with IsTrigger enabled only receives collision information, it does not physically collide with other things.
Unity will automatically call the OnTriggerEnter2D function whenever Pac-Man or one of the Ghosts walk over the Pac-Dot.
So let's select Add Component->New Script, name it Pacdot, selectCSharp, move it into our Scripts folder and then open it:
using UnityEngine;
using System.Collections;
public class Pacdot : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
We won't need the Start or the Update function, so let's remove both of them. Instead we will use the previously mentioned OnTriggerEnter2Dfunction:
using UnityEngine;
using System.Collections;
public class Pacdot : MonoBehaviour {
void OnTriggerEnter2D(Collider2D co) {
// Do Stuff...
}
}
Now this one is easy. We will check if the thing that walked over the Pac-Dot was Pac-Man, and if so then we will destroy the Pac-Dot:
using UnityEngine;
using System.Collections;
public class Pacdot : MonoBehaviour {
void OnTriggerEnter2D(Collider2D co) {
if (co.name == "pacman")
Destroy(gameObject);
}
}
Note: if we wanted to implement a highscore in our game, then this would be the place to increase it.
Now we can right click the Pac-Dot in the Hierarchy, select Duplicateand move it to the next free position. It's important that we always position the Pac-Dots at rounded coordinates like (1, 2) and never (1.003, 2.05).
Here is the result after duplicating and positioning the Pac-Dot over and over again:
If we press Play then Pac-Man can now eat them:
Feel free to also move all those Pac-Dots into the maze GameObject:
So that we can collapse the maze GameObject in order to have a clean view at the Hierarchy:
The Gosts
A good Pac-Man clone needs some enemies, so let's add a few ghosts.
The Red Ghost Image
As usual we will begin by drawing a ghost Sprite with all the animations in it. Each row will contain one animation:
- right
- left
- up
- down
The first one will be the red ghost, also known as Blinky:
Note: right click on the image, select Save As... and save it in the project'sAssets/Sprites folder.
Note: right click on the image, select Save As... and save it in the project'sAssets/Sprites folder.
Let's select it in the Project Area and then modify the Import Settings in the Inspector:
Creating the Animations
It's important that we set the Sprite Mode to Multiple again because our Sprite contains several slices. Let's click the Sprite Editor button and slice it as a 16 x 16 grid:
Afterwards we can close the Sprite Editor and press Apply. Now it's time to create the animations by dragging the slices into the Scene, just like we did with Pac-Man.
At first we will drag Slice 0 and 1 into the Scene and save the animation as right.anim in a new BlinkyAnimation folder.
We will repeat this process for:
- Slice 2 and 3 as left
- Slice 4 and 5 as up
- Slice 6 and 7 as down
Cleaning up after Unity
Now we can clean up the Hierarchy again by deleting the blinky_2,blinky_4 and blinky_6 GameObjects:
We can also delete the blinky_2, blinky_4 and blinky_6 files from theBlinkyAnimation folder in the Project Area:
The Animation State Machine
Let's double click the blinky_0 file to open the Animator:
We will create the exact same animation state machine that we used for Pac-Man before. All we have to do is drag in the animations, create the Parameters and then set the Transitions:
- Any State to right with the Condition DirX > 0.1
- Any State to left with the Condition DirX < -0.1
- Any State to up with the Condition DirY > 0.1
- Any State to down with the Condition DirY < -0.1
Here is the final animation state machine in the Animator:
Renaming and Positioning Blinky
Let's make sure to keep everything nice and clean. We will select theblinky_0 GameObject in the Hierarchy and rename it to blinky:
We will also change the position in the Inspector so that Blinky is in the middle of the maze:
Ghost Physics
Alright so Blinky should be part of the Physics world again. Let's selectAdd Component->Physics 2D->Circle Collider 2D in the Inspector and assign the following properties:
Note: we enabled Is Trigger because Blinky is a ghost, and ghosts can walk right through things.
Note: we enabled Is Trigger because Blinky is a ghost, and ghosts can walk right through things.
Blinky is also supposed to move around the maze, which means that we will need to add a Rigidbody to him. Let's select Add Component->Physics 2D->Rigidbody 2D:
Bringing Blinky to the Foreground
Just like Pac-Man, we want Blinky to be drawn in the foreground, in front of the Pac-Dots. All we have to do to is set the Order in Layervalue to 1:
The Ghost Movement
Alright so we don't know very much about how the AI works in the original Pac-Man game, except for the fact that it's deterministic (which is a fancy word for not-random). It also appears as some ghosts are more focused on moving around the maze, while others are more focused on following Pac-Man, or perhaps even trying to position themselves in front of him.
In this Tutorial we will focus on the easiest of the AI options: moving around the maze. We will create a waypoint movement Script that makes Blinky run along a certain path. What sounds almost too simple is actually a pretty decent solution to our AI problem. The longer and the more complex the path, the harder it is for the player to evade Blinky.
Let's select Add Component->New Script, name it GhostMove, selectCSharp and then move it into our Scripts folder. Afterwards we can double click it so it opens:
using UnityEngine;
using System.Collections;
public class GhostMove : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
We won't need the Start or the Update function, instead we will use theFixedUpdate function (because it's meant for physics stuff like movement):
using UnityEngine;
using System.Collections;
public class GhostMove : MonoBehaviour {
void FixedUpdate () {
}
}
Let's add a public Transform array so that we can set the waypoints in the Inspector later on:
using UnityEngine;
using System.Collections;
public class GhostMove : MonoBehaviour {
public Transform[] waypoints;
void FixedUpdate () {
}
}
Note: an array means that it's more than just one Transform.
We will also need some kind of index variable that keeps track of the waypoint that Blinky is currently walking towards:
using UnityEngine;
using System.Collections;
public class GhostMove : MonoBehaviour {
public Transform[] waypoints;
int cur = 0;
void FixedUpdate () {
}
}
Note: the current waypoint can always be accessed with waypoints[cur].
And of course a movement speed variable:
using UnityEngine;
using System.Collections;
public class GhostMove : MonoBehaviour {
public Transform[] waypoints;
int cur = 0;
public float speed = 0.3f;
void FixedUpdate () {
}
}
Now we can use the FixedUpdate function to go closer to the current waypoint, or select the next one as soon as we reached it:
void FixedUpdate () {
// Waypoint not reached yet? then move closer
if (transform.position != waypoints[cur].position) {
Vector2 p = Vector2.MoveTowards(transform.position,
waypoints[cur].position,
speed);
GetComponent<Rigidbody2D>().MovePosition(p);
}
// Waypoint reached, select next one
else cur = (cur + 1) % waypoints.Length;
}
Note: we used the Vector2.MoveTowards function to calculate a point that is a bit closer to the waypoint. Afterwards we set the ghost's position withrigidbody2D.MovePosition. If the waypoint is reached then we increase thecur variable by one. We also want to reset the cur to 0 if it exceeds the list length. We could use something like if (cur == waypoints.Length) cur = 0, but using the modulo (%) operator makes this look a bit more elegant.
Let's not forget to set the Animation Parameters too:
void FixedUpdate () {
// Waypoint not reached yet? then move closer
if (transform.position != waypoints[cur].position) {
Vector2 p = Vector2.MoveTowards(transform.position,
waypoints[cur].position,
speed);
GetComponent<Rigidbody2D>().MovePosition(p);
}
// Waypoint reached, select next one
else cur = (cur + 1) % waypoints.Length;
// Animation
Vector2 dir = waypoints[cur].position - transform.position;
GetComponent<Animator>().SetFloat("DirX", dir.x);
GetComponent<Animator>().SetFloat("DirY", dir.y);
}
Great, there is one last thing to add to our Script. The ghosts should destroy Pac-Man upon colliding with him:
void OnTriggerEnter2D(Collider2D co) {
if (co.name == "pacman")
Destroy(co.gameObject);
}
Note: feel free to decrease Pac-Man's lives or show a Game Over screen at this point.
Adding Waypoints
Alright, let's add some waypoints for Blinky. We will begin by selectingGameObject->Create Empty from the top menu. We will rename it toBlinky_Waypoint0 and then assign a Gizmo to it so we can see it easier in the Scene:
Note: a Gizmo is just a visual helper, we don't see it when playing the game.
Note: a Gizmo is just a visual helper, we don't see it when playing the game.
Let's also position it at (15, 20). Here is how it looks in the Scene now:
Now we can duplicate the Waypoint, rename it to Blinky_Waypoint1and position it at (10, 20):
Then Blinky_Waypoint2 at (10, 14):
And Blinky_Waypoint3 at (19, 14):
And finally Blinky_Waypoint3 at (19, 20):
And because our Movement Script will automatically continue to walk to the first waypoint after the last one was reached, we will have a perfect loop.
Let's select blinky in the Hierarchy again and then drag one waypoint after another into the Waypoints slot of our GhostMove Script:
If we press Play then we can see how Blinky moves along the waypoints:
Of course, the current waypoints are rather simple. So feel free to create a more complex route like this one:
Pinky, Inky and Clyde
We don't want Blinky to feel lonely in there, so let's repeat the same work flow for Pinky, Inky and Clyde:
Note: it's important that we use different waypoints and movement speeds for each ghost in order to make the game more challenging.
Note: it's important that we use different waypoints and movement speeds for each ghost in order to make the game more challenging.
If we press Play then we can now play a nice round of Pac-Man:
Summary
We just created a very solid, fast and simple 2D Pac-Man clone in Unity. We learned about Pixels to Units, Mecanim, Colliders, Rigidbodies, Layer Orders and Scripting. And even though the AI is very simple and deterministic, the game is still very challenging.
Now it's up to the reader to make the game even more fun. There are tons of features that could be added:
- Sound
- High Score
- Advanced AI
- Portals
- More Levels
- Lives
- Energizers
- More Animations
link project: https://drive.google.com/file/d/0B8-j62SXiaNsVFktb2lMUWxGeWc/view?usp=sharing
0 comments:
Post a Comment