I tried following a combination of Lee Brimlow's blitting tutorial series and and the technique in Rex Van der spuy's "advanced game design with flash"
I am a developer working on a web online virutal world made in flash. I made a phone application (works similar to the phone in grand theft auto games). Anyway, when a message is sent we want to play this crazy animation of an envelope flying around and transforming with sparkles around it. It was laggy (especially on older computers) so I thought it would be a great chance to use blitting. However, the blitting animation actually plays slower than a regular movieclip!! What the heck is going on here? Is blitting only better for mobile devices and actually slower on computers? Maybe I am doing something wrong. Here is my code:
// THIS PART HAPPENS WHEN PHONE IT INITIALIZED
//**
//---------------- Blitting stuff ----------------------------------
// add this bitmap stage to the display list so we can see it
_bitmapStage = new BitmapData(550, 400, true, 0xD6D6D6);
_phoneItself.addChild(new Bitmap(_bitmapStage));
var _spritesheetClass:Class = getDefinitionByName("ESpritesheet_1") as Class;
_spritesheet = new _spritesheetClass() as BitmapData;
_envelopeBlit = new BlitSprite(_spritesheet, BlitConfig.envelopeAnimAry , _bitmapStage);
_envelopeBlit.x = -100;
_envelopeBlit.y = 0;
_envelopePlayTimer = new Timer(5, 0);
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopeBlit.addEventListener("ENV_ANIM_DONE", onEnvAnimFinished);
// a "BlitSprite" is a class that I made. It looks like this:
package com.fs.util_j.blit_utils
{
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
public class BlitSprite extends EventDispatcher
{
private var _fullSpriteSheet:BitmapData;
private var _rects:Array;
private var _bitmapStage:BitmapData;
private var pos:Point = new Point ();
public var x:Number = 0;
public var y:Number = 0;
public var _animIndex:
int = 0; private var _count:int = 0;
public var animate:Boolean = true;
private var _whiteTransparent:BitmapData;
private var _envelopeAnimAry:Array;
private var _model:Object;
public function BlitSprite(fullSpriteSheet:BitmapData, envelopeAnimAry:Array, bitmapStage:BitmapData, model:Object = null)
{
_fullSpriteSheet = fullSpriteSheet;
_envelopeAnimAry = envelopeAnimAry;
_bitmapStage = bitmapStage;
_model= model;
init();
}
private function init():void
{
// _whiteTransparent = new BitmapData(100, 100, true, 0x80FFffFF);
this.addEventListener("ENV_ANIM_DONE", onEvnAnimDone);
}
protected function onEvnAnimDone(event:Event):void
{
}
public function render():void
{
// pos.x = x - _rects[_animIndex].width*.5;
// pos.y = y - _rects[_animIndex].width*.5;
// if (_count % 1 == 0 && animate == true)
// {
// trace("rendering");
if (_animIndex == (_envelopeAnimAry.length - 1) )
{
// _animIndex = 0;
dispatchEvent(new Event("ENV_ANIM_DONE", true));
animate = false;
// trace("!!!!animate over " + _model.animOver);
// if (_model != null)
// {
// _model.animOver = true;
// }
// trace("!!!!animate over " + _model.animOver);
}
else
{
_animIndex++;
}
pos.x = x + _envelopeAnimAry[_animIndex][1];
pos.y = y + _envelopeAnimAry[_animIndex][2];
_bitmapStage.copyPixels(_fullSpriteSheet, _envelopeAnimAry[_animIndex][0], pos, null, null, true);
}
}
}
// THIS PART HAPPENS WHEN PHONE'S SEND BUTTON IS CLICKED
_envelopeBlit.animate = true;
_envelopeBlit._animIndex = 0;
_darkSquare.visible = true;
_envelopePlayTimer.addEventListener(TimerEvent.TIMER, onEnterTimerFrame);
_envelopePlayTimer.start();
it also uses BlitConfig which stores the info about the spritesheet spit out by TexturePacker
package com.fs.pack.phone.configuration
{
import flash.geom.Rectangle;
public final class BlitConfig
{
public static var _sending_message_real_20001:Rectangle = new Rectangle(300,1020,144,102);
public static var _sending_message_real_20002:Rectangle = new Rectangle(452,1012,144,102);
public static var _sending_message_real_20003:Rectangle = new Rectangle(852,852,146,102);
public static var _sending_message_real_20004:Rectangle = new Rectangle(2,1018,146,102);
public static var _sending_message_real_20005:Rectangle = new Rectangle(702,822,148,102);
.
.
.
public static var _sending_message_real_20139:Rectangle = new Rectangle(932,144,1,1);
public static var envelopeAnimAry:Array = [
// rectangle, x offset, y offset
[ _sending_message_real_20001, 184,155],
[ _sending_message_real_20002, 184,155],
[ _sending_message_real_20003, 183,155],
[ _sending_message_real_20004, 183,155],
.
.
.
[ _sending_message_real_20139, 0,0]
]
public function BlitConfig()
{
}
}
}
EDIT: Knowing that this is not mobile, my answer below is irrelevant. I will leave it there, though, in case someone is having trouble with blitting on mobile in the future.
With regards to this specific question, you are running your timer every 5ms. First off, the lowest range that a Timer is accurate is >15ms so that will never be a viable solution. For any Timer relating to displaying soemthing on the stage, you should never do it less than a single frame. (
1000/stage.framerate
. ~40ms for a 30fps app)For blitting, the goal is to reduce calculations and rendering. The way you have this set up right now, it looks like you are blitting every 5ms. That is actually more than 8 times as often as the MovieClip is rendering. You should reduce how often you blit. Only do it when a change has actually been made beyond translation. Doing it any more often than that is overkill and the reason it is so slow (again, creating bitmaps is slow)
In general, you do not want to blit in an AIR for Mobile application (which I assume you are doing since you mentioned the phone being initialized). I'm not sure if it is okay to do it using other/native SDKs, but avoid it in AIR.
Essentially, it comes down to how blitting works. Blitting takes a screen capture and displays that on the stage rather than the actual object. In general, this is great. It means that your display objects, particularly vectors which are slow to render, have to render far less often. It is especially good when animating because an object tends to re-render every time it is translated in any way, but not a bitmap.
On mobile platforms, however, creating that bitmap is incredibly slow. I've never looked into how the SDK creates the Bitmaps, but it doesn't do it efficiently (it often makes me wonder if it does it pixel-by-pixel). On desktops, this is generally fine. There is plenty of CPU and plenty of RAM to make this happen quickly. On mobile, however, that luxury is not there at the moment. So when you blit and create that bitmap, it takes a while to run that process.
The problem is exacerbated on high-resolution screens. An app I developed from January to May of this year selectively used blitting to use filters in a GPU accelerated environment. On an iPad 2, the blitting took my app from 30fps to ~24fps. Not a big deal, not anything the user would notice. On an iPad 3 with retina display, however, it dropped down to 10fps. It makes sense when you think about it, as retina iPads have 4x as many pixels as non-retina iPads do.
If you do want to use blitting on mobile, I recommend a few things:
View
in debug mode to less than a frame (~40ms) to do it in release mode on an iPhone 4So in general, use blitting sparingly on mobile because bitmap creation is slow.