All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.starling.animation.Juggler.as Maven / Gradle / Ivy

// =================================================================================================
//
//	Starling Framework
//	Copyright 2011-2015 Gamua. All Rights Reserved.
//
//	This program is free software. You can redistribute and/or modify it
//	in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================

package starling.animation
{
    import flash.utils.Dictionary;

    import starling.core.starling_internal;
    import starling.events.Event;
    import starling.events.EventDispatcher;

    /** The Juggler takes objects that implement IAnimatable (like Tweens) and executes them.
     * 
     *  

A juggler is a simple object. It does no more than saving a list of objects implementing * "IAnimatable" and advancing their time if it is told to do so (by calling its own * "advanceTime"-method). When an animation is completed, it throws it away.

* *

There is a default juggler available at the Starling class:

* *
     *  var juggler:Juggler = Starling.juggler;
     *  
* *

You can create juggler objects yourself, just as well. That way, you can group * your game into logical components that handle their animations independently. All you have * to do is call the "advanceTime" method on your custom juggler once per frame.

* *

Another handy feature of the juggler is the "delayCall"-method. Use it to * execute a function at a later time. Different to conventional approaches, the method * will only be called when the juggler is advanced, giving you perfect control over the * call.

* *
     *  juggler.delayCall(object.removeFromParent, 1.0);
     *  juggler.delayCall(object.addChild, 2.0, theChild);
     *  juggler.delayCall(function():void { rotation += 0.1; }, 3.0);
     *  
* * @see Tween * @see DelayedCall */ public class Juggler implements IAnimatable { private var _objects:Vector.; private var _objectIDs:Dictionary; private var _elapsedTime:Number; private static var sCurrentObjectID:uint; /** Create an empty juggler. */ public function Juggler() { _elapsedTime = 0; _objects = new []; _objectIDs = new Dictionary(true); } /** Adds an object to the juggler. * * @return Unique numeric identifier for the animation. This identifier may be used * to remove the object via removeByID(). */ public function add(object:IAnimatable):uint { return addWithID(object, getNextID()); } private function addWithID(object:IAnimatable, objectID:uint):uint { if (object && !(object in _objectIDs)) { var dispatcher:EventDispatcher = object as EventDispatcher; if (dispatcher) dispatcher.addEventListener(Event.REMOVE_FROM_JUGGLER, onRemove); _objects[_objects.length] = object; _objectIDs[object] = objectID; return objectID; } else return 0; } /** Determines if an object has been added to the juggler. */ public function contains(object:IAnimatable):Boolean { return object in _objectIDs; } /** Removes an object from the juggler. * * @return The (now meaningless) unique numeric identifier for the animation, or zero * if the object was not found. */ public function remove(object:IAnimatable):uint { var objectID:uint = 0; if (object && object in _objectIDs) { var dispatcher:EventDispatcher = object as EventDispatcher; if (dispatcher) dispatcher.removeEventListener(Event.REMOVE_FROM_JUGGLER, onRemove); var index:int = _objects.indexOf(object); _objects[index] = null; objectID = _objectIDs[object]; delete _objectIDs[object]; } return objectID; } /** Removes an object from the juggler, identified by the unique numeric identifier you * received when adding it. * *

It's not uncommon that an animatable object is added to a juggler repeatedly, * e.g. when using an object-pool. Thus, when using the remove method, * you might accidentally remove an object that has changed its context. By using * removeByID instead, you can be sure to avoid that, since the objectID * will always be unique.

* * @return if successful, the passed objectID; if the object was not found, zero. */ public function removeByID(objectID:uint):uint { for (var i:int=_objects.length-1; i>=0; --i) { var object:IAnimatable = _objects[i]; if (_objectIDs[object] == objectID) { remove(object); return objectID; } } return 0; } /** Removes all tweens with a certain target. */ public function removeTweens(target:Object):void { if (target == null) return; for (var i:int=_objects.length-1; i>=0; --i) { var tween:Tween = _objects[i] as Tween; if (tween && tween.target == target) { tween.removeEventListener(Event.REMOVE_FROM_JUGGLER, onRemove); _objects[i] = null; delete _objectIDs[tween]; } } } /** Removes all delayed and repeated calls with a certain callback. */ public function removeDelayedCalls(callback:Function):void { if (callback == null) return; for (var i:int=_objects.length-1; i>=0; --i) { var delayedCall:DelayedCall = _objects[i] as DelayedCall; if (delayedCall && delayedCall.callback == callback) { delayedCall.removeEventListener(Event.REMOVE_FROM_JUGGLER, onRemove); _objects[i] = null; delete _objectIDs[tween]; } } } /** Figures out if the juggler contains one or more tweens with a certain target. */ public function containsTweens(target:Object):Boolean { if (target) { for (var i:int=_objects.length-1; i>=0; --i) { var tween:Tween = _objects[i] as Tween; if (tween && tween.target == target) return true; } } return false; } /** Figures out if the juggler contains one or more delayed calls with a certain callback. */ public function containsDelayedCalls(callback:Function):Boolean { if (callback != null) { for (var i:int=_objects.length-1; i>=0; --i) { var delayedCall:DelayedCall = _objects[i] as DelayedCall; if (delayedCall && delayedCall.callback == callback) return true; } } return false; } /** Removes all objects at once. */ public function purge():void { // the object vector is not purged right away, because if this method is called // from an 'advanceTime' call, this would make the loop crash. Instead, the // vector is filled with 'null' values. They will be cleaned up on the next call // to 'advanceTime'. for (var i:int=_objects.length-1; i>=0; --i) { var object:IAnimatable = _objects[i]; var dispatcher:EventDispatcher = object as EventDispatcher; if (dispatcher) dispatcher.removeEventListener(Event.REMOVE_FROM_JUGGLER, onRemove); _objects[i] = null; delete _objectIDs[object]; } } /** Delays the execution of a function until delay seconds have passed. * This method provides a convenient alternative for creating and adding a DelayedCall * manually. * * @return Unique numeric identifier for the delayed call. This identifier may be used * to remove the object via removeByID(). */ public function delayCall(call:Function, delay:Number, ...args):uint { if (call == null) throw new ArgumentError("call must not be null"); var delayedCall:DelayedCall = DelayedCall.starling_internal::fromPool(call, delay, args); delayedCall.addEventListener(Event.REMOVE_FROM_JUGGLER, onPooledDelayedCallComplete); return add(delayedCall); } /** Runs a function at a specified interval (in seconds). A 'repeatCount' of zero * means that it runs indefinitely. * * @return Unique numeric identifier for the delayed call. This identifier may be used * to remove the object via removeByID(). */ public function repeatCall(call:Function, interval:Number, repeatCount:int=0, ...args):uint { if (call == null) throw new ArgumentError("call must not be null"); var delayedCall:DelayedCall = DelayedCall.starling_internal::fromPool(call, interval, args); delayedCall.repeatCount = repeatCount; delayedCall.addEventListener(Event.REMOVE_FROM_JUGGLER, onPooledDelayedCallComplete); return add(delayedCall); } private function onPooledDelayedCallComplete(event:Event):void { DelayedCall.starling_internal::toPool(event.target as DelayedCall); } /** Utilizes a tween to animate the target object over time seconds. Internally, * this method uses a tween instance (taken from an object pool) that is added to the * juggler right away. This method provides a convenient alternative for creating * and adding a tween manually. * *

Fill 'properties' with key-value pairs that describe both the * tween and the animation target. Here is an example:

* *
         *  juggler.tween(object, 2.0, {
         *      transition: Transitions.EASE_IN_OUT,
         *      delay: 20, // -> tween.delay = 20
         *      x: 50      // -> tween.animate("x", 50)
         *  });
         *  
* *

To cancel the tween, call 'Juggler.removeTweens' with the same target, or pass * the returned 'IAnimatable' instance to 'Juggler.remove()'. Do not use the returned * IAnimatable otherwise; it is taken from a pool and will be reused.

* *

Note that some property types may be animated in a special way:

*
    *
  • If the property contains the string color or Color, * it will be treated as an unsigned integer with a color value * (e.g. 0xff0000 for red). Each color channel will be animated * individually.
  • *
  • The same happens if you append the string #rgb to the name.
  • *
  • If you append #rad, the property is treated as an angle in radians, * making sure it always uses the shortest possible arc for the rotation.
  • *
  • The string #deg does the same for angles in degrees.
  • *
*/ public function tween(target:Object, time:Number, properties:Object):uint { if (target == null) throw new ArgumentError("target must not be null"); var tween:Tween = Tween.starling_internal::fromPool(target, time); for (var property:String in properties) { var value:Object = properties[property]; if (tween.hasOwnProperty(property)) tween[property] = value; else if (target.hasOwnProperty(Tween.getPropertyName(property))) tween.animate(property, value as Number); else throw new ArgumentError("Invalid property: " + property); } tween.addEventListener(Event.REMOVE_FROM_JUGGLER, onPooledTweenComplete); return add(tween); } private function onPooledTweenComplete(event:Event):void { Tween.starling_internal::toPool(event.target as Tween); } /** Advances all objects by a certain time (in seconds). */ public function advanceTime(time:Number):void { var numObjects:int = _objects.length; var currentIndex:int = 0; var i:int; _elapsedTime += time; if (numObjects == 0) return; // there is a high probability that the "advanceTime" function modifies the list // of animatables. we must not process new objects right now (they will be processed // in the next frame), and we need to clean up any empty slots in the list. for (i=0; i { return _objects; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy