In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Boid for Android

A quick demonstration of how different devices can be targeted with the same codebase

Credits: 
Boid adapted from a BIONIC SIMULATION (C) 2001 - Luis Pabon (luis@pabon.com) made available on FlashKit. 
Original mentions that "You can freely use and modify this code but please, give credit to the author"
- Ported to AS3
- Added logic to support different types of controllers (point and click vs accelerometer)
 *
How to use:
- In accelerometer supported environments, move your device around. 
- Elsewhere, move your mouse on the blue part of the screen
Get Adobe Flash player
by widged 08 Jan 2011
/**
 * Copyright widged ( http://wonderfl.net/user/widged )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/bQJS
 */

/**
 * Boid for Android
 * 
 * A quick demonstration of how different devices can be targeted with the same codebase
 * 
 * Credits: 
 * Boid adapted from a BIONIC SIMULATION (C) 2001 - Luis Pabon (luis@pabon.com) made available on FlashKit. 
 * Original mentions that "You can freely use and modify this code but please, give credit to the author"
 * - Ported to AS3
 * - Added logic to support different types of controllers (point and click vs accelerometer)
 *
 * How to use:
 * - In accelerometer supported environments, move your device around. 
 * - Elsewhere, move your mouse on the blue part of the screen
 */
package
{
    import flash.display.Sprite;
    import flash.events.EventDispatcher;
    import flash.sensors.Accelerometer;
    
    [SWF(width = "480", height = "800", backgroundColor='#FFFFFF')]
    public class BoidFishWonderfl extends Sprite
    {
        
        private var gameScreen:GameScreen;
        private var gameDispatcher:EventDispatcher;
        private var gameController:IDeviceController;

        public function BoidFishWonderfl()
        {
            gameDispatcher  = new EventDispatcher();
            gameController = (Accelerometer.isSupported) ? new AndroidController(gameDispatcher) : new DefaultController(gameDispatcher);
            gameScreen       = new GameScreen();
            gameScreen.gameController = gameController;
            addChild(gameScreen);
        }
    }
}
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.AccelerometerEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.sensors.Accelerometer;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;

class GameScreen extends Sprite
{
    
    // controllers
    private var _controller:IDeviceController;
    private var boidFish:Boid;

    public function GameScreen()
    {
        var shape:Shape = new Shape();
        shape.graphics.beginFill(0x204A87,0.8);
        shape.graphics.drawRect(0,0,480, 762);
        addChild(shape);
    }

    public function set gameController(value:IDeviceController):void
    {
        _controller = value;
        _controller.init(this);
    }
    
    public function addBoid(boid:Boid):void
    {
        boidFish = boid;
        addChild(boid);
    }
    public function updateTarget(pt:Point):void
    {
        var screenRect:Rectangle = _controller.screenRectangle;
        if(pt.x < screenRect.left) { pt.x = screenRect.left; } else if(pt.x > screenRect.right)  { pt.x = screenRect.right; }
        if(pt.y < screenRect.top)  { pt.y = screenRect.top;  } else if(pt.y > screenRect.bottom) { pt.y = screenRect.bottom; }
        boidFish.upateTarget(pt);
    }
        
}

// ########################
// #  Boids 
// ########################
class Boid extends Sprite
{
    
    private var nodeList:Array = [];
    private var firstNode:BoidFirstNode
    
    public function Boid(spriteList:Array)
    {
        super();
        init(spriteList);
    }
    
    /**
     * initialisation
     */
    private function init(spriteList:Array):void
    {
        var i:int;
        nodeList = [];
        
        var node:BoidNode;
        var previousNode:BoidNode;
        
        // We built the creature:
        previousNode = null; 
        for ( i = 0; i < spriteList.length; i++) {
            var sprite:Sprite = spriteList[i] as Sprite;
            if(i == 0)
                node = firstNode = new BoidFirstNode(sprite);
            else
                node = new BoidNode(sprite, previousNode);
            nodeList.push(node);
            previousNode = node;
            this.addChild(sprite);
        }
    }
    
    
    public function upateTarget(pt:Point):void
    {
        // Moves the head towards the mouse cursor:
        firstNode.targetPosition = pt;
        for(var i:int = 0; i < nodeList.length; i++)
        {
            (nodeList[i] as BoidNode).refresh();
        }
    }
    
    public function hitTestPick(pick:DisplayObject):Boolean
    {
        
        return (nodeList[1] as BoidNode).hitTestObject(pick);
    }
    
    
}



class BoidNode
{
    
