• home
  • forum
  • my
  • kt
  • download
  • Using Hit Tests & HitTestObject

    Author: 2009-05-11 10:05:39 From:

    This post is one of many in a series titled AS3 Flash Games for Beginners. If you haven’t followed the series from the beginning, I strongly recommend you return to the first tutorial and follow through to here. If you don’t  return to the beginning you will want to download the source code from the previous tutorial here.  Please keep in mind that if you have issues understanding parts of this tutorial and you didn’t return to the beginning, you’ll likely find clarity from doing so.

    First of all, I’m probably going to setup our Hit Test a lot differently then you’ve seen in the past. I use the normal Flash provided functions; however, I make a different use of them. If you don’t know a lot about hit Tests, there is a lot of talk about them and you can find information on them everywhere. Why? Because games need hit tests and certain methods work better than others depending on how it’s needed. My method is very simple (which is why I use it), and it’s a relatively accurate hit test.

    Step 1: Setting up our Ships to be Hit

    So here’s what we are going to do. Goto our Ship in the library. Create a new layer and draw a box with the Rectangle tool that covers as much area of our ship as possible while staying inside the ship as much as possible. Hope that’s not confusing… my box looks like this.

    Green Box representing our hit area of our Ship

    Green Box representing our hit area of our Ship

    Okay, right click our box then Convert to Symbol… and name it hitBox then make it a MovieClip with the Registration in the center. Now, we just need to name the instance. So click our hitBox to highlight it and in our Properties window you’ll see a white textbox with gray text saying <Instance Name>.  Make the instance name “hit” (without quotes). Here’s a screenshot:

    Setting up an instance of our hitBox MovieClip

    Setting up an instance of our hitBox MovieClip

    Nice, now guess what? We need to do this to our Stinger ship as well. So let’s open our Stinger ship MovieClip and pull our hitBox clip into it. Simply click and drag hitBox from the Library onto a new layer inside the Stinger ship MovieClip. Now grab the Free Transform Tool (F) and resize hitBox to be the size you need it to cover the Stinger ship. Mine looks like this.

    basics1_7step1-3

    Large hit area for our enemy ship

    Just name the instance of the box hit like we did before. Now double click our big box “hit” MovieClip to jump inside it.  Select our rectangle and goto our Color window (SHIFT+F9) and change the alpha to 0. This will make our “hit” MovieClip invisible.

    Let me explain the reasoning behind these hit boxes. First of all, Flash’s hitTestObject function checks if two objects hit each other. However, it checks the bounding box around our object not the pixels inside of it. So if a projectile comes inside our bounding box, it’s a hit even if it doesn’t touch the artwork of our ship. By creating the hit boxes we now have an area that we can do the hit test on that is more appropriately the size of our ship. Yes the tip of our ship and it’s wings are not entirely covered but as you’ll see when you test this momentarily it works extremely well.

    Step 2: Programming our HitTest

    Alright, this is going to be easy… We’ve got a few checks and one or two minor adjustments and our hitTests will be working perfectly. We’ll only check for a hit with in our bullet classes and not our ships. This will prevent us from checking for the same hitTest in two places which will only slow our game down and is entirely useless (as one check works great).  So open our StingerBullet class, we’re going to add one snippet of code to the loop function (at the bottom):

    			if (hitTestObject(target.hit))
    			{
    				trace("hitME");
    				removeSelf();
    			}

    Alright let’s break it down:

    • hitTestObject(target.hit). The hitTestObject function returns a boolean variable of either true or false depending on whether or not the two objects are hitting one another. If you only see one object here, that’s because this.hitTestObject(target.hit) is the same thing… It’s like basic English. If I say “Get the ball”, you is implied… it’s the same as “You get the ball”.  Well with our function calls “this” is implied. Therefore, our two objects being checked is this(this stinger bullet) and target.hit. If you remember target is our Ship variable that we passed through… and hit is the instance of our hitBox MovieClip that we created just a few seconds ago. If you had named the instance hitboxarea then we’d be checking with target.hitboxarea instead.
    • trace(”hitMe”); This just makes Flash send a message out that says hitMe everytime our Ship is hit. It’s good for testing because it lets us know the condition was met but useless for anything else. So feel free to use it during testing but you might as well delete them all before you release.
    • removeSelf(); this calls our function that removes our bullet from the stage… We created this in an earlier tutorial.

    Alright so we have one more problem… I forgot to tell you to remove your EnterFrame event listener from your bullet. Do that now in the removeSelf function. Here’s what your StingerBullet.as file should look like now:

    package com.asgamer.basics1
    {
     
    	import flash.display.MovieClip;
    	import flash.display.Stage;
    	import flash.events.Event;
     
    	public class StingerBullet extends MovieClip
    	{
     
    		private var stageRef:Stage;
    		private var target:Ship;
     
    		private var vx:Number;
     
    		public function StingerBullet(stageRef:Stage, target:Ship, x:Number, y:Number, vx:Number) : void
    		{
    			this.stageRef = stageRef;
    			this.target = target;
    			this.x = x;
    			this.y = y;
    			this.vx = vx;
     
    			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
    		}
     
    		private function loop(e:Event) : void
    		{
    			x += vx;
     
    			if (x > stageRef.stageWidth || x < 0)
    				removeSelf();
     
    			if (hitTestObject(target.hit))
    			{
    				trace("hitME");
    				removeSelf();
    			}
    		}
     
    		private function removeSelf() : void
    		{
    			removeEventListener(Event.ENTER_FRAME, loop); //don't forget to add this
    			if (stageRef.contains(this))
    				stageRef.removeChild(this);
    		}
     
    	}
     
    }

    Alright, our StingerBullet is ready. Let’s make our LaserBlue class check if it hits enemies. First though, we have to be able to find all the enemies currently on the stage to see if we are hitting them. We created an enemyList array in Engine earlier for just this purpose… but we need to make a quick change to it. Open up Engine.as and change it from this:

    private var enemyList:Array = new Array();

    to this:

    public static var enemyList:Array = new Array();

    Here’s the reasoning… because it is a static var it would maintain the same value between all instances of the Engine class(we only have one Engine running so we really don’t have to worry about that). Also it being static and public means we can access it by calling Engine.enemyList whenever we need it. We’ll need to call it in our LaserBlue class so we can tell what enemies to check if we are hitting.

    So open up LaserBlue.as now and in our loop function, at the bottom we’re going to add our hitTest:

    			for (var i:int = 0; i < Engine.enemyList.length; i++)
    			{
    				if (hitTestObject(Engine.enemyList[i].hit))
    				{
    					trace("hitEnemy");
    					Engine.enemyList[i].takeHit();
    					removeSelf();
    				}
    			}

    This looks a little different than before.. let’s break it down:

    • First we for loop through all the enemies in enemyList. You should understand how a for loop works as we have discussed it previously.
    • Then we check for a hitTest again, this time between our LaserBlue and the hit instance in each of our enemy ships.
    • If it returns true we trace “hitEnemy” and call the takeHit function of our enemy… One problem, they don’t have a takeHit function yet. We’ll have to make one.
    • Then we call removeSelf(); to get rid of the laser.

    Alright now we gotta open Stinger.as so we can add that takeHit function. We should of done something like this with our Ship.as class as well but I’ve got plans to do something different with this in our next tutorial, so keep an eye out for it. In Stinger.as simply add a public takeHit function that calls removeSelf(). Why not just call removeSelf? Well one it’s private so we can’t (unless we make it public) but also because we’re going to add some stuff to takeHit in the next tutorial as well :) Also I made a couple adjustments to make it easier to hit the stinger enemy and make it more fun dodging his bullets. I changed vy to 4, ay to .2, and the bullet speeds in fireWeapon to 8 and -8.  But tweak these however you want, it’s your game. Your Stinger.as should now look like this:

    package com.asgamer.basics1
    {
     
    	import flash.display.MovieClip;
    	import flash.display.Stage;
    	import flash.events.Event;
     
    	public class Stinger extends MovieClip
    	{
     
    		private var stageRef:Stage;
    		private var vy:Number = 4;
    		private var ay:Number = .2;
    		private var target:Ship;
     
    		public function Stinger(stageRef:Stage, target:Ship) : void
    		{
    			this.stageRef = stageRef;
    			this.target = target;
     
    			x = Math.random() * stageRef.stageWidth;
    			y = -5;
     
    			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
    		}
     
    		private function loop(e:Event) : void
    		{
    			vy += ay;
    			y += vy;
     
    			if (y > stageRef.stageHeight)
    				removeSelf();
     
    			if (y - 15 < target.y && y + 15 > target.y)
    				fireWeapon();
    		}
     
    		private function fireWeapon() : void
    		{
    			stageRef.addChild(new StingerBullet(stageRef, target, x, y, -8));
    			stageRef.addChild(new StingerBullet(stageRef, target, x, y, 8));
    		}
     
    		private function removeSelf() : void {
     
    			removeEventListener(Event.ENTER_FRAME, loop);
     
    			if (stageRef.contains(this))
    				stageRef.removeChild(this);
     
    		}
     
    		public function takeHit() : void
    		{
    			removeSelf();
    		}
     
    	}
     
    }

    Okay, so at this point if you compile the game it should work. If a bullet hits a ship it disappears. If a bullet hits an enemy ship it disappears. If a bullet hits your ship… nothing happens to your ship.  So let’s just make a small implosion when our ship gets hit.

    Step 3: Creating Bullet Explosions

    Create a new MovieClip (CTRL+F8) and name it implosion.  Now inside the MovieClip draw a small yellow circle about the size of your enemy bullet. Go down 5 frames and right click and select Insert Keyframe. In your new keyframe, resize your circle til it’s almost invisible.  Now right click in the area between your first and last keyframe and click Create Shape Tween. You should end up with your timeline looking something like this:

    Shot of timeline after creating a small implosion MovieClip

    Shot of timeline after creating a small implosion MovieClip

    Alright now right click your MovieClip in the Library (CTRL+L) and select Linkage…  export for ActionScript and set it’s class to “com.asgamer.basics1.SmallImplosion”

    Now you know what’s next…. create a new file in the com/asgamer/basics1 directory and call it SmallExplosion.as… Now fill that blank document up with this ActionScript.

    package com.asgamer.basics1
    {
     
    	import flash.display.MovieClip;
    	import flash.display.Stage;
    	import flash.events.Event;
     
    	public class SmallImplosion extends MovieClip
    	{
     
    		private var stageRef:Stage;
     
    		public function SmallImplosion(stageRef:Stage, x:Number, y:Number)
    		{
    			this.stageRef = stageRef;
    			this.x = x;
    			this.y = y;
    			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
    		}
     
    		private function loop(e:Event)
    		{
    			if (currentFrame == totalFrames)
    				removeSelf();
    		}
     
    		private function removeSelf() : void
    		{
    			removeEventListener(Event.ENTER_FRAME, loop);
     
    			if (stageRef.contains(this))
    				stageRef.removeChild(this);
    		}
     
    	}
     
    }

    Pretty simple really… we’ve discussed 90% of this before… let’s break it down:

    • we set some imports, create our class that extends MovieClip, and setup a class variable called stageRef.  Our constructor function needs the stage reference and an x and y. We then set our variables according to the parameters passed into the constructor.  We create an event listener that fires everytime we enter the frame which calls the loop function.
    • Inside loop we check for one thing… if (currentFrame == totalFrames) currentFrame and totalFrames are variables we inherited from MovieClip. Because MovieClips operate on the timeline they always have a current frame that they are on and totalFrames is the number of frames in the MovieClip’s timeline that are being used. So if our currentFrame is equal to totalFrames (which is the same as the last frame number) then we call removeSelf.
    • removeSelf is like all the other removeSelf’s that we have made. It kills the event listener and removes the MovieClip from the stage.

    Alright…. one last thing. When an enemy bullet hits our ship we need the small implosion to appear. So let’s open back up StingerBullet and add this line inside the if (hitTestObject) condition: stageRef.addChild(new SmallImplosion(stageRef, x, y));.

    So our StingerBullet.as file should look like this.

    package com.asgamer.basics1
    {
     
    	import flash.display.MovieClip;
    	import flash.display.Stage;
    	import flash.events.Event;
     
    	public class StingerBullet extends MovieClip
    	{
     
    		private var stageRef:Stage;
    		private var target:Ship;
     
    		private var vx:Number;
     
    		public function StingerBullet(stageRef:Stage, target:Ship, x:Number, y:Number, vx:Number) : void
    		{
    			this.stageRef = stageRef;
    			this.target = target;
    			this.x = x;
    			this.y = y;
    			this.vx = vx;
     
    			addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
    		}
     
    		private function loop(e:Event) : void
    		{
    			x += vx;
     
    			if (x > stageRef.stageWidth || x < 0)
    				removeSelf();
     
    			if (hitTestObject(target.hit))
    			{
    				trace("hitME");
    				stageRef.addChild(new SmallImplosion(stageRef, x, y));
    				removeSelf();
    			}
    		}
     
    		private function removeSelf() : void
    		{
    			removeEventListener(Event.ENTER_FRAME, loop);
    			if (stageRef.contains(this))
    				stageRef.removeChild(this);
    		}
     
    	}
     
    }

    Alright I’m done. Compile your file and you should get something like this:

    Next time we’ll create effects when a ship is destroyed so it’s not so dull and we’ll do something else with our ship to make it look cool as well.

    discuss this topic to forum

    relation tutorial

    No information

    Category

      3D (36)
      Math Physics (18)
      3rd Party (10)
      Navigation (70)
      Actionscripting (228)
      Optimization (17)
      Animation (166)
      Projector (11)
      Audio (54)
      Special Effects (170)
      Backend (26)
      Text Effects (89)
      Drawing (34)
      Tips and Techniques (51)
      Dynamic Content (34)
      Tricks (8)
      Games (106)
      Utilities (23)
      Getting Started (96)
      Video (59)
      Interactivity (46)
      Web Design (35)

    New

    Hot