Welcome Guest | Signup | Login


Best Flash Components | Flash Gallery | Flash Audio & Video | Flash Menus | User Interface | Website Templates

Tutorials

 

Tutorials >> Avoider
Posted by fflash

PART 1

The game we shall be making is shown below. You are the black circle, try and get the blue squares but avoider the white, 'enemy' circles. Here is a live example of what we will create.

obrazek

During this project, we will be building on the skills learned from our previous tutorials, such as Pong!. Specifically, we will also be adding the following to our ActionScripting arsenal:

- Using external classes and initialising them as objects

- Using these objects to hold information about themselves

- Use the mouse to move

- Removing an object fully from AS3 (harder than it sounds)


Well, let's crack on. First of all, you will need to create a new project, including one .fla and one .as. Call the .as file Avoider.as (it is customary to entitle classes, and thus their .as files, with a capital letter). Save them both in the same folder. Then, in the .fla, make the document class "Avoider" (with the capital letter, see for more one of our previous tutorials). This will make sure that the Avoider class constructor is loaded as soon as the swf is compiled. Good.

Now we need to actually put some code in Avoider.as. Go to it, and type:
Code: Select allpackage{
import flash.display.MovieClip;
public class Avoider extends MovieClip{
public function Avoider(){

}
}

}
This forms the bare bones of our file. You will notice it is very similar to our previous projects, and includes the MovieClip class, which we will needs, then uses that as a foundation for our new Avoider class. The other imports you will need to add are listed below:
Code: Select allimport flash.text.TextField;
import flash.display.Shape;
import flash.events.Event;
Copy and paste them into the section, under the one import we had already. The bottom two we have met before and/or are quite self explanatory. However, we don't have a textfield yet, so why do we need to have the TextField class. Go on then, create one! Go back to you .fla, add a dynamic textfield, giving it an instance name of scoretext. Yes, that will be holding our score.

While you're in you .fla, you'll need to add in the four walls. Make them quite thin, at most about 15px (or alter the later code). Give them, in clockwise order, the instance names walltop, wallright, wallbottom and wallleft. WE will be using them later for both the player and the enemies, who 'bounce' off them.

Finally for today, then, we will be adding in some variables to use later. These need to go into between the Avoider class definition line and the Avoider function definition line, and are as follows:
Code: Select allpublic var target:MovieClip = new MovieClip;
public var player:MovieClip = new MovieClip;
public var score:int = 0;
public var mouseDiffX:int = 0;
public var mouseDiffY:int = 0;
The target will become the blue square; the player is the you, the black circle; score holds, er, the score; mouseDiff X and Y will hold how far away from the mouse you are.

All this is to come of course, but I do so hope you will join me for it.

PART 2

A copy of the full source code for today's tutorial is available.

The game we shall be making is shown below. You are the black circle, try and get the blue squares but avoider the white, 'enemy' circles.Here is a live example of what we will create.

obrazek

Well, that's the slight rewrite of what I said last week out of the way (oh, the beauties of copy and paste!). This week we are going to be actually doing something. Sharp intake of breath. From last week, you should have four Movie Clips for the walls, with which we shall be interacting either this week or next. For now though, we will turn our attention to the player.

Go to your ActionScript, and inside the constructor - the Avoider() function - call a new function, which we are about to create, called drawPlayer(). In case you don't know how to do that (and you really should by now), it's like this:
Code: Select alldrawPlayer();
There. Now, of course, we have to make a function called drawPlayer. We does this in the standard way, which is all explained in another tutorial:
Code: Select allpublic function drawPlayer() {

}
Now we need to draw the player within that function. For this we are going to be using that Movie Clip we made last week called 'player', just adding some graphic details. For an explanation of them, please see earlier tutorials.
Code: Select allplayer.graphics.beginFill(0x000000);
player.graphics.drawCircle(0, 0, 10)
; player.x = 200;
player.y = 200;
player.graphics.endFill();
addChild(player);
I think, perhaps, the only important thing to note from that is that the registration point of the circle (its (0,0)) is in the middle of it, not in the top left hand corner. This makes the game that little bit better when you move using the mouse in this way. If you're wondering why, look at the three parameters of the drawCircle function: x of the middle of the circle, y of the middle of the circle and radius. Compiling your movie at this point will show you (oh joy of joys!) that we now have a black circle on the stage. A black circle that doesn't move. Well, now it shall.

Go back to the constructor function and, underneath the drawPlayer function, add in this line:
Code: Select alladdEventListener(Event.ENTER_FRAME, mouseMovement);
We first mentioned Event listeners way back when so you should know about them already. As you can see, every frame, the mouseMovement frame will be called. Wait a minute! We don't have a mouseMovement function. Oh yes we do (as of now):
Code: Select allpublic function mouseMovement(event:Event):void {

mouseDiffY = mouseY - player.y;

mouseDiffX = mouseX - player.x;

}
We simply calculate the current mouseDiffs, as noted last week, but we're still not actually doing anything. THere are four more blocks of code to add UNDER those two lines, I will talk you through one and you can copy and paste the other three. Firstly though, here is a helpful diagram showing the various mouseDiffs:

