Hit the Target With a Deadly Homing Missile

Hit the Target With a Deadly Homing Missile

Tutorial Details
  • Difficulty: Intermediate
  • Platform: Flash (Flash Player 9+)
  • Language: AS3
  • Software used: Flash Professional CS5.5 (but can be followed with CS3+)
  • Estimated Completion Time: One hour
This entry is part 9 of 13 in the You Do The Math Session
« PreviousNext »

This tutorial will guide you through adding deadly accurate homing missiles to the arsenal of your next game.


Final Result Preview

Let’s take a look at the final result we will be working towards:


Step 1: Set up the FLA Document

Create a new Flash document set for ActionScript 3.0. I’ll be using the dimensions of 600×400, and a frame rate of 30 FPS. Save the file with a name of your choice.

Create a .FLA document

Step 2: Create a Document Class

Besides the FLA, we also need to create a document class. Create a new Actionscript file, and add this code:

package 
{
	import flash.display.Sprite;
	
	public class Main extends Sprite 
	{
		public function Main()
		{
			
		}
	}
	
}

Save this file in the same directory as our FLA. Name it Main.as.


Step 3: Link the Main Class with the FLA

In order to compile the code from the Main class, we need to link it with the FLA. On the Properties panel of the FLA, next to Class, enter the name of the document class, in this case, Main.

Link the Main class with the FLA

Then, save the changes on the FLA.


Step 4: Draw a Missile

We need a missile graphic to be displayed when shooting. You may import a bitmap or draw a shape right there on Flash. I’ll be using a tiny shape on this example.

Draw a missile shape or import a missile bitmap.

What’s important here is that you have to make the missile point straight to the right, since that’s the origin point for the rotation. So 0° means pointing straight to the right, -90° means upwards, 90° means downwards, and 180° points to the left. Later on we’ll need to rotate the missile according to its direction.


Step 5: Create a MovieClip for the Missile

Once you have the missile graphic, select it and press F8 to create a Movie Clip. Name it "Missile", make sure the the Registration Point is at the center, and tick the "Export for ActionScript" checkbox.

Create a MovieClip out of the missile you drew.

You’ll end up with a Missile MovieClip in the Library.

Missile MovieClip in the Library.

If you have a Missile instance on the Stage, delete it. We’ll be adding the Missile MovieClip by code.


Step 6: Aim

The first thing a homing missile needs to know is where the target is located. We’re going to set the rotation of the missile according to the position of the mouse cursor first. Let’s work with the enterFrame Event for a constant rotation update.

Add a Missile instance to the stage, I’m placing it at the center (300, 200). Then calculate the distance from the missile to the mouse cursor (I’m storing it in variables targetX and targetY). Finally, the missile’s angle will be the arc tangent of both points (targetX, targetY). The result you’ll get will be in radians, but the rotation works in degrees, so you’ll need to do the conversion by multiplying by 180/Pi. (To see why, check this article.)

import flash.events.Event;

public class Main extends Sprite 
{
    private var missile:Missile = new Missile();
    
    public function Main()
    {
        addChild(missile);
        missile.x = 300;
        missile.y = 200;
        addEventListener(Event.ENTER_FRAME, playGame);
    }
    
    private function playGame(event:Event):void
    {
        var targetX:int = mouseX - missile.x;
        var targetY:int = mouseY - missile.y;
        missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
    }
}

