Chasing and Evading Sample
This sample shows how to implement several simple behaviors for AI, including chasing, evading, and wandering.Sample Overview
This sample demonstrates chase and evade behaviors by using three actors—a cat, a mouse, and a tank. The tank and the mouse are controlled by AI, and the cat is controlled by the player. The tank chases the cat, and the mouse runs from the cat. The tank and the mouse wander around the screen when the cat is not nearby. The "turn toward" functionality from the "AI Series 1: Aiming" sample makes it very simple to create these behaviors—chase, evade, and wander—very simple.
Minimum Shader Profile
- Vertex Shader Model 1.1
- Pixel Shader Model 1.1
Sample Controls
This sample uses the following keyboard and gamepad controls.
Action | Keyboard Control | Gamepad Control |
---|---|---|
Move the cat. | UP ARROW, DOWN ARROW, LEFT ARROW, and RIGHT ARROW | Left thumb stick, D-Pad |
Exit the sample. | ESC or ALT+F4 | BACK |
How the Sample Works
Chase
The sample first introduces chase behavior. How does it work? In the first sample of this series, we saw how to make one object turn toward another by using the method TurnToFace. This method makes it very easy to make the tank chase the cat—all we have to do is make the tank TurnToFace the cat, and then move forward.
Evade
The evade behavior is similar to the chase behavior. An object turns away from the target, instead of turning toward the target. If we make the mouse turn away from the cat and move forward, the mouse evades the cat. Simple enough—but the TurnToFace method takes a point to turn toward, not a point to turn away from. So, to what point do we make the mouse turn toward?

This diagram shows the cat and the mouse. The position of the mouse is represented by the vector mousePosition, and the position of the cat is represented by the vector catPosition. We can see that if the mouse turns towards the point marked seekPosition, it then turns away from the cat. We can use the catToMouse vector to find seekPosition:
- seekPosition = mousePosition + catToMouse
The catToMouse vector is actually just mousePosition - catPosition, which we can substitute in the above equation. This gives us the following:
- seekPosition = mousePosition + (mousePosition - catPosition)
- seekPosition = 2 * mousePosition - catPosition
To make the mouse evade the cat, we use this equation to calculate seekPosition. The mouse then moves toward seekPosition and away from the cat.
Wander
The wander behavior uses a vector called wanderDirection for each character. In every frame, wanderDirection is slightly modified. When a character wanders, it turns so that it faces toward wanderDirection.
To keep the characters at the center of the screen, you can also make characters turn to face the screen center. The Wander method calculates a variable called turnToCenterSpeed. This calculation determines how much the characters will turn towards the center of the screen. This variable is based on the distance of the character from the center of the screen. The farther a character is from the screen center, the more it aims back toward the center.
To do this, first we calculate normalizedDistance, which varies from 0 to 1 depending on how far the character is from screenCenter. The turnToCenterSpeed variable is .3 * normalizedDistance * normalizedDistance * turnSpeed. By squaring normalizedDistance, we get a smooth response curve. Near the center of the screen, a character does not turn very much. However, toward the outer edge of the screen, it turns much more sharply. The turnSpeed variable is the character's maximumTurnSpeed, and .3 is simply a constant that was found by experimentation. The constant .3 seems to work well for this sample, but feel free to play with it for different results. Smaller values will make the characters explore further from the center, but the characters may get stuck at the edges. Larger numbers will hold the characters to the center of the screen. If the number is too large, the characters may end up orbiting the center.
Movement
All the behaviors in this sample are created by turning toward some point and then moving forward.
The TurnToFace
method can be used to turn towards a point, but how do we move forward? The direction the characters face is stored as an angle. How do we use this angle to change the x and y components of the character's positions?
In other words, how can we convert from a heading angle to a heading vector?
Trigonometry to the rescue again. Don't cringe, though—this is actually very straightforward. Trigonometry has a concept of a unit circle, which is a circle that has a radius of 1. The cosine and sine functions that we all know and love are defined as x and y coordinates on the unit circle.