    // parameters
    private var _scaleFactor:Number = 0.96;
    private var _alphaFactor:Number = 0.95;
    private var _compactFactor:Number = 2;    // = compactness (depends on R)
    
    // class variables
    protected var _sprite:Sprite;
    protected var _targetPosition:Point = new Point(0,0);
    protected var _scale:Number = 1;
    protected var _alpha:Number = 1;
    private var _previousNode:BoidNode;        
    
    public function BoidNode(sprite:Sprite, previousNode:BoidNode)
    {
        _sprite = sprite;
        _previousNode = previousNode;
    }
    
    public function refresh():void
    {
        if(!_sprite) { return; }
        if(_previousNode) { 
            // We reduce size a bit, and reduce the opacity of the tail:
            _scale = _previousNode.scaleFactor * _scaleFactor;
            _alpha  = _previousNode.alphaFactor * _alphaFactor;
        }
        _sprite.scaleX = _sprite.scaleY = _scale;
        _sprite.alpha = _alpha;
        
        if(_previousNode) { 
            // compute target
            _targetPosition.x += (_previousNode.targetPosition.x - _targetPosition.x) / _compactFactor;
            _targetPosition.y += (_previousNode.targetPosition.y - _targetPosition.y) / _compactFactor;
            // updateposition
            var targetLoc:Point = _previousNode.targetPosition;
        }            
        
        _sprite.x = (targetLoc.x + _targetPosition.x) / 2;
        _sprite.y = (targetLoc.y + _targetPosition.y) / 2;;
        // And we calculate the right orientation of each piece:
        _sprite.rotation = 57.295778*Math.atan2((_targetPosition.y-targetLoc.y),(_targetPosition.x-targetLoc.x));
    }
    
    
    
    public function get scaleFactor():Number { return _scale; }
    public function get alphaFactor():Number { return _alpha; }
    public function get targetPosition():Point { return _targetPosition; }
    
    public function hitTestObject(object:DisplayObject):Boolean
    {
        if(!_sprite) { return false;}
        return _sprite.hitTestObject(object);    
    }
    
    
}

class BoidFirstNode extends BoidNode
{
    private var R:Number = 10;    // = how slow the fish follows the mouse for firstNode only
    private var _previousPosition:Point;
    
    public function BoidFirstNode(sprite:Sprite)
    {
        super(sprite, null);
    }
    
    public function set targetPosition(pt:Point):void
    {
        _previousPosition = _targetPosition
        var posX:Number = Math.round(_previousPosition.x + (pt.x - _previousPosition.x ) / R);
        var posY:Number = Math.round(_previousPosition.y + (pt.y - _previousPosition.y ) / R);
        _targetPosition = new Point(posX, posY);
        refresh();
    }
    
    override public function refresh():void
    {
        if(!_sprite) { return; }
        _sprite.scaleX = _sprite.scaleY = _scale;
        _sprite.alpha = _alpha;
        
        _sprite.x = (_previousPosition.x + _targetPosition.x) / 2;
        _sprite.y = (_previousPosition.y + _targetPosition.y) / 2;;
        // And we calculate the right orientation of each piece:
        _sprite.rotation = 57.295778*Math.atan2((_previousPosition.y- _targetPosition.y),(_previousPosition.x-_targetPosition.x));
    }
    
}

class BoidUtils
{
    /**
     * Construct the list of images
     */
    public static function makeSpriteList(nodeQty:int, DefaultImageClass:Class, extraImages:Array):Array
    {
        var arr:Array = [];
        var sprite:Sprite;
        for (var i:int = 0; i < nodeQty ; i++ ) 
        {
            sprite = new DefaultImageClass();
            arr[i] = sprite;
        }        
        
        for each(var part:Object in extraImages)
        {
            var posList:Array = part.positions;
            for (var j:int = 0; j < posList.length; j++)
            {
                var idx:int = posList[j];
                var ImgClass:Class = part.image as Class;
                sprite = new ImgClass();
                arr[idx] = sprite;
            }
        }
        return arr;
    }    
}



// ########################
// #  Devices Controllers 
// #
// #  Provide different behaviors depending on the device the game runs on
// #  Touch if accelerometer is supported, point and click otherwise
// ########################
interface IDeviceController
{
    function get dispatcher():IEventDispatcher;
    function get screenRectangle():Rectangle;
    function init(gameScreen:GameScreen):void;
    
}

