At the moment, the tangerines just stay at their
initial positions on the screen looking dangerous, which is rather
boring. Perhaps they could chase the bread bat instead (your younger
brother really likes this idea). The posh name for what you are doing
now is artificial intelligence (AI). You want to make it look as though
the tangerines are being controlled by an intelligent opponent who knows
where you are and is heading that way.
1. Chasing the Bread Bat
To change the way the tangerines behave, you just have to override the Update method in the KillerSprite
class. To chase the bread bat, a killer sprite first has to know where
the bat is. At the moment, this information is hidden inside the Bat class, so we need to add some code to make this information visible. The best place to put this code is the MovingSprite class; then we can get the position of any of the moving sprites on the screen, including the Cheese and the DeadlyPepper.
1.1. Using Properties to Read the Bread Bat Position
We could provide a method called GetX to read the X position of the a MovingSprite, but C# provides something called a property, which makes this much easier. Look at this code:
public float XPos
{
get
{
return x;
}
}
When placed inside the MovingSprite class, the code provides a property that lets objects read the value of x (which is the member of the sprite that holds the position). The new XPos property can be used very easily:
float breadX = Bread.XPos;
This assigns the x location of the bread to the value of breadX. This looks a lot like direct access to a member of a class, but what is actually happening is that the code inside get portion of the property is running and the value following the return
is being sent back as the result of the property. At the moment, there
is no way that the position of the bread can be changed (which is what
we want in this case). However, this would not stop programmers like
your younger brother from trying, as in the following code:
Bread.XPos = 99;
Because there is no set behavior, this fails to compile. However, you can provide such a behavior if you like by adding a set part to the property declaration, as shown here in bold:
public float XPos
{
get
{
return x;
}
set
{
x = value;
}
}
The set behavior of a property uses the key word value
as a placeholder for the value specified on the right side of the
assignment operator. So if your younger brother’s code to write to the
property was performed, the value of x would be set to 99.
Properties are quite
neat, and they can make code look simpler. I don’t use them much myself
(and neither does the Great Programmer) because we like it to be very
clear to a user of a class just when code is running within it (as
opposed to just accessing a property within it). If you provide a set
behavior in a property you should of course make sure that this
validates the incoming data before changing a value held inside the
object.
1.2. Creating the KillerSprite-Chasing AI
The code to make a killer sprite chase the bread bat is actually quite simple:
if (game.BreadBat.XPos > x)
{
x += xSpeed;
}
else
{
x -= xSpeed;
}
if (game.BreadBat.YPos > y)
{
y += ySpeed;
}
else
{
y -= ySpeed;
}
This is pure AI. These
statements are doing exactly what you would do if you were steering a
tangerine towards the bread. They work on the principle that if the
bread was to the left of you, you’d move left. If the bread was below
you, you’d move down, and so on. If this code is placed in the Update
method, the tangerines try to head towards the bread. The speed of the
tangerine has been set so that the tangerines move quite slowly, but it
definitely feels like they are chasing you. If you really want to scare
the player, you can speed the tangerines up. Figure 1
shows the situation in the game a few seconds after the tangerines have
been made to appear. This is actually quite a scary point in the game,
as the pepper is also deadly.
If you want to
make tangerines that ran away from the bread bat (to make a kind of
chasing game), you just have to reverse this behavior.
1.3. Hitting the Killer Sprite Tangerines
The only problem
with the tangerines is that we can’t really have them make the player
lose a life when they touch the bread bat. This would be very unfair
because the player would almost certainly die quickly. We get your
younger brother to test this form of the game play and see how long he
can survive and the answer is what we expected—not very long.
However, we can
arrange things so that the player suffers in other ways. Every killer
sprite that hits the player’s bat could cost them 10 points, whereas
every one they manage to get rid of by hitting it with the cheese could
earn them 10 points. This makes the killer sprite a bit like the pepper
in some respects. The final Update behavior for the KillerTangerine looks like this:
public override void Update(BreadAndCheeseGame game)
{
if (game.GetScore() > killerTriggerScore)
{
// Score has passed a threshold.
// Turn the killer sprite on and move the threshold.
isDeadly = true;
killerTriggerScore = killerTriggerScore + killerScoreStep;
}
if (isDeadly)
{
if (game.BreadBat.CheckCollision(spriteRectangle))
{
// bat has hit the Killer Sprite.
isDeadly = false;
// lose some score
game.UpdateScore(-10);
}
if (game.CheeseBall.CheckCollision(spriteRectangle))
{
// ball has hit the Killer Sprite.
isDeadly = false;
// update the score
game.UpdateScore(10);
}
if (game.BreadBat.XPos > x)
{
x += xSpeed;
}
else
{
x -= xSpeed;
}
if (game.BreadBat.YPos > y)
{
y += ySpeed;
}
else
{
y -= ySpeed;
}
}
spriteRectangle.X = (int)(x + 0.5f);
spriteRectangle.Y = (int)(y + 0.5f);
base.Update(game);
}
Note that, unlike the pepper, the tangerines are drawn and moved only when they are deadly.
The interesting thing
about this is that we have added only a few lines to the game to get the
new character, and many of the lines we have added were copied from
other methods. We could easily add other kinds of sprites and make them
appear and disappear when we want them. It would also be quite easy to
add things like "Extra Life" sprites if we wanted the game sprites that
increase the number of lives available.