obrazek

And here is that one, which handles all negative Y differences:
Code: Select allif (mouseDiffY < 0) {

if (!player.hitTestObject(walltop)) {

player.y += mouseDiffY / 6;

}

}
We also have a tutorial about the hitTestObject function as well. Basically what we say here is that "If we are below the player, and not touching the top wall, move closer to them on the y-axis."n.b. Not upwards, towards. And, finally for today, the other three:
Code: Select allif (mouseDiffY > 0) {

if (!player.hitTestObject(wallbottom)) {

player.y += mouseDiffY / 6;

}

}

if (mouseDiffX < 0) {

if (!player.hitTestObject(wallleft)) {

player.x += mouseDiffX / 6;

}

}

if (mouseDiffX > 0) {

if (!player.hitTestObject(wallright)) {

player.x += mouseDiffX / 6;

}

}
If you now compile your game (CTRL/Command + Enter) you should see that the player moves towards the mouse, which is obviously an integral part of the game.

PART 3

A copy of the full source code for today's tutorial is available.

The game we shall be making is shown below. You are the black circle, try and get the blue squares but avoider the white, 'enemy' circles.Here is a live example of what we will create.

obrazek

If you remember back to last time, I said that we were going to do targets and scoring today, and that we shall. Naturally, we shall be starting with the former - the targets. These are essentially little blue squares drawn in. Alright - they are little blue squares drawn in.