class GameController implements IDeviceController
{
    protected var eventTarget:IEventDispatcher;
    protected var gameScreen:GameScreen;
    protected var screenRect:Rectangle; 
    // status
    private var gameStatus:int = STATUS_LOADING; 
    private const STATUS_LOADING:int    = 0;
    private const STATUS_STARTING:int   = 1;
    private const STATUS_PLAYING:int    = 2;
    private const STATUS_PAUSED:int     = 3;
    private const STATUS_END:int        = 4;
    // images to be used as sprite
    private var Head:Class;
    private var Spine:Class;
    private var Fin:Class;
    
    public function GameController(target:IEventDispatcher)
    {
        eventTarget = target;
    }
    
    public function get screenRectangle():Rectangle     { return screenRect; }
    public function get dispatcher():IEventDispatcher   { return eventTarget; }

    
    // :TODO: could be more decoupled by getting the gamescreen reference out of here 
    // and have everything go by way of events. 
    public function init(screen:GameScreen):void
    {
        gameScreen = screen;
        loadAssets();
    }

    protected function deviceInit():void
    {
        throw new Error("must be overloaded by inheriting classes")
    }
    
    private function changeStatus(id:int):void
    {
        gameStatus = id;
    }
    
    private function loadAssets():void
    {
        gameStatus = STATUS_LOADING;
        var loader:Loader = new Loader();
        loader.load(new URLRequest("http://widged.com/labs/wonderfl/bionicFish.swf"), new LoaderContext(true, null, SecurityDomain.currentDomain));
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete)
    }
    
    private function onLoadComplete(event:Event):void
    {
        var loaderInfo:LoaderInfo = LoaderInfo(event.target);
        Head = Class(loaderInfo.applicationDomain.getDefinition("Head"));
        Spine = Class(loaderInfo.applicationDomain.getDefinition("Spine"));
        Fin = Class(loaderInfo.applicationDomain.getDefinition("Fin"));
        start();
    }
    
    private function start():void
    {
        gameStatus = STATUS_STARTING;
        var spriteList:Array = BoidUtils.makeSpriteList(20, Spine, [{image: Head, positions: [0]},{image: Fin, positions: [3, 13]}]);
        var boid:Boid = new Boid(spriteList);
        gameScreen.addBoid(boid);

        //     pickList = PickableUtils.generateSpriteList(5);
        //    PickableUtils.addOntoDisplayObject(gameContainer, pickList, screenRect);

        deviceInit();
        gameStatus = STATUS_PLAYING;
    }
    
    protected function updateTarget(pt:Point):void
    {
        gameScreen.updateTarget(pt);
    }
    
    private function testAssets():void
    {
        // test
        var head:DisplayObject = DisplayObject(new Head());
        gameScreen.addChild(head);
        var spine:DisplayObject = DisplayObject(new Spine());
        spine.x = 40;
        gameScreen.addChild(spine);
        var fin:DisplayObject = DisplayObject(new Fin());
        fin.x = 80;
        gameScreen.addChild(fin);
    }

}

class AndroidController extends GameController implements IDeviceController
{
    
    private var boidTarget:Point = new Point(200,200);
    private var accelerometer:Accelerometer;
    private var sensitivity:int = 20;    
    
    public function AndroidController(target:IEventDispatcher)
    {
        super(target);
        screenRect = new Rectangle(0, 0, 480, 762);
    }
    
    
    override protected function deviceInit():void
    {
        accelerometer  = new Accelerometer();
        accelerometer.addEventListener(AccelerometerEvent.UPDATE, onAccelerometerChange);
        accelerometer.setRequestedUpdateInterval(40);
    }
    
    
    // #### Accelerometer Movement
    public function onAccelerometerChange(event:AccelerometerEvent):void {
        var ax:Number = (event.accelerationX*sensitivity)*-1;
        var ay:Number = (event.accelerationY*sensitivity);
        var az:Number = event.accelerationZ;
        var tx:Number = boidTarget.x + ax;
        var ty:Number = boidTarget.y + ay;
        boidTarget = new Point(tx, ty);
        super.updateTarget(boidTarget)
    }
}

class DefaultController extends GameController implements IDeviceController
{
    public function DefaultController(target:IEventDispatcher)
    {
        super(target);
        screenRect = new Rectangle(0, 0, 480, 762);
    }
    override protected function deviceInit():void
    {
        gameScreen.addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    private function onEnterFrame(event:Event):void
    {
        var pt:Point = new Point(gameScreen.mouseX, gameScreen.mouseY);
        super.updateTarget(pt)
    }    
}