package com.actiontad.gameUtils
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.display.Shape;
import flash.utils.Timer;
import flash.events.TimerEvent;
import com.actiontad.gameUtils.RPEase;
/**
* com.actiontad.gameUtils.SimpleStaticTweener VERSION 1 A simple and lightweight frame based static Tweener class.
* Example use:
* SimpleStaticTweener.tweenToXY(myMovieClip, 5, 5, 10, "OutBounce");//obj,x,y,intervals,ease - starts from current x and y
*
* SimpleStaticTweener.tweenSpecific(myObj, "someNumericProperty", 15, 1, "Linear");//obj, property, end result, intervals, ease - starts from current value of property
*
* SimpleStaticTweener.tweenBatch(mySprite, {x:{start:5, end:10, interval:1, how:"OutBounce"}, y:{start:0, end:10, interval:2, how:"Linear"}});
*
*
* Intervals in this class are in milliseconds.
* The rate of any tween can not be faster than the frameRate. (and the real frame rate of the swf, if not using a timer)
*
*
*
* @author (t)ad
*/
public final class SimpleStaticTweener
{
public static var speed:int = 1;
/**
* When useTimer is set to true, this is the delay of the timer.
*
*/
public static var frameRate:Number = 30;
/**
* Must be set before first tween call if using true.
*
*/
public static var useTimer:Boolean;
public static var millisecondRate:Number = 1000;
private static var easeOptions:Array = [ "Linear", "InLinear", "OutLinear", "InOutLinear", "InElastic",
"OutElastic", "InOutElastic", "InQuad", "OutQuad", "InOutQuad",
"InBounce", "InOutBounce", "OutBounce", "InCirc", "InBack", "OutBack", "InOutBack",
"InQuint", "OutQuint", "InOutQuint", "OutCirc", "InOutCirc",
"In", "Out", "InOut", "InSine", "OutSine", "InOutSine" ];
private static var cache:Array = [];
private static var gatherTweeners:Event;
private static var tweenerCall:String = "recycle";
private static var loopSource:EventDispatcher;
private static var theLoop:Event;
private static var recycleMe:Event;
private static var globalCommand:String = "recycleTweeners";
private static var individualLoop:Event;
private static var iLoopDispatch:String = "imLooping";
private static var capitalLoop:String = "TheLoop";
private static var backs:String = "Back";
private static function nextRunCalc(toThis:Object):Function {
//this function is up here because it may well be a setter but just can not be.
var tw:Object = toThis;
var runCalcsForTw:Function = function (e:Event):void {
var tweenObj:Object;
for (var i:String in tw.propObject) {
tweenObj = tw.propObject[i];
if (tweenObj.finished == 0) {
if (tweenObj.index == tweenObj.values.length ) {
tweenObj.finished = 1;
} else {
tw.mover[i] = tweenObj.values[tweenObj.index];//calcs
tweenObj.index += 1;// speed;
}
}
}
var done:int = 1;
for (i in tw.propObject) {
if (tw.propObject[i].finished == 0) { done = 0; }
}
if (done == 1) {
e.target.removeEventListener(iLoopDispatch, tw.runCalcs);
if (tw.toBeCalled != null) { tw.toBeCalled(); }
tw.propObject = { };
tw.runCalcs = null;
loopSource.removeEventListener(capitalLoop, tw.callBack);
tw.callBack = null;
tw.mover = { };
var pickMe:Function = function(e:Event):void {
loopSource.removeEventListener(globalCommand, pickMe);
tw.evnd.dispatchEvent(recycleMe);
}; loopSource.addEventListener(globalCommand, pickMe);
}
}; return runCalcsForTw;
}
/**
* The constructor is not used.
*/
public function SimpleStaticTweener() {
//Instantiation is not needed. All relevant methods and properties are static.
}
private static function fillCacheQue():void {
if (!loopSource) {
cache = [];
if(useTimer) {
loopSource = new Timer(frameRate, 0);
loopSource.addEventListener(TimerEvent.TIMER, dispatchTheLoop);
(loopSource as Timer).start();
} else {
loopSource = new Shape();
loopSource.addEventListener(Event.ENTER_FRAME, dispatchTheLoop);
}
gatherTweeners = new Event("recycleTweeners");
recycleMe = new Event("recycle"); theLoop = new Event("TheLoop");
individualLoop = new Event("imLooping");
} loopSource.dispatchEvent(gatherTweeners);
if (cache.length == 0) {
var count:int = 10; while (count > 0) {
cache.push({evnd:new EventDispatcher()}); count--;
}
}
}
public static function deactivate(e:Event = null):void {
cache = [];
loopSource.removeEventListener(TimerEvent.TIMER, dispatchTheLoop);
loopSource.removeEventListener(Event.ENTER_FRAME, dispatchTheLoop);
if(loopSource as Timer) (loopSource as Timer).stop();
}
public static function tweenToXY(obj:Object, x:int = 1, y:int = 1, intervals:Number = 1, how:String = "Linear", thenCall:Function = null):void {
if ( cache.length <= 1 ) { fillCacheQue(); }
var tw:Object = cache.pop(); //get a tween spec holding object from the cache
var duration:Number = intervals;// int( frameOffsetCalc( intervals ) );
tw.mover = obj;//the object to move
tw.propObject = { };
if (obj.x is Number && obj.y is Number) { //make sure the object has at least x and y Number properties
tw.propObject.x = { start:obj.x, end:x, interval:duration, index:0, finished:0 }; // set the initial specs of the x tween
tw.propObject.y = { start:obj.y, end:y, interval:duration, index:0, finished:0 }; // set the initial specs of the y tween, these are 2d objects, or json like objects
tw.propObject.x.values = calcProps(tw.propObject.x.start, tw.propObject.x.end, tw.propObject.x.interval, how);// calculate points to cross
tw.propObject.y.values = calcProps(tw.propObject.y.start, tw.propObject.y.end, tw.propObject.y.interval, how);// this is the magic, caclProps is the heart of the whole system.
}
if (thenCall != null) { tw.toBeCalled = thenCall; } //if a after tween function is defined reference it for later
var cb:Function = function(e:Event):void {
tw.evnd.dispatchEvent(individualLoop);
}; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw);
loopSource.addEventListener(capitalLoop, tw.callBack);
tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack); //subscribe the tweener to the globaly dispatched recycling event
tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs);//setup the tween to run on the LoopEvent
return;
}
public static function tweenSpecific(obj:Object, propertyToTween:String, endAmount:Number, intervals:Number = 1, how:String = "Linear", thenCall:Function = null):void {
if ( cache.length <= 1 ) { fillCacheQue(); }
var tw:Object = cache.pop();
tw.mover = obj; tw.propObject = { };
var duration:Number = intervals;// int( frameOffsetCalc(intervals) );
tw.propObject[propertyToTween] = { start:obj[propertyToTween], end:endAmount, interval:duration, index:0, finished:0 };
tw.propObject[propertyToTween].values =
calcProps(tw.propObject[propertyToTween].start, tw.propObject[propertyToTween].end, tw.propObject[propertyToTween].interval, how);
if (thenCall != null) { tw.toBeCalled = thenCall; }
var cb:Function = function(e:Event):void {
tw.evnd.dispatchEvent(individualLoop);
}; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw);
loopSource.addEventListener(capitalLoop, tw.callBack);
tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack); //subscribe the tweener to the globaly dispatched recycling event
tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs);//setup the tween to run on the LoopEvent
return;
}
public static function tweenBatch(obj:Object, propertyBatch:Object, thenCall:Function = null):void {
if ( cache.length <= 1 ) { fillCacheQue(); }
var tw:Object = cache.pop();
tw.mover = obj; tw.propObject = { };
var duration:Number;
for (var i:* in propertyBatch) {
duration = propertyBatch[i].interval;
tw.propObject[i] =
{ start:propertyBatch[i].start, end:propertyBatch[i].end, interval:duration, how:propertyBatch[i].how, index:0, finished:0 };
tw.propObject[i].values =
calcProps(tw.propObject[i].start, tw.propObject[i].end, tw.propObject[i].interval, tw.propObject[i].how);
} if (thenCall != null) { tw.toBeCalled = thenCall; }
var cb:Function = function(e:Event):void {
tw.evnd.dispatchEvent(individualLoop);
}; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw);
loopSource.addEventListener(capitalLoop, tw.callBack);
tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack);
tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs);
return;
}
private static function pleaseTakeMeBack(e:Event):void {
var t:Object = e.target;
t.removeEventListener(tweenerCall, pleaseTakeMeBack);
cache.unshift({evnd:new EventDispatcher()});
}
public static function calcProps(start:Number, end:Number, interval:Number, how:String = "Linear"):Array {
var v:Array = []; var si:int = 0; var realHow:String = "Linear";
for (si = 0; si < easeOptions.length; si++) {
if ( easeOptions[si].toLowerCase() == how.toLowerCase() ) {
realHow = easeOptions[si]; break;
}
}
var ending:Number = end - start; si = 0;
var argsers:Array = [ ((realHow.indexOf(backs) != -1)?1.70158:0) , 0 ];
var secondIterations:int = int( frameOffsetCalc(interval) );
var milliRate:int = int(millisecondRate / frameRate);
var millisecondIterations:int = (interval / milliRate)>=1?int(interval/milliRate):1;
for (si = 0; si <= millisecondIterations; si++) {
v[si] = RPEase[realHow](si, start, ending, millisecondIterations, argsers[0], argsers[1]);//heart
} return v;
}
private static function dispatchTheLoop(e:Event = null):void {
if(e as TimerEvent) (e as TimerEvent).updateAfterEvent();
loopSource.dispatchEvent(theLoop);
}
public static function frameOffsetCalc(secondsWanted:Number = 1):Number {
var result:Number = ( (millisecondRate / frameRate) * secondsWanted );
return result;
}
}
}