Академический Документы
Профессиональный Документы
Культура Документы
SUBMITTED BY
AMNA 2016-ag-7763
MARIAM HAFEEZ 2016-ag-7791
MOMINA SHAHID 2016-ag 7730
DEPARTMENT
COMPUTER SCIENCE
FACULTY
SCIENCE
SUBMITTED ON DATE
MAY 17, 2019
AMNA 2016-ag-7763
1. Create a game object in the scene from the zombie and view-cone sprites
2. Add an AI script to the zombie
3. Add a collision script to the view cone
4. Build a finite state machine to receive data from the AI
5. Add behaviors to the finite state machine to trigger the required behavior from the AI
script
6. Make a prefab from the finished zombie and add a whole bunch of them to the scene.
Introduction to environment:
C# is a more high-level approach than C++. It is usually faster to develop in. It comes with
a large framework of predeveloped components, which makes it particularly useful for server-
side programming. It is full of features that make development faster and easier, usually at the
cost of flexibility and/or runtime performance.
If MS goes bankrupt, C# is not dead: it is a standardized language, as are the core parts of
the framework. However, large parts of the .Net framework are still MS's own and in the
unlikely case that MS drops C#, there would probably be a significant loss of investment.
On the other hand, and though I'm not really knowledgeable about the subject, I'd guess Java
users would suffer more if Sun dropped out; in some ways at least.
Benefits:
Learn To Code in C#
A finite state machine sounds complicated but at its simplest, it is just a way of keeping
track of the situation (state) of an object and the rules which determine when that state will
change.
If you want to build a finite state machine in code, there are dozens of tutorials out there
already. Unity, however, provides a neat visual way to build an AI using Mecanim, the
animation system built into Unity. If you are concerned that this tutorial is going to be some kind
of dirty hack where we use the game engine in a way it was not designed to be used then let me
assure you this is a legitimate use for Mecanim and as we will see the way we handle things like
behaviors and communication between a finite state machine and its related objects is completely
integrated into Unity. What you should be able to do by the end of this tutorial is implement your
own, more complicated AI using these same simple techniques.
We will see how to set up a set of waypoints and then get our zombies to choose one at
random. The zombie will then set off towards it until it gets close and then it will pick another
one. This simple behavior will continue forever unless the player enters the zombie’s field of
view. Of course, the field of view is something we must define and we will do so with a triangle
shaped sprite to which we will attach a trigger collider. If the player enters the trigger, then the
zombie has “seen” the player. It would be simple to give the zombie more “sense” perhaps a
circle collider for hearing or smell.
When the player is seen by a zombie it will abandon its waypoint and home in on the player. If
the player escapes the zombies AI sight, the zombie will stop for a few seconds and then pick a
random waypoint and go back to doing what it was doing before lunch wandered into its
triangular field of view.
Take a look at this diagram read words in the rectangles that represent the states a zombie can be
in and also look at the lines connecting the states. You will see that each line has an arrow
indicating the direction that a state transitions from state to state as well as some notes I have
added indicating the parameters (variables of the state machine) that need to be true for the
transition to take place.
To explain the above, first of all, forget about the Exit and Any State states.When the state
machine starts it enters the Entry state and immediately and unconditionally transitions to the Get
New Waypoint state. When the Get New Waypoint state is entered it will communicate with the
zombie and the zombie will pick a new waypoint. At this point, the distance from the chosen
waypoint (distanceFromWaypoint) will be greater than one. This will cause a transition to
the Move To Waypoint state. The state machine will communicate this change to the zombie
object.
Every frame the zombie object will feed data into the state machine. One of these pieces of data
is its distance from the current waypoint. When this distance is less than one a transition occurs
back to the Get New Waypoint state and the zombie picks a new waypoint and sets off towards
it… until it is less than one unit away and so this continues.
If however, the player stumbles into the zombie’s vision (playerInSight is true) the zombie object
will let the state machine know and the state will change to Chase. The zombie’s behavior will
be modified by the state machine and a battle for life and death will ensue. In order to keep this
short enough for a single and functional tutorial, nothing happens when the zombie catches the
player. You would probably want to kill the player or subtract hit points, etc. In addition, you
would probably arm the player with a weapon etc.
If the player manages to escape the zombie’s sight then playerInSight will turn to false and the
state machine will transition to the Wait state. After the Wait state the state machine either goes
back to the Get New Waypoint state or if the player was careless enough to wander back into the
field of view then the Chase state will resume.
Methodology:
Starting the project
Scripts Create a new project in Unity, call it Zombie AI, choose the 2D option and click
the Create Project button.
Create some new folders to stay organized as we proceed. You need an FSM, Prefabs,
and Sprites, like this.
Import the three images below and keep them in the Sprites folder.
To get started with the visuals grab yourself a fancy background from opengameart.org. Import it
into the Sprites folder, drag it into the scene and repeat, positioning the background like tiles
until you have covered a little bit more than the camera area. This step is not essential, the entire
project will work without a background; however with just a plain background, when we set the
camera to follow the player there will be a reduced sense of movement. One alternative if you
don’t want to bother with the background is to simply play your game while observing the scene
view which has handy grid-lines. Background or not, let’s move on to the project proper.
Next, we will create some waypoints that the zombies will randomly choose from to wander
between. Once you see the code, you will see you could just as easily make your zombies follow
a path of waypoints if you prefer.
Right click in the Hierarchy tab and select Create Empty. Now we have an empty object that is
not visible to the player during the game. We want to make it easy for us to see in
the Scene view, however. Rename the empty object to p1. Make sure that p1 is selected and in
the Inspector window give it an icon to make it clearer in the Scene view. You do so by clicking
this icon in the top-left of the inspector window.
Choose a new icon. I changed it to the red pill shape but you can choose whatever you prefer.
You can see the new empty game object in the Scene view as shown in the next image.
The empty game object called p1 now has a nice clear icon and a tag called p1.
Repeat this process (empty object, icon, and tag)four more times and name them p2, p3, p4,
and p5. Actually, you can make as many or as few waypoints as you like but you will then need
to remember to vary the code as we proceed. Note that for this particular project to work all of
them must have a unique tag.
Arrange your waypoints around the scene. This is what my scene view looks like.
Now the zombies have some waypoints let’s get started with the zombies themselves.
Prefabricating an intelligent(ish) zombie
1. Create a game object in the scene from the zombie and view-cone sprites
2. Add an AI script to the zombie
3. Add a collision script to the view cone
4. Build a finite state machine to receive data from the AI
5. Add behaviors to the finite state machine to trigger the required behavior from the AI
script
6. Make a prefab from the finished zombie and add a whole bunch of them to the scene
In the Sprites folder select vision_cone. Then, in the Inspector window click the Pivot button,
then choose bottom and click Apply. This has made the center-bottom(narrow part) of the cone
the pivot point for this sprite. This will make things work when we combine two sprites next.
Drag the zombie and the sight_cone sprites into the scene. Then in the Hierarchy view, drag
the vision_cone game object to be a child of the zombie game object. Set the X, Y and Z values
of the vision_cone‘s Transform component to 0,0,0. This will make
the vision_cone and zombie objects line up, just as we want them to.
Add a Rigidbody 2D component to the zombie object and set it to Is Kinematic. We want the
zombie to move but not collide.
Right click in the Scripts folder and select Create | C# Script. Name it ZombieAi. Code the script
as follows. Read through the code including the comments and then we can discuss it.
CODE:
1 using UnityEngine;
2 using System.Collections;
16
18 Vector3 direction;
22
25 {
27 playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
28
30 animator = gameObject.GetComponent<Animator>();
31
39 point1,
40 point2,
41 point3,
42 point4,
43 point5
44 };
45
46 }
47
49 {
51 if (chasing)
52 {
54 rotateZombie();
55 }
56
58 if (!waiting)
59 {
61 }
62
63 }
64
66 {
68 distanceFromTarget = Vector3.Distance(waypoints[currentTarget].position,
transform.position);
69
animator.SetFloat("distanceFromWaypoint", distanceFromTarget);
70
animator.SetBool("playerInSight", inViewCone);
71
72
}
73
74
public void SetNextPoint()
75
{
76
// Pick a random waypoint
77
// But make sure it is not the same as the last one
78
int nextPoint = -1;
79
80 do
81 {
83 }
85
86 currentTarget = nextPoint;
87
90 rotateZombie();
91 }
92
94 {
97 rotateZombie();
98 }
99
101 {
104
106 {
110 }
111
113 {
115 }
116
117
119 {
121 }
122
}
Add the script to the zombie game object by selecting the zombie game object in
the Hierarchy window and clicking Add Component in the Inspector window. Now
choose Scripts | ZombieAi. This is how the code works.
At a glance, the code is long and sprawling but it really doesn’t hold too much that we haven’t
seen in other tutorials previously.
The variables initialized at the start include a Transform that will be used to track the position of
the player. The variables related to the state machine include an Animator. As we will see soon,
an Animator is a state machine. Each frame we will send values to the Animator/state machine.
These values are held in distanceFromTarget andinViewCone which will indicate how far away
a zombie is from its current waypoint as well as if the player is currently inside its trigger
collider. The chasing and waiting values do NOT get sent to the state machine. Rather, as we will
see, the state machine will update theses variables when appropriate. We then declare a bunch of
variables that will handle movement. A Vector3 called direction which keeps track of the
zombies heading, a float called walkSpeedwhich is how fast the zombie can move.
An int called currentTarget which indicates the position in an array which holds the Transform of
the current waypoint. The waypoints array into which we will load all the Transformcomponents
of our waypoints in the scene.
In the Awake method we use the tags of the waypoints and the player to get a reference to them.
All the waypoints are stashed in the waypoints array. The interesting thing that happens
in Awake is we initialize animator by getting a reference to an Animator component which is the
finite state machine we will soon build.
In Update there are just two if statements. If the zombie is currently chasing calculate the
direction to the player and turn to face him. If the zombie is not waiting just walk in whichever
direction it is facing. Note that thisdirection will have been previously set to a waypoint. This
implies that if the zombie is waiting it won’t move at all. We will see the rotateZombie method
soon.
In FixedUpdate is where the action happens. The first line of code calculates how far the zombie
is from the current waypoint then calls SetFloat and SetBool on animator to set the values
of distanceFromWaypoint andplayerInSight, inside the state machine. These two values will be
all that our finite state machine will need in order to make decisions and transition between
states.
At this point, we have not seen any way that the state machine can communicate back to the
zombie. We will get to the first part of the conundrum really soon.
Next up is the SetNextPoint method. SetNextPoint uses a do while loop to keep picking a new
position in thewaypoints array until it chooses one that is different to the current one. It then
alters direction and callsrotateZombie to do a little math to face the zombie in the correct
direction.
The Chase method sets direction relative to the player(rather than the next waypoint) then also
calls rotateZombie.
StopChasing does one thing. It sets chasing to false. When we build our state machine we will
see how it accesses this method in order to control how the zombie behaves. Note there is a
corresponding StartChasing method as well.
The RotateZombie method uses a trigomoteric function to calculate an angle to make the zombie
face where it is headed. If you want to know more about this then take a look at the tutorial series
on heading and trigonometric functions in games.
The last method, ToggleWaiting will also be called by the state machine to toggle the waiting
state of the zombie.
Add a collider by selecting the vision-cone in the Hierarchy and clicking the Add
Component | Physics2D | Polygon Collider 2D. If you check the scene view you will see that a
nice neat collider has been added to the vision-cone object. Check the Is Trigger checkbox so
that we can code a script to respond to objects entering the perimeter of the collider.
Right click in the Scripts folder and select Create | C# Script. Name it ConeOnTrigger. Code the
script as follows
1 using UnityEngine;
2 using System.Collections;
3
4 public class ConeOnTrigger : MonoBehaviour {
5
6 public ZombieAi zombieAi;
7
8
9 void OnTriggerEnter2D(Collider2D o)
10 {
11
12 if (o.gameObject.tag == "Player")
13 {
14 zombieAi.inViewCone = true;
15 }
16 }
17
18 void OnTriggerExit2D(Collider2D o)
19 {
20
21
22 if (o.gameObject.tag == "Player")
23 {
24 zombieAi.inViewCone = false;
25 }
26 }
27 }
Add the script to the vision-cone game object by selecting the vision-cone game object in
the Hierarchy window and clicking Add Component in the Inspector window. Now
choose Scripts | ConeOnTrigger. This is how the code works.
The previous code has a reference to the zombie object. Every time the player either enters or
leaves the trigger theinConeView boolean variable is updated appropriately. Remember the value
of inConeView is sent to the state machine every frame during the FixedUpdate method in
the ZombieAi script. We can start to see all the pieces of how we talk to the state machine.
Now we get to build the finite state machine that will receive the inputs and initiate the behaviors
we have just coded.
To get started let’s add a couple of components to the zombie game object.
Make sure the zombie object is selected in the Hierarchy then click Add Component in
the Inspector. Choose Miscellaneous | Animator. If you look closely at this new component you
will see a slot for a Controller. This is where we will add the finite state machine when we have
built it. Next, add another component to the zombie. Click Add Componentand choose Physics
2D | Rigidbody 2D. Set Gravity Scale to 0 to stop our zombie falling to oblivion and set it to Is
Kinematic. We want the zombie to move but not collide.
Let’s build the finite state machine in an animator controller. This sounds like a hack but it is a
totally legitimate way to program AI in Unity. Select Window | Animator from the Unity main
menu to create a workspace for this purpose. Dock the window (by dragging and dropping its
tab) somewhere you will have a large workspace. I docked mine in the same space as the Scene.
Right-click in the FSM folder and select Create | Animator Controller. Name the Animator
Controller ZombieFSM. Rearrange the states that are created for you like this following image.
Note this is not necessary it is just keeping things tidy.
Add the following states in the following order by right-clicking and selecting Create
State | Empty. You rename the states by selecting them in the Animator window and adjusting
the Name in the Inspector window.
Notice the first state you create is a different color and was automatically connected to
the Entry state. Rearrange your states to look like this next image. Notice we are already getting
close to how we envisioned our finite state machine at the start of the article.
Now we can add the two parameters and the transitions that their different values will trigger. To
add a parameter click Parameters in the top left of the Animator window and then click
the + icon. Add the following parameters as the following types.
playerInSight as a bool
distanceFromWaypoint as a float
Remember the FixedUpdate method in the ZombieAI class constantly updates these parameters.
Here is a reminder of the code which executes each frame.
Chase to Wait
Wait to Chase
Check you have the exact same state machine as shown in this image.
Zombie finite state machine with transitions links
Now we can plug in the values of the parameters that will initiate the transitions. Select the
transition that goes from Get New Waypoint to Move To Waypoint by left clicking it. Notice the
options that appear in the inspector window.
This is how the inspector should look after you have done this.
The Has Exit Time option optionally delays the exit when the condition is true.
The Conditions is the actual condition that would cause this transition link to be made. So as the
zombie is closer than one unit to the current waypoint it will get a new waypoint. Fill out all the
rest of the transition parameters and exit times as detailed next.
We have so far seen how the ZombieAi class repeatedly updates the state machine with the
values it needs and we have just programmed how and when the state machine will step back and
forth between states. Now we can see how the state machine communicates back to the
ZombieAi class using behaviors.
Behaviors are how the FSM communicates with the AI. In this project, the behaviors don’t
control HOW the AI does something they just choose WHICH ONE and WHEN.
To add a behavior we choose the state that we want to add a behavior to and then in the inspector
window click the Add Behavior button. We can add behaviors on numerous different events as
we will see.
Select the Get New Waypoint state and click Add Behavior. Choose New Script and
type SelectWaypointState in the Namefield. A new C# script has been added as a behavior. Open
the script and edit the code to look like this.
using UnityEngine;
1
using System.Collections;
2
3
public class SelectWaypointState : StateMachineBehaviour
4
{
5
6
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this
7 state
13
}
14
15
}
The first thing you will notice when you view the auto-generated script is there are a few more
methods. TheOnStateEnter method is called when the state is first entered. For this behavior, that
is exactly what we want. The code above, therefore, when the Get Next Waypoint state is entered
will get a reference to the appropriate instance of theZombieAi script and call
its SetNextPoint method. The ZombieAi script will take care of rotating the zombie and sending
it on its way.
Select the Chase state and follow the previous steps to create a behavior
called ChaseStateBehaviour. Edit the code to look like.
using UnityEngine;
1 using System.Collections;
5 // OnStateEnter is called when a transition starts and the state machine starts to evaluate
this state
6
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int
7 layerIndex) {
9 zombieAi.StartChasing();
10 }
11
12
13 // OnStateExit is called when a transition ends and the state machine finishes evaluating
this state
14
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int
15 layerIndex) {
In the Chase state, we get to use both OnStateEnter and OnStateExit. When the state machine
first enters the state it calls StartChasing on the ZombieAi script which changes where the
zombie is pointed and where it moves to each frame. In OnStateExit the same method is called
which causes the zombie to revert to its previous behavior of wandering between waypoints.
Select the Wait state and follow the previous steps to create a behavior called WaitingStateBehaviour.
Edit the code to look like this
using UnityEngine;
1 using System.Collections;
5 // OnStateEnter is called when a transition starts and the state machine starts to evaluate
this state
6
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int
7 layerIndex) {
9 zombieAi.ToggleWaiting();
10 }
11
12 // OnStateExit is called when a transition ends and the state machine finishes evaluating
this state
13
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int
14 layerIndex) {
We now have all the references in our scripts apart from the Player reference but we haven’t
made that yet.
Our zombie is almost done. Drag it from the Hierarchy to the Prefabs folder to create a prefab
from all of our hard work. Make sure the prefab has definitely been created in the Prefabs folder
and delete the zombie from the Hierarchy. Drag and drop a whole load of zombies from
the Prefabs folder into the scene.
Tip: If you can’t wait any longer, you could comment out the few lines of code
in ZombieAi.cs that refer to playerTransform(and are causing an error), run the game and see all
your zombies wandering around between the different waypoints.
Get started by adding the player sprite to the scene. Add a tag called Player to the player object. If you
need a reminder how to do this then refer back to when we created the waypoints. Add a Ridgidbody
2D and set its Gravity Scale to 0. Next, add a Box Collider 2D. Now add a new script component
named PlayerController. Edit the script to be the same as this next code.
1 using UnityEngine;
2 using System.Collections;
9 void Update () {
10
11 // Quit if the player presses Escape
12 if (Input.GetKey("escape"))
13 Application.Quit();
14
16 float h = Input.GetAxis("Horizontal");
17 float v = Input.GetAxis("Vertical");
18
22
25
28 }
29
30 }
This is very straight forward code to move the player and make him face the appropriate
direction. Note that if you plug a controller into your PC/Mac you will get more refined rotations
and movements compared to just using the cursor keys. We have discussed simple movement in
several previous tutorials.
Making the camera follow the player
Select the Main Camera object in the hierarchy. Add a new C# script as a component in
the Inspector and name it FollowCamera. Edit the code to be the same as this.
1 using UnityEngine;
2 using System.Collections;
5 {
9 void LateUpdate()
10 {
12 player.transform.position.y, transform.position.z);
13 }
14 }
This script keeps a reference to the Transform component on the player so the camera follows
the player as it moves.
Finally, select the Main Camera object and drag the player to the slot on
the FollowCamera script.
Running the game
Run the game and observe it in the scene view if you didn’t bother with the fancy background or
the game view if you did.
You can add hundreds of objects before even my fairly modest laptop starts to struggle. There
are optimizations we could make to our code but that would be for another tutorial. The point is
that our finite state machine tracks the states and makes all the decisions for us. It might not be
obvious how much coding this is saving us. Think about the vast number of if statements that
would be necessary to replace what the state machine is doing. Furthermore, it is much less error
prone. Click on a zombie in the Scene view, switch to the Animator view and then run the game.
You can see a nice animation of the states and when they transition. This is very clear to debug
when things don’t behave as expected. The visual nature of the Animator is also really simple to
add in extra states.
RESULT:
SCREENSHOT:
PROJECT COMPLETED