8bitrocket.com
24Apr/070

Advanced Flash 8 Game Programming : Pixel level collision detection Part II (or let's add Bitmap animation caching)

In Part 1 - Advanced Flash 8 Game Programming : Pixel level collision detection I discussed the very basics of doing Pixel level hit detection with the bitmapData object. In this second part, I am going to add one more small feature that hopefully will improve the performance of your games. This technique will work in CS3 as well as Flash 8, but I have not used CS3 yet, so I can't yet explain any differences in syntax or usage.

Action games need SPEED. Flash is actually very slow when rendering the screen, and the biggest culprit are the math calculations that are needed when using the timeline to run through a key frame vector animation. If your movie clip contains no animation, and just needs to move from point x1,y1 to point x2,y2, you can use bitmap surface caching (movieClip.cacheAsBitmap=true) to see a noticeable increase in performance. But, what do you do if the movieClip actually needs to run through a timeline animation? My theory is to eliminate as many timeline and vector animations as possible by caching them all before they are needed. If you play my game Retroblaster , you will notice that I spend a inordinate amount of time at the beginning doing something called "Preparing System Warps". What I am actually doing is pre-caching all of the game animations to be used later. This allows me to have many more explosions, enemy ships, etc on the screen than if I tried to render vector timeline based animations in real-time.

I have taken the Part 1 code and modified it to add in this feature. Again, I am using Ari Feldman's Sprite Lib GPL for my quick and dirty animation. I have changed the player and enemy objects to be a spaceman (player) and scorpion (enemy). I have pre-cached two frames of animation for the spaceman, and when you press the arrows keys in any direction the animation will run through one iteration of the loop. It looks a little weird because I haven't spent any time adding frames of animation for the various directions, but rest assured, the sprite library contains then. (thanks Ari) They are not necessary for this demo though.

 

[cc lang="javascript" width="550"]//hitpixels.fla
//Jeff Fulton 2007
//www.8bitrocket.com
//pixel level collision detection with bitmap data object and animation caching
import flash.display.*;
import flash.geom.*;

//1. load in spriteMap from library
var spriteMapBitmap:BitmapData = BitmapData.loadBitmap("actionMap");
//mc.attachBitmap(spriteMapBitmap, this.getNextHighestDepth());

//2 create an array of bitmaps for the player sprite
//in this example, the player has two frames of animation
//this is a simple example, so we won't do 2 frames for every direction of movement, we'll just use these two
var aPlayerSpriteArray:Array=new Array();
var tempSprite:BitmapData=new BitmapData(32,32,true,0x00000000);
tempSprite.copyPixels(spriteMapBitmap, new Rectangle(0, 72, 32, 32), new Point(0, 0));
aPlayerSpriteArray.push(tempSprite);
var tempSprite2:BitmapData=new BitmapData(32,32,true,0x00000000);
tempSprite2.copyPixels(spriteMapBitmap, new Rectangle(0, 102, 32, 32), new Point(0, 0));
aPlayerSpriteArray.push(tempSprite2);

//3. put player on screen, copy pixels from sprite to mc1
var playerSprite:BitmapData=new BitmapData(32,32,true,0x00000000);
//playerSprite.copyPixels(spriteMapBitmap, new Rectangle(0, 72, 32, 32), new Point(0, 0));
this.createEmptyMovieClip("player_mc",this.getNextHighestDepth());
player_mc.attachBitmap(aPlayerSpriteArray[0],100);
playerSprite=aPlayerSpriteArray[0];
player_mc._x=100;
player_mc._y=100;
var playerSpriteIndex:Number=0;

//4. copy pixels from spriteMap to enemy
var sprite2:BitmapData=new BitmapData(32,32,true,0x00000000);
sprite2.copyPixels(spriteMapBitmap, new Rectangle(409, 72, 32, 32), new Point(0, 0));
this.createEmptyMovieClip("sprite2_mc",this.getNextHighestDepth());
sprite2_mc.attachBitmap(sprite2,100);
sprite2_mc._x=200;
sprite2_mc._y=200;

function updatePlayerAnimation() {
playerSpriteIndex++;
if (playerSpriteIndex > aPlayerSpriteArray.length-1) playerSpriteIndex=0;
player_mc.attachBitmap(aPlayerSpriteArray[playerSpriteIndex],100);
playerSprite=aPlayerSpriteArray[playerSpriteIndex];
sprite_txt.text=playerSpriteIndex;
}

this.onEnterFrame=function() {

if (Key.isDown(Key.UP)) {
player_mc._y ;
updatePlayerAnimation();
}
if (Key.isDown(Key.DOWN)) {
player_mc._y++;
updatePlayerAnimation();
}
if (Key.isDown(Key.LEFT)) {
player_mc._x ;
updatePlayerAnimation();
}
if (Key.isDown(Key.RIGHT)) {
player_mc._x++;
updatePlayerAnimation();
}

if (player_mc.hitTest(sprite2_mc)) {
normal_txt.text="true";
//trace("basic hit detected");
//test for pixel level hit
var myPoint:Point = new Point (sprite2_mc._x, sprite2_mc._y);
if (playerSprite.hitTest (new Point (player_mc._x , player_mc._y), 255, sprite2, myPoint, 255)){
bitmap_txt.text="true";
//trace("bitMap hit detected");
}else{
bitmap_txt.text="false";
}
}else{
normal_txt.text="false";
bitmap_txt.text="false";

}

}

[/cc]

hitpixels_with_bitmap_anmationloop.swf (2007)
A demo of using bitmapData to do pixel level collision detection and bitmap animation caching.

Use the arrow keys to move the spaceman to the scorpion sprite.

When the spaceman enters the 32x32 area for the scorpion, the normal hit becomes true.

The bitmap hit will become true only if the spaceman collides with a non-trnasprant pixel in the scorpion.

The Player Sprite number in the text box represents the current sprite in the array (bitmapData object).

download the fla file

Ari Feldman's Sprite Lib used via GPL

1. First we create a spriteMap in memory from a bitmap (png with transparency) that I have in the library.
2. Next, I use some very very rudimentary code to pull two 32x32 images from the spriteMap and place them in an array called "aPlayerSpriteArray". This can be done with loops, etc, but I just wanted to demonstrate it as clearly as possible here.
3. I put the player on the screen by creating a holder clip for the player and attaching the first bitmap in the array to the clip. I also make sure to place a reference to the attached bitmap in the variable "playerSprite". This is necessary to make sure hit detection can be performed on the bitmap.
4. I copy some more pixels from the spriteMap to a bitmap data object named "sprite2" and then attach it to a movieClip. This will represent our enemy (scorpion) sprite.
5. Next I have a basic function for updating the player sprite. This is called when ever a key is pressed, notice that I have to re-reference the playerSprite on each change to make sure hit detection is done properly.
6. The onEnterFrame is the main loop. It checks for key input. I don't use a key Listener object here because key repeats are difficult to detect with that object.
7. The hitTesting is the final part of the code. I first look for a basic hit. This can be done with hitTest, or one of a number of math based detections (circle - circle, rect - rect, etc). If that hit is true, then I check for the pixel level hit. Hopefully this will save cycles in your game.

Good luck, I'm off to use this code in a new game I am making based on the Atari classic called Gravitar (or OIDS if you had an Amiga or Atari ST back when that was the cool thing to do).

 

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.

This site is protected by Comment SPAM Wiper.