(Not sure what Math.atan2() is for? Check out this article on trigonometry.

If you Publish (Ctrl + Enter) the document at this point, you should be getting something like this:

Move your mouse near the missile to see it rotate.


Step 7: Seek

We got the rotation, now we need the movement. The missile has to seek the target, no matter whether it’s a steady or a moving target. What we’ll do is calculate the movement according to the current rotation of the missile. Let’s set a value for the speed, and make the missile chase after the mouse cursor.

We’ll include a couple of new variables to calculate the velocity (vx, vy). When the missile is pointing to the right, its angle is lower than 90° and higher than -90°, so it’s always lower than the absolute value of 90°. When it’s pointing to the left, its angle has an absolute value higher than 90°. This will determine vx in accordance to speed, then vy will be the difference of speed and vx.

private var speed:int = 10;

public function Main()
{
    addChild(missile);
    missile.x = 300;
    missile.y = 200;
    addEventListener(Event.ENTER_FRAME, playGame);
}

private function playGame(event:Event):void
{
    var targetX:int = mouseX - missile.x;
    var targetY:int = mouseY - missile.y;
    missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
    //Velocity in x is relative to the angle, when it's 90° or -90°, vx should be 0.
    var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
    var vy:Number;//Velocity in y is the difference of speed and vx.
    if (missile.rotation < 0)
        vy = -speed + Math.abs(vx);//Going upwards.
    else
        vy = speed - Math.abs(vx);//Going downwards.
    
    missile.x += vx;
    missile.y += vy;
}

You’ll get a missile chasing your cursor.

You can use a different speed if you want.


Step 8: Create a Missile Launcher

Missiles don’t come out of thin air, they are shot out of missile launchers. Let’s make a MovieClip representing a cannon (I’ll use a simple rectangle), and name it Cannon. I’m going to add a Cannon instance by code, so I’m going to keep the stage empty.

Cannon MovieClip in the Library.

Step 9: Shoot

Now, instead of adding a missile, I’m just going to add a cannon, and a missile will be added at the cannon’s position when I click on the stage. We’ll add a Boolean to check if the missile has been shot, and also a new function for shooting after the click.

import flash.events.MouseEvent;

public class Main extends Sprite 
{
    private var missile:Missile = new Missile();
    private var speed:int = 10;
    private var cannon:Cannon = new Cannon();
    private var missileOut:Boolean = false;//Has the missile been shot?
    
    public function Main()
    {
        addChild(cannon);
        cannon.x = 50;
        cannon.y = 380;
        addEventListener(Event.ENTER_FRAME, playGame);
        stage.addEventListener(MouseEvent.CLICK, shoot);
    }
    
    private function playGame(event:Event):void
    {
        if (missileOut)
        {
            var targetX:int = mouseX - missile.x;
            var targetY:int = mouseY - missile.y;
            missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
            
            var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
            var vy:Number;
            if (missile.rotation < 0)
                vy = -speed + Math.abs(vx);
            else
                vy = speed - Math.abs(vx);
            
            missile.x += vx;
            missile.y += vy;
        }
    }
    
    private function shoot(event:MouseEvent):void
    {
        if (!missileOut)
        {
            addChild(missile);
            swapChildren(missile, cannon);//missile will come out from behind cannon
            missileOut = true;
            missile.x = cannon.x;
            missile.y = cannon.y;
        }
    }

This is what you’ll get:

This doesn’t look nice. We have to either make the cannon rotate as well, or force the missile to go upwards right after being shot. Since option #1 is the easiest approach, we’ll take option #2.


Step 10: Less Precision for Better Looks

If the cannon is vertical, we would expect the missile to launch upwards and then get on track towards its target. The approach I’ll use to achieve this is to give the missile a starting angle of -90° (pointing upwards), and smoothly rotate to get on track to the mouse cursor. We’ll add an ease variable to determine the smoothness or sharpness of the rotation. Then we’ll create another variable to keep track of the actual rotation that points straight to the target, while the missile’s rotation will change according to the ease we set (ease = 1 will behave just like before, anything higher will make a smoother turn).

Since half of the rotation values are negative, in some cases we’ll need to calculate them against 360 to get the actual difference between the target angle and the missile’s rotation.

private var ease:int = 10;

public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);
}

private function playGame(event:Event):void
{
    if (missileOut)
    {
        var targetX:int = mouseX - missile.x;
        var targetY:int = mouseY - missile.y;
        var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
        if (Math.abs(rotation - missile.rotation) > 180)
        {
            if (rotation > 0 && missile.rotation < 0)
                missile.rotation -= (360 - rotation + missile.rotation) / ease;
            else if (missile.rotation > 0 && rotation < 0)
                missile.rotation += (360 - rotation + missile.rotation) / ease;
        }
        else if (rotation < missile.rotation)
            missile.rotation -= Math.abs(missile.rotation - rotation) / ease;
        else
            missile.rotation += Math.abs(rotation - missile.rotation) / ease;
        
        var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
        var vy:Number;
        if (missile.rotation < 0)
            vy = -speed + Math.abs(vx);
        else
            vy = speed - Math.abs(vx);
        
        missile.x += vx;
        missile.y += vy;
    }
}

private function shoot(event:MouseEvent):void
{
    if (!missileOut)
    {
        addChild(missile);
        swapChildren(missile, cannon);//missile will come out from behind cannon
        missileOut = true;
        missile.x = cannon.x;
        missile.y = cannon.y;
        missile.rotation = -90;//missile will start pointing upwards
    }
}

Check it out:

