Pre Rotated Spritemap
What it does: creates an animatable spritemap that is also fully rotatable. Essentially it for loops through each sprite, and creates a row of that sprite rotated how ever many times you want.
It also caches each one, so if you have 30 enemies on screen, they all draw from the same bitmapdata, rather than having 30 1200x1200 bmps in memory.
This is very very raw, undocumented, early code. I'm posting it for anyone who might enjoy having this feature, and will give help/update when I can.Modifications required:
In order for this to work you must create an interface(normal class as file) in a folder inside
net.flashpunk.graphics called
interfacesCreate an as file called
IAnimated.ascopy and paste this code inside:
package net.flashpunk.graphics.interfaces
{
import net.flashpunk.graphics.Anim;
/**
* ...
* @author Ryan Luce
*/
public interface IAnimated
{
function play(name:String = "", reset:Boolean = false):Anim;
}
}
Secondly, you must edit
net.flashpunk.graphics.Anim on line 60, changing
internal var _parent:Spritemap; to
internal var _parent:IAnimated; making sure you import IAnimated into Anim.asWhew, if you made it this far, you've got dedication! Enjoy the rewards

package net.flashpunk.graphics
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import net.flashpunk.FP;
import net.flashpunk.graphics.interfaces.IAnimated;
/**
* ...
* @author Ryan Luce
*/
public class SpritemapRotated extends Image implements IAnimated
{
public var frameAngle:Number = 0;
/**
* If the animation has stopped.
*/
public var complete:Boolean = true;
/**
* Optional callback function for animation end.
*/
public var callback:Function;
/**
* Animation speed factor, alter this to speed up/slow down all animations.
*/
public var rate:Number = 1;
// Rotation information.
/** @private */ private var _source:BitmapData;
/** @private */ private var _width:uint;
/** @private */ private var _frameCount:uint;
/** @private */ private var _last:int = -1;
/** @private */ private var _current:int = -1;
// Spritemap information.
/** @private */ private var _rect:Rectangle;
/** @private */ private var _height:uint;
/** @private */ private var _columns:uint;
/** @private */ private var _rows:uint;
/** @private */ private var _anims:Object = { };
/** @private */ private var _anim:Anim;
/** @private */ private var _index:uint;
/** @private */ private var _frame:uint;
/** @private */ private var _timer:Number = 0;
/** @private */ private var _numAnimationFrames:uint = 0;
/** @private */ private var _currentRow:uint = 0;
// Global information.
/** @private */ private static var _rotated:Dictionary = new Dictionary;
/** @private */ private static var _size:Dictionary = new Dictionary;
/** @private */ private static const _MAX_WIDTH:uint = 4000;
/** @private */ private static const _MAX_HEIGHT:uint = 4000;
private static var _cache:Vector.<BitmapData> = new Vector.<BitmapData>();
private static var _cacheKey:Array = [];
public function SpritemapRotated(source:Class, frameWidth:uint = 0, frameHeight:uint = 0, frameCount:uint = 36)
{
var tempSource:BitmapData = (new source).bitmapData;
var numRows:uint = Math.ceil(tempSource.width / frameWidth) * Math.ceil(tempSource.height / frameHeight);
_numAnimationFrames = numRows;
_rect = new Rectangle(0, 0, frameWidth, frameHeight);
_frameCount = frameCount;
if (_cacheKey[String(source)] == null)
{
_source = new BitmapData(frameCount * frameHeight, numRows * frameHeight, true, 0x00);
_size[source] = Math.ceil(FP.distance(0, 0, frameWidth, frameHeight));
_width = tempSource.width;
for (var a:uint = 0; a < numRows; a++)
{
doRotation(a, tempSource);
}
_cacheKey[String(source)] = _cache.length;
_cache.push(_source);
} else {
trace("using cached");
_source = _cache[_cacheKey[String(source)]];
}
var rectSize:uint = Math.max(_rect.width, _rect.height);
_rect.width = rectSize;
_rect.height = rectSize;
//FP.stage.addChild(new Bitmap(_source));
super(_source, _rect);
}
/**
* Updates the spritemap's buffer.
*/
override public function update():void
{
// get position of the current frame
_rect.x = _rect.width * uint(_frameCount * (frameAngle / 360));
_rect.y = _currentRow * _rect.height;
if (_flipped) _rect.x = (_width - _rect.width) - _rect.x;
// update the buffer
super.update();
}
/** @private Updates the animation and renders the Image. */
override public function render(point:Point, camera:Point):void
{
frameAngle %= 360;
if (frameAngle < 0) frameAngle += 360;
_current = _currentRow * _frameCount + uint(_frameCount * (frameAngle / 360));
if (_last != _current)
{
_last = _current;
_rect.x = _rect.width * _last;
_rect.y = uint(_rect.x / _width) * _rect.height;
_rect.x %= _width;
update();
}
// update the animation
if (_anim && !complete)
{
_timer += (FP.fixed ? _anim._frameRate : _anim._frameRate * FP.elapsed) * rate;
if (_timer >= 1)
{
while (_timer >= 1)
{
_timer --;
_index ++;
if (_index == _anim._frameCount)
{
if (callback != null) callback();
if (_anim._loop) _index = 0;
else
{
_index = _anim._frameCount - 1;
complete = true;
break;
}
}
}
_currentRow = uint(_anim._frames[_index])
_frame = _currentRow * _frameCount + uint(_frameCount * (frameAngle / 360));
update();
}
}
// render it to the screen
super.render(point, camera);
}
/**
* Add an Animation.
* @param name Name of the animation.
* @param frames Array of frame indices to animate through.
* @param frameRate Animation speed.
* @param loop If the animation should loop.
* @return A new Anim object for the animation.
*/
public function add(name:String, frames:Array, frameRate:Number = 0, loop:Boolean = true):Anim
{
if (_anims[name]) throw new Error("Cannot have multiple animations with the same name");
(_anims[name] = new Anim(name, frames, frameRate, loop))._parent = this;
return _anims[name];
}
/**
* Plays an animation.
* @param name Name of the animation to play.
* @param reset If the animation should force-restart if it is already playing.
* @return Anim object representing the played animation.
*/
public function play(name:String = "", reset:Boolean = false):Anim
{
if (!reset && _anim && _anim._name == name) return _anim;
_anim = _anims[name];
if (!_anim)
{
_frame = uint(_frameCount * (frameAngle / 360));
_currentRow = 0;
_index = 0;
complete = true;
update();
return null;
}
_index = 0;
_timer = 0;
_currentRow = _anim.frames[0];
_frame = _anim._frames[0] * _frameCount + uint(_frameCount * (frameAngle / 360));
complete = false;
update();
return _anim;
}
/**
* Sets the current frame index. When you set this, any
* animations playing will be stopped to force the frame.
*/
public function get frame():uint { return _frame; }
public function set frame(value:uint):void
{
_currentRow = value;
_anim = null;
value = value * _frameCount + uint(_frameCount * (frameAngle / 360));
//value %= _frameCount;
if (_frame == value) return;
_frame = value ;
update();
}
/**
* Current index of the playing animation.
*/
public function get index():uint { return _anim ? _index : 0; }
public function set index(value:uint):void
{
if (!_anim) return;
value %= _anim._frameCount;
if (_index == value) return;
_index = value;
_frame = uint(_anim._frames[_index]);
update();
}
private function doRotation(index:uint, drawFrom:BitmapData):void
{
var rectSize:uint = Math.max(_rect.width, _rect.height);
var width:uint = rectSize * _frameCount,
height:uint = rectSize,
yOffset:uint = index * rectSize;
var tempRect:Rectangle = new Rectangle(0, 0, _rect.width, _rect.height);
tempRect.x = tempRect.width * index;
tempRect.y = uint(tempRect.x / drawFrom.width) * tempRect.height;
tempRect.x %= _width;
var temp:BitmapData = new BitmapData(_rect.width, _rect.height);
temp.copyPixels(drawFrom, tempRect,new Point(0,0));
var m:Matrix = FP.matrix,
a:Number = 0,
aa:Number = (Math.PI * 2) / -_frameCount,
ox:uint = temp.width / 2,
oy:uint = temp.height / 2,
o:uint = rectSize / 2,
x:uint = 0,
y:uint = index * rectSize;
while (x < width)
{
m.identity();
m.translate(-ox, -oy);
m.rotate(a);
m.translate(o + x, o + y);
_source.draw(temp, m, null, null, null, smooth);
x += rectSize;
a += aa;
}
}
}
}