Hiya all, I’ve started writing up a tut on creating a basic game engine for all those struggling - so here it is…!
Creating a basic game engine using classes in AS2.0/3.0:
This tutorial is for AS2.0 but can be adapted to 3.0 with a little shuffling and a couple of changes!
I’ve been using the Kirupa forums for a while now and one of the things I see the most is people following tutorials for simple games. While these tutorials are great for learning the basics of AS and enable a simple game to be created quickly and easily they are usually inflexible, convoluted and contain bad coding habits. One of my pet hates is the ‘string to variable name’ thing that Flash allows you to do so readily.
This tutorial covers getting a more flexible game engine working in Flash which will allow you to do more! It takes a little longer to get the ‘basics’ working but it’s worth it!
First of all I’ll just do a little crash course on classes. I see a lot of people using the procedural approach to programming getting their code up and running fine, but then stumbling when it comes to changing something fundamental or adding more functionality because their code is disorganised.
We will aim to organise things as best as possible!
First of all, what is a class?
1. Classes, a crash course
A class is basically a collection of methods (functions) and properties (variables) which form an ‘object’.
A class can be instantiated, which means a ‘copy’ of that object will be created in memory for you to manipulate. You can have as many instances of the same class as you want, and the properties of each instance will be unique to that instance.
For example, imagine you have 2 apples - they are both apples, but one may be slightly redder, one may be slightly bigger or sweeter. This is akin to having 2 instances of the same class but with different properties.
Properties can be either public or private…
Public properties can be accessed from anywhere
Private properties can only be accessed from inside the class
I wont go into detail on private/publics as there’s no good reason I would use private variables in a game project (as it’s all my code and I know what I should/shouldn’t be accessing/altering)
A class is defined in AS as follows:
class myClass {
// Constructor
function myClass() {
}
}
Simple. A class file must be saved with the same name as the class and placed into the same directory as the .FLA file to be included in the project. This class filename would be “myClass.as”.
Notice the definition of one method myClass(). This method is called the ‘constructor’ and it must be named the same as the class. This function will be called every time a new instance of myClass is created. This is useful for initialising your objects upon creation.
Now to add to myClass - let’s add some properties and another method.
class myClass {
var number1:Number;
var number2:Number;
// Constructor
function myClass() {
number1 = 5;
number2 = 10;
}
function ShowNumber() {
trace(number1 + number2);
}
}
I’ve added 2 properties called number1 and number2. I’ve also added a function called ShowNumber().
Now when we create a new myClass object the 2 variables number1 and number2 will be set to 5 and 10 respectively.
If we call the objects ShowNumber() method, it will output the sum of these numbers to the Flash console window.
To create the object and call the method we use the following code
var instanceOfMyClass:myClass = new myClass();
instanceOfMyClass.ShowNumber();
Now this is a great way to organise your code, but we aren’t finished yet!
Classes can do much more!
Your myClass now can be added to, but what happens when you want an object which does most of the stuff that myClass does, but maybe a little differently here and there, or completely differently in places?
You could add arguments to the functions and base the function code on a switch statement or if statements like so: (taking the ShowNumber function as an example)
function ShowNumber(method:Number) {
if(method == 0) {
trace(number1 + number2);
} else {
trace(number1 / number2);
}
}
That will work yes…but you could just create a new class
‘But why copy all that code?’ you ask…
Well, classes have a feature called ‘inheritance’ which allows you to base a new class upon an existing class, and then override or add to it’s methods!
So you could do the following
class myOtherClass extends myClass {
// Constructor
function myOtherClass() {
super();
}
}
Now if you instatiate this class - what do you notice? Yes you can call the ShowNumber method on this new class. All the properties and methods of the parent class are available. Notice the call to super() - this basically means I’m calling the constructor on the parent class - to access anything on the parent class from a subclass you just use the ‘super’ keyword. This means I don’t have to write the same initialisation code as the parents constructor can do it for me.
Now it’s possible to modify the ShowNumber() function to suit the new class
class myOtherClass extends myClass {
// Constructor
function myOtherClass() {
super();
}
function ShowNumber() {
trace(number1 / number2);
}
}
Now your myOtherClass objects will have a different ShowNumber function from your myClass objects. You can even run the ShowNumber function from the parent but modify the properties before you call it like so:
class myOtherClass extends myClass {
// Constructor
function myOtherClass() {
super();
}
function ShowNumber() {
number1 = 20;
super.ShowNumber();
}
}
So as you can see, there’s a lot of scope for intricate layering of objects. The basic idea in this tutorial is to give all your objects the most ‘basic’ functions and then build upon these by extending them with more specific levels of functionality.
A quick example in one of my games is as follows
BaseObject -> BaseSoldier -> EnemySoldier -> SoldierAK
The base object handles rendering, physics etc. The BaseSoldier handles the movement, control and some behavioural aspects. The EnemySoldier handles behaviour of an Enemy object (i.e. fires at good guys, can be hit by good guys etc) and finally the SoldierAK describes how a Soldier with an AK-47 rifle fires his gun and what he fires.
This means that I can have a number of different types of soldier running about with minimal code needed to add a new one. I can simply create a new SoldierXXX (XXX being the weapon) class and define how that soldier fires this new weapon. His movement, physics and rendering are handled by the parent classes.
2. Creating the game objects
Now we know how to create a class and what they can be useful for, we can start to create our game engine. First of all we need to think about what needs to happen every Flash ‘frame’ to keep our game in motion.
We need some way of controlling multiple objects that may be on screen simultaneously, keeping track of their animations and updating their positions/collisions/behaviour etc!
Seems like a lot to look after in one go…this is why creating a ‘Base’ class can be a good idea. Basically we will be creating a generic ‘Base’ class that the game engine will take care of for us. This class will be responsible for the basics in showing an object on the screen - pretty much everything that will interact with the game world will be derived from this Base class.
We will also need an Array to hold the list of objects so that we can keep track of them.
First of all let’s define our base object
Create a new .as file called BaseObject.as in your project directory (you did make one right?!)
Add the following code
class BaseObject {
// Ok so here are some useful vars
var parent:BaseObject = null; // Holds information on the 'parent' object - this allows you to create heirarchys of objects and means you can have objects which are 'attached' to others. Useful for multipart objects like tanks with turrets etc
var oList:ObjectList = null; // This is a reference to an instance of a class we are going to create next - the ObjectList. This will be the class responsible for handling our list of objects
var clip:MovieClip = null; // The movieclip to use for display purposes - you could use bitmap rendering, but for the sake of simplicity I'll use this for the tut
var _x:Number = 0; // x/y position of the object
var _y:Number = 0;
var velx:Number = 0; // Velocity of object
var vely:Number = 0;
// Initialise the object
function BaseObject(_oList:ObjectList, clipName:String, _parent:BaseObject) {
oList = _oList;
parent = _parent;
SetMovie(clipName);
oList.push(this); // Add this to the object list
}
// Attaches the movieclip which will represent this object - attaches to _root if parent is not specified
function SetMovie(clipName:String) {
if(parent == null) {
clip = oList.root.attachMovie(clipName, clipName + oList.root.getNextHighestDepth(), oList.root.getNextHighestDepth());
} else {
clip = parent.clip.attachMovie(clipName, clipName + parent.clip.getNextHighestDepth(), parent.clip.getNextHighestDepth());
}
clip.stop(); // Stop the animations from playing
Render(); // Call render to set the MC to the correct place/size/etc on screen - should cut any flickering
}
// Behaviour function, does nothing on base
function Behaviour() {
// No behaviour for the base
}
// Physics, set the x/y position += the velocity
function Physics() {
_x += velx;
_y += vely;
}
// Put the MC in the correct place on screen
function Render() {
clip._x = _x;
clip._y = _y;
}
// Function to remove this object from the game
function Remove() {
oList.RemoveObject(this);
}
// Clean up, remove movie clips etc
function CleanUp() {
clip.removeMovieClip();
}
}
So now we have our basics for our game object - let’s create the ObjectList class and see how these two fit together
class ObjectList extends Array {
// The object list is just an 'array' with some extra functionality
var rList:Array; // This will hold a list of objects to remove from the game, the reason we use this list will become apparent (or maybe not, but I'll tell you anyway!)
var root:MovieClip; // Just holds a reference to the _root so that you dont need to use _root in your game in case you want to put a preloader on it
// Init etc..!
function ObjectList(_rootRef:MovieClip) {
root = _rootRef;
rList = new Array();
}
// Simply pushes the object onto the removal list
function RemoveObject(obj:BaseObject) {
rList.push(obj);
}
// Gets the array index for the specified object
function GetIndex(o:BaseObject):Number {
var i:Number;
for (i = 0; i < this.length; i++) {
if(this* == o) {
return i;
}
}
}
// This runs through the list of objects which are set to be removed and gets rid of them by running their cleanup() function to remove the movieclips from the game and then splicing them out of the array
function CleanUp() {
var SpliceIndex:Number;
for(var i = 0; i < rList.length; i++) {
SpliceIndex = GetIndex(rList*);
this[SpliceIndex].CleanUp();
this.splice(SpliceIndex, 1);
}
// EDIT: Added this to clear out the rList array or it just grew and grew and things got slow :(
rList = new Array();
}
// Loop through all game objects and run their update functions
function MainLoop() {
for(var i = 0; i < this.length; i++) {
this*.Behaviour();
this*.Physics();
this*.Render();
}
CleanUp(); // Remove any items that are marked for deletion
}
}
So now you have these two classes, you should be able to start up Flash and get the basics of your engine on screen…
So…open Flash and go make a cuppa while it starts
Now create a new Flash project and set the framerate to 30 (or 31 if you are old skool :P)
Create a new movieclip symbol - anything will do, draw a circle maybe or some lines…save it and add it to the library with a linkage identifier of ‘TestObject’
On your main timeline let’s add the following code:
EDIT: Fixed missing reference to root in ObjectList below
var oList:ObjectList = new ObjectList(this);
var test:BaseObject = new BaseObject(oList, "TestObject", null);
test._x = 200;
test._y = 200;
onEnterFrame = function() {
oList.MainLoop();
}
Now you should see your symbol appear on the screen in the specified place (200, 200).
Your game engine is now up and running!
3. Animation
Now we have our base object - let’s add a bit more to baseobject to allow animation. You could just use the movieclips in flash and call gotoAndPlay() on clips, but we really need to link the animations to an object and we also want a bit more control. At the moment the movieclip that BaseObject is using for display purposes will just run in a constant animation loop. We can add our own custom animation system using classes so that we have a lot more control over which animations get played, and the speed at which they play. Our animation will be based on keyframes - you will be creating all your frames of animation for an object in one movieclip - no nested animations should be in there (unless you want a subclip that just loops over and over which can be useful). I use this animation system because it’s a little more flexible and extensible than the regular flash animation system. With this system you can extend it to add your own event system which is very useful.
A good thing about this is that you can use any movieclip symbol to represent any object in the game.
So let’s create our animation class:
class Animation {
// Vars
var animName:String;
var frames:Array;
var delays:Array;
var animTag:String;
// Constructor
function Animation(newName:String) {
animName = newName;
}
// Add frames to this animation
function addFrames(frms:Array) {
frames = frms;
}
// Add delays to this animation
function addDelays(dels:Array) {
delays = dels;
}
// Tag this animation - will come back to this later! :)
function tag(tag:String) {
animTag = tag;
}
// returns the next frame for the animation
function getNextFrame(currentFrame:Number):Number {
// if the current frame is greater than the array length then we need to check the last frame
if(currentFrame + 1 == frames.length) {
return 0;
} else {
return currentFrame + 1;
}
}
// Some getters!
function getFrame(currentFrame:Number):Number {
return frames[currentFrame];
}
function getDelay(currentFrame:Number):Number {
return delays[currentFrame] - 1;
}
}
Ok so we have an animation class now which is basically just an object which stores a list of frames and a list of corresponding delays for those frames. Now that we have this class we can use it to decide which frames our game objects should show.
We now need to add some bits to the BaseObject class to make sure it can utilise the animation class…
Add the following code
// variables
var anims:Array; // An array of animation objects
var currentFrame:Number; // Holds the index of the current animation frame
var currentDelay:Number; // Holds the amount of time left before we get the next frame
// in the constructor add
anims = new Array(); // to initialise the anims array
// Add the new 'animate' function - this function just uses the animation class to find out which
// frame we should be displaying from the movieclip
function Animate() {
// Check if we need to get the next frame, if not just decrement the delay
if(currentAnim != null) {
if (currentDelay == 0) {
currentFrame = currentAnim.getNextFrame(currentFrame);
currentDelay = currentAnim.getDelay(currentFrame);
clip.gotoAndStop(currentAnim.getFrame(currentFrame));
trace(currentAnim.getFrame(currentFrame));
// If the delay is negative then quit this animation
if(currentDelay < 0) {
StopAnimation();
}
} else {
currentDelay -= 1;
}
}
}
// Add this function - it just stops the animation thats currently playing
function StopAnimation() {
currentFrame = 0;
currentDelay = 0;
currentAnim = null;
}
// Add this setanimation function
function SetAnimation(searchName:String) {
// Find animation by name in anim list does nothing if it can't find one
var i:Number;
for(i = 0; i < anims.length; i++) {
if (anims*.animName == searchName) {
currentAnim = anims*;
}
}
currentDelay = 0;
currentFrame = 0;
}
// Add this randomanimation function
function RandomAnimation(searchTag:String) {
// Find animation by tag in anim list and choose a random one
var i:Number = 0;
var targetAnims:Array;
targetAnims = new Array();
for(i = 0; i < anims.length; i++) {
if (anims*.animTag == searchTag) {
targetAnims.push(anims*);
}
}
currentAnim = targetAnims[random(targetAnims.length)];
currentDelay = 0;
currentFrame = 0;
}
// Add this code to the Physics() function
Animate();
Now we have several new functions and some bits and pieces which will allow our objects to be animated. We have added this code to the BaseObject class because most likely all game objects will require animation so it’s a pretty fundamental process that needs to go in at the lowest level. The Animate() function chooses the next frame for the movieclip based on the animations you have added to the object.
So now we will add some extra frames to our library symbol - open it up and add a few more keyframes - in the keyframes move the object about a bit or change it - animate it in your own personal style if you like. I’m going to draw a pentagon shape and rotate it by 14 degrees in each frame (it will look like it’s completely rotating 360 degreed when these 5 frames play in order)
Now that the symbol has some frames let’s create a new class extending the base class and give it some animations so we can see the code in action.
To add an animation use the following code
var anim:Animation = new Animation("AnimName"); // Create a new animation object called "AnimName"
anim.tag("Tag1"); // Tag this animation - optional, I'll explain what this is for in a second!
anim.addFrames(new Array(1,2,3,4,5)); // Add the frames - this animation will use frames 1 - 5 in the movieclip
anim.addDelays(new Array(1,1,1,1,1)); // Add the frame delays - this will change frame every
someObject.anims.push(anim); // Add the animation to an object
So if we create a new class we can add some animations directly to this new class instead of modifying the base
class animatedClass extends BaseObject {
function animatedClass(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
var anim:Animation = new Animation("Test1");
anim.addFrames(new Array(1,2,3,4,5));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
var anim:Animation = new Animation("Test2");
anim.addFrames(new Array(5,4,3,2,1));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
}
function Behaviour() {
super.Behaviour();
if(Key.isDown(Key.UP)) {
SetAnimation("Test1");
} else if(Key.isDown(Key.DOWN)) {
SetAnimation("Test2");
}
}
}
Remember to save this as animatedClass.as in your source directory and that’s it! I’ve added some extra code in the Behaviour function for this class - this is just a quick hack to enable you to mess with the animations.
Change the instantiation code in the root timeline to
var test:animatedClass = new animatedClass(oList, "TestObject", null);
Run the file and press the up or down arrow to change the animation.
See how flexible classes can be now?
Now have a play about with the frame delays - there are 2 options
A delay of > 0 gives the current animation frame a delay of the specified number of ‘Flash’ frames (or physics frames if you seperate the physics/rendering which we will do later!)
A delay of <= 0 means the current animation will end and currentAnim will be set to NULL on the base
You are probably wondering what the tag() function does: the tag function is a way of ‘grouping’ animations under ‘tags’. When you create an animation you can give it a tag using the tag function - tagging multiple animations with the same tag forms a group. Now you can play a random animation from that group of animations by using the RandomAnimation() function on the BaseObject. Imagine a set of death animations for a soldier - maybe falling over backwards, clutching his chest when he gets shot, etc - you might want to play a random one of these animations when a soldier dies - with the tags you can do that.
4. Adding a player class
Next we will add a class which can handle keyboard input. Create a new class called PlayerInput and give it the following code:
class PlayerInput extends BaseObject {
function PlayerInput(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
}
function Behaviour() {
if(Key.isDown(Key.UP)) {
MoveUp();
} else if(Key.isDown(Key.DOWN)) {
MoveDown();
} else {
UpDownReleased();
}
if(Key.isDown(Key.LEFT)) {
MoveLeft();
} else if(Key.isDown(Key.RIGHT)) {
MoveRight();
} else {
LeftRightReleased();
}
}
function UpDownReleased() {
vely = 0;
}
function MoveUp() {
vely = -5;
}
function MoveDown() {
vely = 5;
}
function LeftRightReleased() {
velx = 0;
}
function MoveLeft() {
velx = -5;
}
function MoveRight() {
velx = 5;
}
}
This is your player class. Save it as PlayerInput.as in your source dir. (You could use interfaces for the movement but I don’t really find it advantageous in the situation)
Change the instantiation code on the root to
var test:PlayerInput = new PlayerInput(oList, "TestObject", null);
So now you have a static object which moves about. How about we put some animations on this object while it moves! Let’s create another class to extend this one
class AnimatedPlayer extends PlayerInput {
function AnimatedPlayer(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
var anim:Animation = new Animation("Test1");
anim.addFrames(new Array(1,2,3,4,5));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
var anim:Animation = new Animation("Test2");
anim.addFrames(new Array(5,4,3,2,1));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
}
function LeftRightReleased() {
super.LeftRightReleased();
StopAnimation();
}
function MoveLeft() {
super.MoveLeft();
if(currentAnim.animName != "Test2") {
SetAnimation("Test2");
}
}
function MoveRight() {
super.MoveRight();
if(currentAnim.animName != "Test1") {
SetAnimation("Test1");
}
}
}
Save this as AnimatedPlayer.as and make the change in the root timeline to the instantiation code again…
var test:AnimatedPlayer = new AnimatedPlayer(oList, "TestObject", null);
So now you have an object that animates when you move left and right. See how the classes are built up in layers, this adds a lot of flexibility and means you can concentrate on writing behavioural code rather than copying/pasting code or rewriting code which is already in your project.
The code in BaseObject handles all the physics, animation and rendering, the PlayerInput class handles player input and the AnimatedPlayer class handles behaviour.
Now looking at the structur of the classes can you see any obvious flaws?
I’d say that the PlayerInput class is probably a bad place to put any behavioural code such as the actual movement code. We should be using PlayerInput only for handling key presses and mouse movement/presses - the classes that are built on top of this should really handle all the behaviour.
Let’s rename our AnimatedPlayer class to just Player and move the code for movement from the PlayerInput class into the Player class.
Your Player class should now look like this
// FIX: Error here, this class definition was still named AnimatedPlayer when it should be just Player
class Player extends PlayerInput {
function AnimatedPlayer(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
var anim:Animation = new Animation("Test1");
anim.addFrames(new Array(1,2,3,4,5));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
var anim:Animation = new Animation("Test2");
anim.addFrames(new Array(5,4,3,2,1));
anim.addDelays(new Array(1,1,1,1,1));
anims.push(anim);
}
function UpDownReleased() {
vely = 0;
}
function MoveUp() {
vely = -5;
}
function MoveDown() {
vely = 5;
}
function LeftRightReleased() {
velx = 0;
StopAnimation();
}
function MoveLeft() {
velx = -5;
if(currentAnim.animName != "Test2") {
SetAnimation("Test2");
}
}
function MoveRight() {
velx = 5;
if(currentAnim.animName != "Test1") {
SetAnimation("Test1");
}
}
}
and PlayerInput should now be
class PlayerInput extends BaseObject {
function PlayerInput(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
}
function Behaviour() {
if(Key.isDown(Key.UP)) {
MoveUp();
} else if(Key.isDown(Key.DOWN)) {
MoveDown();
} else {
UpDownReleased();
}
if(Key.isDown(Key.LEFT)) {
MoveLeft();
} else if(Key.isDown(Key.RIGHT)) {
MoveRight();
} else {
LeftRightReleased();
}
}
function UpDownReleased() {
}
function MoveUp() {
}
function MoveDown() {
}
function LeftRightReleased() {
}
function MoveLeft() {
}
function MoveRight() {
}
}
Now your code is a little more structured - you can add handling code in PlayerInput and more behavioural code in Player. Also remember if you decide to make any changes to the MoveXXX() functions in PlayerInput and you also want the Player class or any other classes to follow this behaviour you need to add a call to the parent classes function in your child class…
So for instance in Player:
function MoveUp() {
super.MoveUp(); // Add this to tell the class to also run the code that's in the parent classes MoveUp() function
vely = -5;
}
5. Shooting
So now that we have a moveable player class and some animation, let’s start crafting it all into a little exercise and create a space invaders clone.
First of all we need to add the handling of the ‘shooting’ button into the PlayerInput class
Change the behaviour code and add a ButtonPressed function in PlayerInput:
function Behaviour() {
if(Key.isDown(Key.UP)) {
MoveUp();
} else if(Key.isDown(Key.DOWN)) {
MoveDown();
} else {
UpDownReleased();
}
if(Key.isDown(Key.LEFT)) {
MoveLeft();
} else if(Key.isDown(Key.RIGHT)) {
MoveRight();
} else {
LeftRightReleased();
}
if(Key.isDown(Key.SPACE)) {
ButtonPressed();
}
}
function ButtonPressed() {
}
Now we need to create an object that can be ‘shot’ at something else.
Let’s start by creating a Projectile class which we will use for shooting about the place
class Projectile extends BaseObject {
var power:Number;
var LifeTimer:Number;
var owner:BaseObject;
function Projectile(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
LifeTimer = 50;
}
function Behaviour() {
super.Behaviour();
if(LifeTimer > 0) {
LifeTimer--;
} else {
Remove();
}
}
}
This bullet code contains some behaviour which removes it after 50 frames.
Ok so how do we go about shooting it or making it collide? We’ll take care of the shooting part first.
First create a small movieclip in the library which will serve as your bullet. Export for actionscript in the library and call it ‘Bullet’
Now add this code to Player
var FireTime:Number;
function Behaviour() {
super.Behaviour(); // Remember to add this or all our button handling code wont be run
// Decrement our 'firetime' counter so we can fire again when it reaches 0
if(FireTime > 0) {
FireTime--;
}
}
function ButtonPressed() {
// Are we allowed to fire?
if(FireTime == 0) {
// Create a new projectile, set it to our position, set its velocity
var p:Projectile = new Projectile(oList, "Bullet", null);
p.isEnemy = false;
p.vely = -8;
p._x = _x;
p._y = _y;
p.owner = this;
// Give us a delay of 5 frames before we can fire again
FireTime = 5;
}
}
When you press the button in game you should get a steady stream of bullets
Now onto the collision…one of the easiest ways to do this is to set up a simple bounding box or use sphere collision. Let’s use sphere collision for this basic example as it’s very simple and requires less variables/setup than box collision.
First of all we need to put a variable somewhere which holds the ‘size’ of each of our objects. I reckon we should put this on the BaseObject class as most if not all objects in the game will have some sort of size.
Add the size variable and the collision variable to BaseObject
This size variable will be the radius of your collision circle (not the diameter!)
var size:Number;
var collision:Boolean = true; // Default this to true, most objects in game will have collision
Now in your constructor function for Projectile set the size of the object (or you could do it on the firing code to make bullets with different collision box sizes depending on who fired them - i.e. big for good guys, small for bad guys to give you a chance!)
function Projectile(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
LifeTimer = 50;
size = 5;
}
So we’ve got a ‘size’ on our projectiles. Let’s make them collide with other things - for now we will put the code directly in the Projectile class
Add the CheckCollision function and modify Behaviour accordingly
function CheckCollision() {
// Loop through the object list and find all objects which we could collide with
for(var i = 0; i < oList.length; i++) {
// Don't collide with myself!
if(oList* <> this) {
// Don't let friendly bullets collide with friendlies or enemy bullets collide with enemies
if(oList* != owner) {
// Check to see if we were close enough to hit the object
// Use the formula x*x + y*y - R*R
var dx:Number, dy:Number, r:Number;
dx = _x - oList*._x;
dy = _y - oList*._y;
r = size + oList*.size;
// This formula returns a value - if the value is zero or less than zero the two circles are overlapping
if(dx * dx + dy * dy - oList*.size * oList*.size <= 0) {
// This is where we'd do some damage to the object we hit but we haven't got that far yet, so for now lets just remove the bullet
Remove();
return;
}
}
}
}
}
function Behaviour() {
super.Behaviour();
if(LifeTimer > 0) {
LifeTimer--;
} else {
Remove();
}
CheckCollision();
}
Now these bullets should collide with objects and disappear. Let’s give them something to collide against
Modify the code on the root timeline to
var oList:ObjectList = new ObjectList(this);
var test:Player = new Player(oList, "TestObject", null);
var test2:BaseObject = new BaseObject(oList, "TestObject", null);
test2.size = 30;
test2._x = 200;
test2._y = 10;
test._x = 200;
test._y = 200;
onEnterFrame = function() {
oList.MainLoop();
}
Now you have a solid object which should be absorbing the bullets you are firing at it!
Note that bullets can also collide with themselves at the moment so I’d probably not make them too big (size 50 will make them collide with each other and disappear!)
Why not add a few simple particle effects to the bullets…create a new library object called ‘Spark’ and make it a little yellow circle (or be imaginative!) make sure you link it with the ‘spark’ name
Create a new class called Particle
class Particle extends BaseObject {
var LifeTimer:Number;
function Particle(_oList:ObjectList, clipName:String, _parent:BaseObject) {
super(_oList, clipName, _parent);
LifeTimer = 50;
collision = false;
}
function Behaviour() {
super.Behaviour();
if(LifeTimer > 0) {
LifeTimer--;
} else {
Remove();
}
}
}
Now add the Kill() function to Projectile and change the call to Remove() in the collision code to Kill() instead
function Kill() {
// Create 10 particles and set them to random directions. Also set the lifetimer to a random small value
for(var i = 0; i < 10; i++) {
var p:Particle = new Particle(oList, "Spark", null);
p._x = _x;
p._y = _y;
p.velx = Math.random() * 10 - Math.random() * 10;
p.vely = Math.random() * 10 - Math.random() * 10;
p.LifeTimer = 10 + random(5);
}
// Make sure we remove this object
Remove();
}
There you go, lovely particles flying about the place!
Right - that’s got you started. I will be doing a second part to finish off this tutorial soon
::Charleh::