Notice what happens when you move your mouse out of the SWF, and how this is different from the previous example.


Step 11: Missile Hits, Missile Explodes

Besides the Missile Movie Clip, we need an explosion animation. In my case, I’ll make a separate MovieClip with a simple tween of a circle that expands. I’m exporting it as Explosion. Press O to select the Oval Tool, and hold Shift while drawing the oval to get a circle.

The circle inside its own Movie Clip with a Bevel filter.

For a nicer visual effect, I’ll put the circle inside another Movie Clip of its own, and give it a Bevel filter to get a darker color at the bottom and a lighter color at the top. Next, I’ll go to frame 10 and press F6 to create a Keyframe, then right-click between frame 1 and 10 and create a Classic Tween. Back on frame 10, press Q to select the Free Transform Tool and enlarge the circle.

Enlarge the circle in a Classic Tween.

Then, create another Classic Tween to frame 20, I’ll add a Blur filter effect.

Add a Blur filter in a Classic Tween.

Finally, make it disappear in a last Classic Tween to frame 30 with an Alpha color effect going to 0.

Alpha color effect set to 0 in a Classic Tween.

Step 12: Clean Up the Stage

The explosion animation has to be removed after it finishes, or it will loop indefinitely. Add a new layer and press F6 on the last frame, then press F9 to open the Actions panel, and add this code:

stop();<br />parent.removeChild(this);

This will make the Explosion instance remove itself after the animation is done.

The Explosion instance will remove itself.

Step 13: Explode

Now when the missile meets the cursor, we’ll replace it with an Explosion instance. We just need to add a new conditional in the playGame() function.

private function playGame(event:Event):void
{
    if (missileOut)
    {
        if (missile.hitTestPoint(mouseX, mouseY))
        {
            var explosion:Explosion = new Explosion();
            addChild(explosion);
            explosion.x = missile.x;
            explosion.y = missile.y;
            removeChild(missile);
            missileOut = false;
        }
        else
        {
            var targetX:int = mouseX - missile.x;
            var targetY:int = mouseY - missile.y;
            var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
            if (Math.abs(rotation - missile.rotation) > 180)
            {
                if (rotation > 0 && missile.rotation < 0)
                    missile.rotation -= (360 - rotation + missile.rotation) / ease;
                else if (missile.rotation > 0 && rotation < 0)
                    missile.rotation += (360 - rotation + missile.rotation) / ease;
            }
            else if (rotation < missile.rotation)
                missile.rotation -= Math.abs(missile.rotation - rotation) / ease;
            else
                missile.rotation += Math.abs(rotation - missile.rotation) / ease;
            
            var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
            var vy:Number;
            if (missile.rotation < 0)
                vy = -speed + Math.abs(vx);
            else
                vy = speed - Math.abs(vx);
            
            missile.x += vx;
            missile.y += vy;
        }
    }
}

Take a look:


Step 14: Something Else to Blow Up

Chasing after the mouse cursor was entertaining, but it’s pointless in a game; we need to make a target. I’m going to draw a bunch of circles to form a Target Movie Clip.

Create and export a Target Movie Clip.

Step 15: Shoot the Target

Now we’ll add a Target instance for the missile to have a more tangible objective. So we’ll replace any reference of the mouse cursor for the target’s position. Also, we won’t be testing for a hit point, but an object.

private var target:Target = new Target();

public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);addChild(target);
    target.x = 550;
    target.y = 50;
}

private function playGame(event:Event):void
{
    if (missileOut)
    {
        if (missile.hitTestObject(target))
        {
            var explosion:Explosion = new Explosion();
            addChild(explosion);
            explosion.x = missile.x;
            explosion.y = missile.y;
            removeChild(missile);
            missileOut = false;
        }
        else
        {
            var targetX:int = target.x - missile.x;
            var targetY:int = target.y - missile.y;
            var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
            if (Math.abs(rotation - missile.rotation) > 180)
            {
                if (rotation > 0 && missile.rotation < 0)
                    missile.rotation -= (360 - rotation + missile.rotation) / ease;
                else if (missile.rotation > 0 && rotation < 0)
                    missile.rotation += (360 - rotation + missile.rotation) / ease;
            }
            else if (rotation < missile.rotation)
                missile.rotation -= Math.abs(missile.rotation - rotation) / ease;
            else
                missile.rotation += Math.abs(rotation - missile.rotation) / ease;
            
            var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
            var vy:Number;
            if (missile.rotation < 0)
                vy = -speed + Math.abs(vx);
            else
                vy = speed - Math.abs(vx);
            
            missile.x += vx;
            missile.y += vy;
        }
    }
}

