Photo by adselwood.
HP. Lives. Mans. Credits. 1-UPs. All ways of giving the player an extra chance to play, rather than ending the game after a single mistake. These are also ways of rewarding the player for obtaining a certain number of points, reaching a particularly tricky spot, or inserting another coin.
As before, FrozenHaddock was way ahead of me in adding this feature to his game. Let’s play catch-up, and look at different ways of measuring health.
Click the image below to try the game out:
Click to try it out!
As usual, if you followed the AS3 Avoider Game Tutorial you should be able to fit this into your game, even if you’ve made improvements. I’m going to base this tutorial on the game you end up with after completing the base tutorial. If you didn’t follow the tutorial, you can follow along with me using the files in this zip file, here.
Whatever you’re using, open the FLA, and let’s get started.
###Three Strikes, You’re Out
So at the minute, the game goes straight to the game over screen as soon as the avatar hits an enemy. A simple lives system changes this so that, when the avatar hits an enemy:
– If the player has at least one life left, he loses one life
– Otherwise, Game Over.
Wording it like this makes it fairly obvious what we’ve got to change. Open *AvoiderGame.as*, and find the piece of code that deals with an enemy hitting the avatar:
if ( avatarHasBeenHit )
{
bgmSoundChannel.stop();
dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}
Based on our simple pair of rules (above), it’s easy to rewrite this:
if ( avatarHasBeenHit )
{
if ( playerLives > 0 )
{
playerLives = playerLives - 1;
}
else
{
bgmSoundChannel.stop();
dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}
}
Of course, we’ll need to create this new variable playerLives
. Make it a class-wide variable of type int
. (An int
is just a whole number, i.e. it has no fractions and no decimal point.)
public class AvoiderGame extends MovieClip
{
public var playerLives:int;
We need to give this a value, so do that in the constructor:
public function AvoiderGame()
{
playerLives = 3;
I’ve picked three lives — for some reason, this is a fairly common number. Test this change and you’ll find that when you touch an enemy, whoops, the game freezes.
[flash http://gamedev.michaeljameswilliams.com/structure/AgtLives/SWFs/demo1.swf w=400 h=300 preview={http://gamedev.michaeljameswilliams.com/structure/AgtLives/Images/Snapshot_O01.png} mode=3]
That’s my fault; I forgot we stopped the tick timer as soon as the avatar touched an enemy, rather than when we checked what to do. To fix this, we just need to move the line gameTimer.stop();
from here:
if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) )
{
gameTimer.stop();
avatarHasBeenHit = true;
}
…to the piece of code we were editing earlier:
if ( avatarHasBeenHit )
{
if ( playerLives > 0 )
{
playerLives = playerLives - 1;
}
else
{
gameTimer.stop();
bgmSoundChannel.stop();
dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}
}
If we test this out now, we’ll find that, er… it doesn’t seem to work. We get Game Over as soon as we touch an enemy:
[flash http://gamedev.michaeljameswilliams.com/structure/AgtLives/SWFs/demo2.swf w=400 h=300 preview={http://gamedev.michaeljameswilliams.com/structure/AgtLives/Images/Snapshot_O02.png} mode=3]
What’s going on? We need a better look. It’s time for a visual approach.
###Counter, Our Old Friend
Remember the Counter
class? Both the Clock
and Score
classes extend it to keep track of their internal numbers. Let’s create a lives counter.
So, first create a new symbol of type Movie Clip. Call it Lives and export it for ActionScript. Put a dynamic text field inside, with an instance name of livesDisplay. You can decorate it as well, if you like:
Now create a new AS file, and save it as *Lives.as* in your *Classes* folder. The code is almost identical to that for the *Score* class, so I’ll just paste it here:
package
{
import flash.text.TextField;
public class Lives extends Counter
{
public var livesDisplay:TextField;
public function Lives()
{
super();
}
override public function updateDisplay():void
{
super.updateDisplay();
livesDisplay.text = currentValue.toString();
}
}
}
There’s the basic code, now we have to connect this to the play screen. Open the *PlayScreen* symbol and drag in an instance of *Lives* from the Library.
Give this an instance name of gameLives
.
Now, because we turned off “Automatically declare stage instances” back in Part 12, we need to declare this object as a class-wide variable in *AvoiderGame.as*:
public class AvoiderGame extends MovieClip
{
public var gameLives:Lives;
…and now we can tie this in to our existing code, based around playerLives
, without much of a change:
public function AvoiderGame()
{
playerLives = 3;
gameLives.setValue( playerLives );
if ( avatarHasBeenHit )
{
if ( playerLives > 0 )
{
playerLives = playerLives - 1;
gameLives.setValue( playerLives );
}
else
{
gameTimer.stop();
bgmSoundChannel.stop();
dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}
}
Notice here that we’re not removing the playerLives
variable and replacing it with this new gameLives
, we’re merely using gameLives
to *represent* the playerLives
value. Why? Well, it lets us separate the internal values with the external display of those values. If we wanted, we could add a second display object to show the number of lives in a different way. It’s up to you, as the programmer, to decide whether this is worth it.
Anyway, run the game:
[flash http://gamedev.michaeljameswilliams.com/structure/AgtLives/SWFs/demo3.swf w=400 h=300 preview={http://gamedev.michaeljameswilliams.com/structure/AgtLives/Images/Snapshot_O03.png} mode=3]
We certainly *start* with three lives, but as soon as we hit an enemy this very rapidly counts down to zero. Let’s experiment by setting the initial number of lives to 99 (do this in the *AvoiderGame* constructor function):
[flash http://gamedev.michaeljameswilliams.com/structure/AgtLives/SWFs/demo4.swf w=400 h=300 preview={http://gamedev.michaeljameswilliams.com/structure/AgtLives/Images/Snapshot_O04.png } mode=3]
Now it’s easy to see the source of the problem (apologies to anyone reading that understood this several paragraphs ago π ). The player doesn’t lose a life just at the point they touch an enemy; they lose a life for every tick during which they are touching an enemy!
We could leave the game like this, change the display from a number to a health bar, and let the number of “lives” (HP, really) increase every tick the player *wasn’t* touching an enemy. Feel free to do that, if it suits your game. However, I’m going to go with the FrozenHaddock design of making the enemy disappear once it touches the avatar, taking one of the player’s lives with it.
###An Eye For An Eye
This isn’t actually going to require much of a change to our code. Take a look through and see if you can figure out what’s going to need to change before reading on.
The answer lies in this part:
while ( i > -1 )
{
enemy = army[i];
enemy.moveABit();
if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) )
{
avatarHasBeenHit = true;
}
if ( enemy.y > 350 )
{
removeChild( enemy );
army.splice( i, 1 );
}
i = i - 1;
}
All we need to do is remove the enemy at the point it hits the avatar. Our existing code will take care of the rest. The simplest way of doing this is like so:
while ( i > -1 )
{
enemy = army[i];
enemy.moveABit();
if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) )
{
avatarHasBeenHit = true;
removeChild( enemy );
army.splice( i, 1 );
}
if ( enemy.y > 350 )
{
removeChild( enemy );
army.splice( i, 1 );
}
i = i - 1;
}
The new lines here are 174-175. Hopefully you’re biting your lip at the sight of duplicate code at lines 179-180 — if so, you’re right to whimper. Really, we should avoid such duplication. I shall leave it up to you to clean up this small mess I have created π
Time to test!
[flash http://gamedev.michaeljameswilliams.com/structure/AgtLives/SWFs/demo5.swf w=400 h=300 preview={http://gamedev.michaeljameswilliams.com/structure/AgtLives/Images/Snapshot_O06.png} mode=3]
Oops, I forgot to change the number of lives back to three. Well, never mind; it actually plays pretty well like this, I think. As always, you should adjust this to fit your game.
###Challenges
You won’t be surprised to hear me suggest adding sound effects to accompany a lost life. And I already mentioned the idea of changing the lives counter to a health bar, like in beat-em-up games. What else can we do with this?
Well, for a start, you could combine lives AND a health bar: run out of HP and you lose a life. HP automatically regenerates; lives don’t. Frantic uses a system like this.
We don’t have any way of rewarding the player with an extra life right now. You could do this at the start of each level, or when the player gets a certain number of points. Alternatively, how about mixing this with the Collectibles tutorial, and creating an item that gives the player a 1-UP? Similarly, you could add a “shield” power-up that stops the player taking damage for a while.
Speaking of other tutorials, Mushyrulez’s Enemies tutorial is perfect for this. Perhaps the bigger enemies could do more damage, taking two or three lives at a time, while the smaller enemies actually regenerate your health?
Let me know what you come up with π
###Wrapping Up
You can download the zip of all the files from this tutorial here.
Hey, I managed to get all the way through that without making a lame “get a life” joke. I think that’s pretty impressive.
{ 17 comments… read them below or add one }
Great. I made it to 11720. But hey, remember to embeded the fonts, mine became times new roman.
Other challenges could be:
– Add an animation to the enemies when they disappear.
– Make the ‘sheild’ active on the player when he is losing one life, and make a blinking animation on him.
D’oh, school boy error on my part π
Nice suggestions!
Thank you, its really awesome, and your right 100 Lives is perfect π
Do i actually HAVE to do that garbage collecting in part 12 to make it work?
Anoyingly, it dont show how many lives i got. And when i put that new var in, it says duplicate…something.
Do i have to do that?
That “duplicate” error is probably due to having “automatically declare stage variables” unchecked, rather than the actual garbage collection. If you haven’t checked that, then removing the
public var gameLives:Lives;
line should get rid of that error.Fixed:) Thank you. Wow, you know Michael, just following those 12 tutorials, i have learned a lot of AS3! I had like 80% more errors that i asked you for help about, but i solved them all by myself and how? – Remembering what you told us! Your tutorials are GREAT, seriously if there was Tutorial awards, my vote would definently go to you! π
Good good — and thanks π
Have you read my series on debugging?
I have π And i laughed so much at that
Sometimes itβs best to give your brain time to figure things out on its own, without your interference. So if it feels like youβve been BANGING YOUR HEAD TO A BRICK WALL and getting nowhere solving your problem
Funny because its so true! same as this;
What a lot of new programmers do here is sit and stare at the above lines of code, trying to figure out just where the heck a problem could be occurring, and getting annoyed because it all looks fine.
The up is what i did :<
Hopefully not any more! π
Is this regarding visual basics?
Actually it’s about ActionScript 3.0, a scripting language for Flash.
I’ve completed this, but I’m not sure what’s wrong…
I’ve commented out stuff and tested what stuff was before the tutorial to figure out that the location of the problem is the setValue part.
What happens is it compiles the file without any errors or warnings, but then when I click the start button I get the following in the output:
But if it’s commented out, it works fine (don’t know about the parts in the tutorial after that though).
I’ve checked your .fla and as far as I can tell, everythings the same. I’ve checked: instance name, Lives properties, Lives.as, and the stuff in AvoiderGame.as.
While I do have added features, none should effect this.
PS. I do know the AvoiderGame.as is run properly until it gets to that one line. It doesn’t do anything after (used a trace).
Hey Graham,
Looks like
gameLives
isn’t set to anything, for some reason. (I.e. you’re missing a line where you type, for instancegameLives = new Lives()
or whatever.Does that help?
I’m not sure what I changed (I went to the past version and redid the tutorial), but I fixed it.
My next goal is to make HP, which is separate from lives. Then, see if I can add in HP for the enemies, and speed up the shooting (I added in the shooting feature that you have the tutorial for).
Oh OK, cool π
HP, good idea! Let me know how that goes.
Hi. My lives bar doesnt work, and i have no lives like before. QWhat is the problem?
}
nice tutorial, but something is wrong i think,or i didnt understan some of the steps…xD
{ 1 trackback }