In this diagram, the blue line goes from (0,0) to a point on the unit circle, and makes an angle of θ with the positive x axis. The coordinates of the point where the unit circle intersects the blue line are (cosine(θ), sine(θ)).
So, what does this tell us? Remember, we're trying to find a vector ( x, y coordinates ) from an angle. Well, now it's easy:
C# |
---|
Vector2 headingVector = new Vector2((float)Math.Cos(headingAngle), (float)Math.Sin(headingAngle)); |
To find a heading vector, all we have to do is take the sin and cosine of the heading angle.
Hysteresis
One common problem in game programming—particularly AI programming—is hysteresis. The following text, from Shawn Hargreaves's blog, explains the problem very well.
Sorting Out the Complexity
Games often have to make discrete decisions based on continuously varying analog input values, as in the following:
C# |
---|
if (health > 0.5) AttackEnemy(); else RunAway(); |
This sort of thing happens frequently. Games are complex simulations that contain a lot of chaotically varying information. Often they need to examine the simulation state and make decisions based on specific values.
Game simulations tend to produce a lot of small and basically random fluctuations from one frame to the next. When these values are used to control a discrete decision, and when the input value is close to the decision threshold, the results can look pretty silly. For example, an AI character might decide to attack you, only to change its mind on the next frame and run away, only to heal slightly and decide to attack you again, with the end result that the character just spins in a circle.
The solution is to add hysteresis in your decision making code. Any time you find yourself making a Boolean decision by testing a floating point value:
C# |
---|
if (inputValue > threshold) DoSomething(); else OtherThing(); |
you should think about including a hysteresis region:
C# |
---|
const float hysteresisAmount = 0.1; if (inputValue > threshold + hysteresisAmount) DoSomething(); else if (inputValue < threshold - hysteresisAmount) OtherThing(); else KeepOnDoingWhateverYouAlreadyAre(); |
This prevents tiny fluctuations from causing unwanted flickering in the decision result. If the input value is significantly above or below the decision threshold, the game will change state. However, when the input is near the threshold, it just keeps on doing whatever it had previously decided.
This concept is used in two different ways in this sample—once for the AI for the mouse, and once for the AI for the tank.
Mouse AI
The goal is to make the mouse evade the cat when the mouse gets too close to the cat, and wander around the screen when it feels safe. We already know how to implement the evade and wander behaviors. Deciding which behavior for the mouse to perform, based on its distance from the cat, seems simple enough. In psuedocode, it looks like the following:
C# |
---|
if (distanceFromCat > MouseEvadeDistance ) { wander } else { evade } |
If the distance from the cat is near the threshold, however, the mouse "flickers" between wander and evade. We have to use a hysteresis constant.
C# |
---|
float distanceFromCat = Vector2.Distance(mousePosition, catPosition); if (distanceFromCat > MouseEvadeDistance + MouseHysteresis) { mouseState = MouseAiState.Wander; } else if (distanceFromCat < MouseEvadeDistance - MouseHysteresis) { mouseState = MouseAiState.Evading; } |
In order to implement the "keep doing what you were doing before" part of hysteresis, we keep track of what the mouse is currently doing by using an enumeration called MouseAiState. We use a variable called mouseState, of type MouseAiState, and to change what the mouse is currently doing, we can change the value of mouseState. The end result is that the mouse behavior follows a pattern something like the following:

As the distance from the cat increases, the mouse moves from the evade state to the wander state. If the distance from the cat is in the orange region marked "no change," the mouse continues doing whatever it was last doing.
Now that we have decided the states that the mouse should be in, all that's left is to implement the states. The techniques that are described in this document make this work straightforward.
Tank AI
Our goal is to make the tank chase the cat. If the tank gets close enough to the cat, it considers the chase finished, and stops. If the cat gets too far away, the tank gives up the chase, and wanders around. This gives us a total of three different things the tank do—it can wander, it can chase, or it can stop.
The mouse AI had only two states involved in its decision making process—evade and wander. This made the code fairly simple—just an if-else. Unfortunately, this doesn't work for the tank AI. We need to use a different implementation of hysteresis. The tank AI follows a pattern like the following:

In this diagram, there are the three states that we expect to see—caught, chase, and wander. What is interesting is the regions in the middle. This is another way of implementing hysteresis. In this method, the thresholds for changing state move, depending on the current state. For example, if the tank is in the caught region, it stays in the caught stage until the distance from the cat is greater than TankCaughtDistance + TankHysteresis. However, after the tank switches to the chase stage, the caught/chase threshold moves to the left, and the tank is more likely to stay in the chase stage. This lets us account for small fluctuations, as in the hysteresis method described above, but it also allows for slightly more complicated behavior.
Extending the Sample
Try making the cat behave on its own, instead of being controlled by the player. You might also try making the cat chase the mouse and evade the tank. The cat could decide which behavior is more important, based on its distance from each of the two other actors.