private function shoot(event:MouseEvent):void
{
    if (!missileOut)
    {
        addChild(missile);
        swapChildren(missile, cannon); //missile will come out from behind cannon
        missileOut = true;
        missile.x = cannon.x;
        missile.y = cannon.y;
        missile.rotation = -90;//missile will start pointing upwards
    }
}

The hitTestObject() method actually only checks for an overlap between the bounding boxes of the two objects (i.e., the blue boxes that appear when you click an instance of the object in the stage), so watch out for that; it’s not pixel-perfect collision detection. However, it does the job just fine here.

You may try placing the target at different locations, as well as the cannon.


Step 16: Moving Target

We already saw that the missile will chase a moving target, such as the mouse cursor, so now let’s make the Target instance move a little.

This isn’t realistic physics, I’m just going to make the target bounce vertically. I’ll pick a reference point as the ground level, and add a gravity value to affect the target. And to make it more dynamic, I’ll increase the missile speed to 15.

private var floor:int = 385;
private var gravity:Number = 0.5;
private var targetVY:Number = 0;//Current vertical velocity of the target

public function Main()
{
    addChild(cannon);
    cannon.x = 50;
    cannon.y = 380;
    addEventListener(Event.ENTER_FRAME, playGame);
    stage.addEventListener(MouseEvent.CLICK, shoot);addChild(target);
    target.x = 550;
    target.y = 50;
}

private function playGame(event:Event):void
{
    if (missileOut)
    {
        if (missile.hitTestObject(target))
        {
            var explosion:Explosion = new Explosion();
            addChild(explosion);
            explosion.x = missile.x;
            explosion.y = missile.y;
            removeChild(missile);
            missileOut = false;
        }
        else
        {
            var targetX:int = target.x - missile.x;
            var targetY:int = target.y - missile.y;
            var rotation:int = Math.atan2(targetY, targetX) * 180 / Math.PI;
            if (Math.abs(rotation - missile.rotation) > 180)
            {
                if (rotation > 0 && missile.rotation < 0)
                    missile.rotation -= (360 - rotation + missile.rotation) / ease;
                else if (missile.rotation > 0 && rotation < 0)
                    missile.rotation += (360 - rotation + missile.rotation) / ease;
            }
            else if (rotation < missile.rotation)
                missile.rotation -= Math.abs(missile.rotation - rotation) / ease;
            else
                missile.rotation += Math.abs(rotation - missile.rotation) / ease;
            
            var vx:Number = speed * (90 - Math.abs(missile.rotation)) / 90;
            var vy:Number;
            if (missile.rotation < 0)
                vy = -speed + Math.abs(vx);
            else
                vy = speed - Math.abs(vx);
            
            missile.x += vx;
            missile.y += vy;
        }
    }
    targetVY += gravity;
    target.y += targetVY;
    if (target.y > floor)
    {
        target.y = floor;
        targetVY = -18;
    }
}

If you Publish this now, you should be getting a moving target.


Conclusion

Whether you want an accurate homing missile, or you’d prefer a smooth animation, you can get both results based on this example. Now you’ve got a new weapon to add in your arsenal, maybe you could try making a Worms-like game, or even use the algorithm on something other than a missile, like some weird mosquito that follows your character.

I hope you’ve found this tutorial useful. Thanks for reading!