First of all, then, we're going to make sure a target gets drawnin at the beginning, by adding this line into the constructor:
Code: Select alldrawNewTarget();
and then create the drawNewTarget() function:
Code: Select allpublic function drawNewTarget() {

var posX:uint = randRange(30,520);

var posY:uint = randRange(30,370);

}
We shall of course be adding to that. For now though, we turn our attention to the two lines that I have put in the function, which are going to get done every time the function is called. It creates two variables, and assigns them values using another function, called randRange. You won't have a function called randRange so we will add it in now. I have borrowed this from Mike Nimer who probably borrowed it from someone else, and I thank them all. Here it is, plus you may want to read about function declaration if you haven't already:
Code: Select allprivate function randRange(start:Number,end:Number):Number {

return Math.floor(start + Math.random() * end - start);

}
That returns a value between start and end, or in our case 30 and 520 or 30 and 370. This essentially gives us a point somewhere inside the four walls of our playing area. We haven't done anything with it yet, so return to your drawNewTarget function and add in these lines, under the last two:
Code: Select alltarget.graphics.beginFill(0x0000FF);
target.graphics.drawRect(0, 0, 20, 20);
target.x = posX;
target.y = posY;
target.graphics.endFill();
addChild(target);
Most of the drawing was covered on this tutorial about drawing - this just draws in our blue square (lines 1 and 2), moves it to the random location we just generated (lines 3 and 4) and adds it to the stage, so we can see it (line 6). If you compile your games (CTRL/Command + Enter) now, though, and move to your target, it won't do anything, because we don't have an event listener for handling the player touching the target. No problem! It takes one more line in you drawNewTarget function(~1), and another function altogether (~2). (~1) is this, purely adding the Event Listener, which looks to a function called gotToTarget every frame:
Code: Select alltarget.addEventListener(Event.ENTER_FRAME,gotToTarget);
and that handler function is shown below:
Code: Select allprivate function gotToTarget(e:Event) {

if (e.target.hitTestObject(player)) {

e.target.x = randRange(30,520);

e.target.y = randRange(30,370);

//drawNewEnemy();

score++;

scoretext.text = "Score: " + String((score * 10));

}

}
For more information about the stuff there, read these tutorials about if statements, hittests, comments and remember that e.target accesses the thing calling the function - in this case the target - and then we move it to a new, random, location. I have only commented out the drawNewEnemy() lines because we won't be looking at that today, so if you called it while the function didn't exist (i.e. at the end of today's tutorial) it would cause an error. Note also how this cause score to be incremented (added one to) and scoretext to display the current score, just like in Pong!

And, thankfully, that's it. Yes, I know there's been a lot of code today, but we've achieved something at least: if you now compile your game (CTRL/Command + Enter) you should see that the player moves towards the mouse, and when it touches the target you get some points.

PART 4

A copy of the full source code is available. The code for the extra, enemy class is here, don't forget that either.

The game we shall be making is shown below. You are the black circle, try and get the blue squares but avoider the white, 'enemy' circles.Here is a live example of what we will create.

obrazek

Now, unfortunately, is the moment when we must focus on the hardest part of this games, in my view. Th part to which I am referring is the enemies themselves. Although they have simple movement, it will be a steep learning curve for anyone following (only) these tutorials. That's why I've broken it down for you, into a few little subheadings.

Why is it going to be so difficult?

It is going to be just a bit tricky because for the first time we shall have to introduce a second external class (.as file), import it and use it.

Why?

Here goes: each enemy has a direction - either horizontal, or vertical. Within these it either goes left or right. Now imagine you're a computer and you pick an enemy at random. How are you going to know whether to move it right or left or up or down? Okay, you could set a complicated set of event listeners which switched between one another, but there's a (comparitively) easy way: use a class for the enemy, which hold whether it's a horizontal enemy (from now one: H-Enemy) or vertical one (V-Enemy)

First things first: how do we create the enemy and draw it?

Ah, just like we did with the target and the player. Create a new function-call within the constructor (avoider()) function and call it drawNewEnemy(). NOTE: make sure the function calls are in this order: player, target, enemy! Then it's basically a cut and paste job from the target, information about which you can find if you look at the previous tutorial:
Code: Select allpublic function drawNewEnemy() {

var posX:uint = randRange(50,500);

var posY:uint = randRange(50,350);

var anenemy:Enemy = new Enemy;

anenemy.graphics.beginFill(0xFFFFFF);

anenemy.graphics.lineStyle(2,0x000000);

anenemy.graphics.drawCircle(10, 10, 10);

anenemy.x = posX;

anenemy.y = posY;

anenemy.graphics.endFill();

addChild(anenemy);

anenemy.addEventListener(Event.ENTER_FRAME,touchPlayer);

}
Note that instead of creating a new MovieClip, we're creating a new Enemy, we'll be coming to that later so don't worry. Apart from that though, it's quite seem - it will draw the circle in for us, with a black line line, thickness 2, but with white in the middle. It adds it to the display list and then gives it (i.e. every enemy) the touchPlayer enter frame handler.

I think actually.. yes, we'll do the touchPlayer function in a minute. First though, we need to create that extra class.

What information are we going to hold in our extra class?

Well, the thing we have to hold in the current direction of movement, which we'll call "mydirection". We'll also keep a backup variable for disabling the enemy quickly, which will be "hastouched". For more information about variables, see this tutorial. For now though, create a new .as file and save it as Enemy.as (with a capital E, as all classes should begin with a capital). That should tell you that we're calling our class 'Enemy', if you couldn't remember from earlier. Here is all the code you need to put in that:
Code: Select allpackage{

import flash.display.MovieClip;

public class Enemy extends MovieClip{

public var mydirection:uint = 0;//0 = down/right, 1 = up/left

public var hastouched:Boolean = false;

}

}
See how it extends MovieClip? That means it will be like a MovieClip, just with our variables as extra - this is how we managed to use the .graphics.beginFill() function earlier - it had borrowed the function from the MovieClip class.. It's really not too hard to understand if you can get your head round it - someone defined the MovieClip class in just the same way.[st]touchPlayer()[/st]The touch player function we will use for handling when an enemy touches the player -> get rid of enemies and target, redraw the target, redraw one enemy, wipe out our score, update the score textbox. For getting rid of the enemies, we will use a for loop and the removeChildAt function, which removes from the display list the child at a specified index, which is its only argument. More about that in a previous tutorial. Well, here it is:
Code: Select allprivate function touchPlayer(e:Event) {

if (e.target.hitTestObject(player) &amp;&amp; !e.target.hastouched) {

e.target.hastouched = true;

for (var i = (numChildren - 1); i > 5; i--) {

getChildAt(i).removeEventListener(Event.ENTER_FRAME,touchPlayer);

removeChildAt(i);

}

e.target.removeEventListener(Event.ENTER_FRAME,touchPlayer);

score = 0;

scoretext.text = "Score: 0";

drawNewTarget();

drawNewEnemy();

}
}
Yeah, so we do all of that stuff which I listed above. Good, eh?

Moving the enemy around

We're one the home stretch now - all we have left to do is attach the event listeners for move left and right, utilising the mydirection variable we created earlier. We're going to do this right after the enemy is created, while we can de facto affect all enemies. Go back to the drawNewEnemy() function then, and add, to the end:
Code: Select allif (randRange(0,100) > 50) {

anenemy.addEventListener(Event.ENTER_FRAME,moveVertical);

} else {

anenemy.addEventListener(Event.ENTER_FRAME,moveHorizontal);

}
In effect we use the randRange function to flip a coin. The only reason I have used 100 and not 10, say, is because the larger the number used, the less effect the slight inaccuracies of 'random' numbers have. We've introduced two functions, moveVertical and moveHorizontal - for V-Enemies and H-Enemies respectively. These are, again, self explanatory - just see the if tutorial and the Hit-Testing tutorial. And here it is:
Code: Select allprivate function moveVertical(e:Event) {

if (e.target.mydirection == 0) {

e.target.y += 4;

} else if (e.target.mydirection == 1) {

e.target.y -=4;

}

if (e.target.hitTestObject(wallbottom)) {

e.target.mydirection = 1;

}

if (e.target.hitTestObject(walltop)) {

e.target.mydirection = 0;

}

}

private function moveHorizontal(e:Event) {

if (e.target.mydirection == 0) {

e.target.x += 4;

} else if (e.target.mydirection == 1) {

e.target.x -=4;

}

if (e.target.hitTestObject(wallright)) {

e.target.mydirection = 1;

}

if (e.target.hitTestObject(wallleft)) {

e.target.mydirection = 0;

}

}
Done
And, thankfully, that's it. I mean, that's it. Done. Finished. Finito. Whatever will I write a tutorial about next week? If you now compile your game (CTRL/Command + Enter) you should see that it all works.