Collision Detection and Game Design

This is the second step in my series of tutorial, the first being Easy Keyboard Controls and Game Design. In this tutorial I will be building on what we already have, adding asteroids and the ability to crash into them.
I’ve made a few minor changes to the original code and graphics since the last tutorial, but nothing big.

So I create a MovieClip with 3 frames and a different asteroid shape on each frame (you could have more or less if you wanted). I give the asteroids their own class since each is going to have to move independently.

public static const LARGE:String = "large";
public static const MEDIUM:String = "medium";
public static const SMALL:String = "small";

public var speedX:Number;
public var speedY:Number;
public var size:String;

public function Move():void {
        this.x += speedX;
        this.y += speedY;
        if(this.x > 550 + this.width/2) this.x = -this.width/2;
        if(this.x < -this.width/2) this.x = 550 + this.width/2;
        if(this.y > 400 + this.height/2) this.y = -this.height/2;
        if(this.y < -this.height/2) this.y = 400 + this.height/2;
}

What I’ve done here is give my Asteroid class X and Y speed properties that work with the Move() method for motion. I intend to call the Move() method for each asteroid in the MainLoop() function.

On creation (in the constructor) I want to know if this asteroid is actually a piece of a larger asteroid or not. If it is I’m going to be passing it the base asteroid that this one is breaking off of to use it’s X:Y coordinates and speed, plus to know what size to make the new asteroid.

Now back to the Main class (in the last tutorial I called this the Asteroids class but changed it to Main since I wanted an Asteroid class).

We need to keep track of our asteroids. To do that we will use an Array. We will add this to the Main constructor:

public function Main() {
        dCount = 0;

        refresh_tf.addEventListener(MouseEvent.MOUSE_UP, resetGame);

        asteroids = new Array();
        for(var i:int = 0; i<6; i++) {
                asteroids.push(new Asteroid());
                stage.addChild(asteroids[i]);
        }
}

This is just adding 6 asteroids to the stage and giving a click event to a reset button I decided to add.
dCount stands for Death Count with an obvious purpose.

The resetGame() function is just resetting all the properties to the default. I still have to make sure to remove all the asteroids before adding all new ones, but that’s it.

I moved the contents of the MainLoop() from the previous tutorial into a function called shipControl() which is now being called from the new MainLoop();

Our revised MainLoop() method is as follows:

protected function MainLoop(e:TimerEvent):void {
        for(var i in asteroids) {
                asteroids[i].Move();
        }

        if(dead){
                deathTime–;
                if(deathTime <=0){
                        dead = false;
                        invincibleTime = 150;
                        invincible = true;
                        addChild(ship_mc);
                }
        }else {
                shipControl();
                if(!invincible) {
                        collisionCheck();
                }else {
                        if(e.currentTarget.currentCount%10 == 0){
                                ship_mc.visible = (ship_mc.visible)? false: true;
                        }

                        invincibleTime–;
                        if(invincibleTime <= 0) {
                                ship_mc.visible = true;
                                invincible = false;
                        }
                }
        }
}

These nested if..else statements are here to handle death and resurrection (RIGHT ON!).

So we have our asteroids flying about the screen, but we have yet to make them destroy your ship. You should notice above the call for to a method call collisionCheck();
Here is that function now:

protected function collisionCheck():void {
        var p1:Point = new Point(ship_mc.p1.x, ship_mc.p1.y);
        p1 = ship_mc.localToGlobal(p1);
        var p2:Point = new Point(ship_mc.p2.x, ship_mc.p2.y);
        p2 = ship_mc.localToGlobal(p2);
        var p3:Point = new Point(ship_mc.p3.x, ship_mc.p3.y);
        p3 = ship_mc.localToGlobal(p3);
        var p4:Point = new Point(ship_mc.p4.x, ship_mc.p4.y);
        p4 = ship_mc.localToGlobal(p4);
        var p5:Point = new Point(ship_mc.p5.x, ship_mc.p5.y);
        p5 = ship_mc.localToGlobal(p5);
        for(var i in asteroids){
                if((asteroids[i].hitTestPoint(p1.x, p1.y, true) ||
                        asteroids[i].hitTestPoint(p2.x, p2.y, true) ||
                        asteroids[i].hitTestPoint(p3.x, p3.y, true) ||
                        asteroids[i].hitTestPoint(p4.x, p4.y, true) ||
                        asteroids[i].hitTestPoint(p5.x, p5.y, true)) &amp;&amp; !dead) {

                        destroyShip();
                        remove(i);
                }
        }
}

Each asteroid does a hitTest with each point on my ship_mc. It’s important that I’m using hitTestPoint() here because it is capable of checking based on the actual shape of the movie clip. A regular hitTest between two MovieClips will only check to see if the bounding boxes are overlapping… in our case that could still be quite 20 pixels or more away from an actual collision. This is exactly what makes realistic 2D physics so difficult in ActionScript.

Anyways, the Hit Test may be a little buggy in some cases, but hitTestPoint works pretty well for us here. After we detect a hit of course we have to destroy the ship and asteroid and set the death timer. the destroyShip() function is trivial enough, but I would like to take a quick look at a line from the remove() method:

stage.addChild(asteroids[asteroids.push(new Asteroid(asteroids[num]))-1]);

I really just need to explain how this slightly complicated jumble works. Reading this from the inner most parenthases out;

We are creating a new Asteroid object (passing it our parent asteroid that has just been destroyed).
new Asteroid(asteroids[num])

We add that Asteroid object to the end of our asteroids Array which is returning the new length of the Array.
asteroids.push(…)

We then use that new length (minus 1) to add our newest asteroid to the stage.
stage.addChild(asteroids[...-1]);

Eh, see now? Collision detection is easy! I think my next installment might be a little more interesting when I add levels, lives, and a scoring system.

… No bullets you ask? Well that’s just an extension of what I’ve already written here right? The only extra thing you have to remember is that the bullets have a limited lifespan.

If you are still confused and can’t do it on your own (if you want to learn you really aught to at least try) then you can always check out the finished source when it’s released.

The source code for the example in this tutorial can be found here: Asteroids – V2 Source

< Lesson 1: Easy Keyboard Controls and Game Design

2 Responses to “Collision Detection and Game Design”

  1. Hank says:

    Usually a ship won’t die that easy. If you would want to create an actual game with the ship, you would have to also create an algorithm for asteroids bouncing off, which is a lot of un-needed work. Instead, I say, people should just learn Box2DFlash or any other physics engine for Flash.

  2. dounia says:

    i really like graphic design and graffiti i`m not just a fan i am a student too

Leave a Reply