Daniel Ramirez is Dallenad on Activeden
  • http://www.nitras.be nitras

    Good stuff! You cover a fair amount of issues one can encounter.

    I especially enjoyed “Since option #1 is the easiest approach, we’ll take option #2.”
    skills++

    • http://dallenad.com/games/ Daniel Ramirez
      Author

      Glad you liked it.

  • Frank Mawn

    Good stuff !

  • Nick Johnson

    Best missle/rocket effect I have ever seen in a flash game is in this game called 2099: http://www.games121.com/2010/06/2099.html I would actually like to see a tutorial how to make such missle movement.

  • http://thedevelopertuts.com Bratu Sebastian

    Wow, cool effect, I was trying to do this before.

    • http://dallenad.com/games/ Daniel Ramirez
      Author

      Really? Well, there it is. ;)

    • Frank Mawn

      SPAM!

      • http://michaeljameswilliams.com/ Michael James Williams

        Nope, that’s actually one of our authors!

  • / John

    It would be better if you could move the target with arrow keys. Great work :)

  • shumi

    i need some help
    i ve drawn a Rocket Launcher Tank and i can rotate the turret of the tank with my Mouse
    but when i click to launch the rokcet the rocket x and y is straight ahead it doesnt move with the turret what should i do ? :D what can you suggest tried to put missile.x = turret.x . cos ( angle of rotation ) but it didnt work ?

    • http://dallenad.com/games/ Daniel Ramirez
      Author

      Try the same algorithm for the speed and direction of the missile, replacing the speed for the distance between the center of the tank and the turret. I tiried this piece of code and it worked for me:

      missile.x = cannon.x;
      missile.y = cannon.y;
      var targetX:int = mouseX – missile.x;
      var targetY:int = mouseY – missile.y;
      missile.rotation = Math.atan2(targetY, targetX) * 180 / Math.PI;
      missile.x += distance * (90 – Math.abs(missile.rotation)) / 90;
      if (missile.rotation < 0)
      missile.y += -distance + Math.abs(missile.x – cannon.x);
      else
      missile.y += distance – Math.abs(missile.x – cannon.x);

      It's the same algorithm that controls the direction, but relative to the distance of the turret instead of the speed of the missile. I'm sure there are better ways to do this, but this is the first thing that worked for me.

  • http://www.3rddesign.com 3rddesign

    I enjoyed the flash game. I was like a little kid chasing the target. Anyway that’s really an enjoyable tutorial. I will try this.

    • http://dallenad.com/games/ Daniel Ramirez
      Author

      Go for it. ;)

  • shumi

    thank you :) i will try today and tell you the results :) btw i can make a radom shooting effects using a projectile motion :D

  • Kim Bo

    hi this is great tutorial!! I would like to know how could i do so the misle i trying to hit closest enemie and how to add all enemies to hitable targets… (if missle miss the first target it still trying to hit closest)???

    • http://dallenad.com/games/ Daniel Ramirez
      Author

      You mean like a heat-seeker missile? That sounds interesting. I’ll see if I can figure something out…

  • Kim Bo

    if you do so, give me a sign :)

  • Miklós Borsi

    The tutorial is great, I laughed at the method #1 and method #2 part.
    However, I have some problems. I’m creating a shooter and I used this for my rockets (rockets are stored in an array and they all follow the mouse. The following mechanism is in a for each in loop)
    The problem is that after appearing they instantly teleport to the top left corner and stay there (I figured that out with tracing, they are created at the right position.)
    What could be wrong? I tried entirely copying it and it doesn’t work that way either.

    • Miklós Borsi

      Oh… Sorry about my previous comment, everything is working fine. The problem was dividing by zero (I’m serious, I accidentally set ease to 0.)

  • Alex

    Make missile follow mouse down and then make a fast move to upper-left corner.
    Here is the situation: http://petromi.com/get/432f44c773.png
    You may notice that when moving clockwise, at 6 o’clock (if target fastly moves to about 10-12 o’clock), missile makes “V” movement. It changes it’s direction sharply as opposed to nice curve.
    That only appears “at 6 o’clock” when moving clockwise.

    So here as a solution. It’d be cool if admins will fix that annoying bug.
    Just change the line
    missile.rotation += (360 – rotation + missile.rotation)
    to
    missile.rotation += (360 + rotation – missile.rotation) / ease

    Works for me. Now it’s nice curve movement without sharp “V” things.

    • Alex

      oops. Just spotted that i copied some extra to solution.
      Should be: missile.rotation += (360 + rotation – missile.rotation)
      (no “/ ease”)

  • Aaron

    i think this is awesome however i see a slight issue or 2 number 1 this seems more like a click to start movie, u should be able to move the missle with your mouse, it shouldnt just follow the target
    and another thing is the target wont be destroyed when hit any suggestions?

    • http://michaeljameswilliams.com/ Michael James Williams

      Hey Aaron, check out the sequel tutorial :)

  • tanveer

    it’s good and i am trying to make it complex.
    so good.
    i had successfully build it.

  • Zack

    hi, how do i do multiple missile launching?

    like it keeps shooting missile every time i click so it launches like a whole barrage at once.

    i was thinking of using like a dynamic array of sorts, but then i don’t really know how to do the collision detecting. would i loop through the entire array every time just to check if one hit?

  • jackie

    my missile was downwards while lauching ,and my missile was trembling ad like crazy ,how could i fix this?

  • baek

    please help me!
    I don’t know how convert to other coordinate system language for y axis…