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

Boids test 2

Get Adobe Flash player
by j000 21 Oct 2011
// forked from j000's Boids test (forked from: forked from: Boid)
// forked from nyonyonyo's forked from: Boid
// forked from wanson's Boid

package {
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.filters.GlowFilter;
    import flash.filters.DropShadowFilter;
    import flash.utils.Timer;
    import flash.utils.getTimer;
    import flash.events.TimerEvent;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.geom.Point;
    import net.hires.debug.Stats;
    
    [SWF(width="600", height="600", frameRate="40", backgroundColor="0xddddff")]
    public class BoidDemo extends Sprite {
        private const NUMBOIDS:Number = 25;
        private const SIZE:int = stage.stageHeight/50;
        private const tf:TextFormat = new TextFormat("Lucida Console", SIZE);
        private var boids:Array = new Array();
        private var sprites:Array = new Array();
        private var speeds:Array = new Array();
        private var fail:Array = new Array();
        private var steps:int = 0, fails:int = 0, stepsfield:TextField, failsfield:TextField, timefield:TextField;
        private var gotarget:Boolean = false, target:Point = new Point(-1, -1);
        private var filter:DropShadowFilter = new DropShadowFilter(8, 45, 0, 0.8), filterset:Array = [filter];
        private var move:Boolean = false, moves:int = 0;
        private var drawer:Sprite;
        private var bmpdata:BitmapData;
        
        private var timer:int, timer2:int, fps:int;
        private var b:Boid, i:int;

        public function BoidDemo() {
            Wonderfl.capture_delay(40);
            inittrace(stage);
            stage.quality = "LOW";
            
            bmpdata = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00ffffff);
            this.addChild(new Bitmap(bmpdata));
            
            var tmpw:int = stage.stageWidth/2, tmph:int = stage.stageHeight/2;
            var tmp:Number = 2.0 * Math.PI / NUMBOIDS;
            for (i = 0; i < NUMBOIDS; ++i) {
                const ph:Number = i * tmp;
                b = new Boid(i, tmpw*2, tmph*2,
                    tmpw + ((i%4) * 0.2 + 0.3)*tmpw * Math.cos(ph),
                    tmph + ((i%4) * 0.2 + 0.3)*tmph * Math.sin(ph),
                    ((i%4)*(-4) + 16) * Math.cos(ph + Math.PI / 6 * (1+i%4) * (Math.random() - 0.5)),
                    ((i%4)*(-4) + 16) * Math.sin(ph + Math.PI / 6 * (1+i%4) * (Math.random() - 0.5)));
                boids[i] = b;
                fail[i] = false;
                sprites[i] = new Sprite();
                //sprites[i].filters = filterset;
                var g:Graphics = sprites[i].graphics;
                // Draw view range
                g.lineStyle(1, 0xffffff);
                g.moveTo(0,0);
                for (var j:Number = -1; j <= 1; j += 0.01) {
                    g.lineTo(Math.cos(j * b.myr)*b.perception, Math.sin(j * b.myr)*b.perception);
                }
                g.lineTo(0,0);
                g.drawCircle(0, 0, b.mindist);
                
                //where I'm going
                g.moveTo(0, -b.mindist/2-4);
                g.lineTo(b.perception, -b.mindist/2-4);
                g.lineTo(b.perception, b.mindist/2+4);
                g.lineTo(0, b.mindist/2+4);

                g.lineStyle(0.1, 0x0055ff);
                g.beginFill(0x0055ff);
                g.moveTo(4, 0);
                g.lineTo(-3, -3);
                g.lineTo(-1, 0);
                g.lineTo(-3, 3);
                g.lineTo(4, 0);
                g.endFill();

                speeds[i] = new Sprite();
                speeds[i].addChild(sprites[i]);
                this.addChild(speeds[i]);
                
                failsfield = new TextField();
                failsfield.defaultTextFormat = tf;
                failsfield.x -= 2;
                failsfield.y -= 2;
                b.text = failsfield;
                speeds[i].addChild(b.text);
            }
            drawer = new Sprite();
            this.addChild(drawer);

            stepsfield = new TextField();
            stepsfield.defaultTextFormat = tf;
            stepsfield.autoSize = "right";
            stepsfield.x = stage.stageWidth-5;
            this.addChild(stepsfield);

            failsfield = new TextField();
            failsfield.defaultTextFormat = tf;
            failsfield.autoSize = "right";
            failsfield.x = stage.stageWidth-5;
            failsfield.y = SIZE;
            this.addChild(failsfield);
            
            timefield = new TextField();
            timefield.defaultTextFormat = tf;
            timefield.autoSize = "right";
            timefield.x = stage.stageWidth-5;
            timefield.y = 2*SIZE;
            this.addChild(timefield);

            var myTimer:Timer = new Timer(10, 0);
            myTimer.addEventListener("timer", timerHandler, false, 99);
            do {
                fails = 0;
                step(false);
            } while ((fails > 0) && (steps < 150))
            fails = 0;
            move = true;
            
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 100);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
            myTimer.start();
        }

        public function onEnterFrame (ev:Event) : void {
            redraw();
        }

        public function onMouseDown (ev:MouseEvent) : void {
            move = !move;
        }
        
        public function onMouseWheel (ev:MouseEvent) : void {
            moves += Math.abs(ev.delta / 3);
        }

        public function timerHandler (ev:TimerEvent) : void {
            timer2 = getTimer();
            timefield.text = (timer2 - timer).toString();
            timer = timer2;
            if (move || moves > 0) {
                step();
                --moves;
            } else if (moves < 0) {
                moves = 0;
            }
            timer2 = getTimer();
            timefield.text = "Timers: "+(timer2 - timer).toString() + " (" + timefield.text + ")";
        }
        
        public function redraw() : void {
            stepsfield.text = "Steps: " + steps.toString();
            failsfield.text = "Fails: " + (fails).toString() + " (1/"+(steps/fails).toFixed(0)+" steps)";
            for (i = 0; i < NUMBOIDS; ++i) {
                b = boids[i];
                speeds[i].x = b.px;
                speeds[i].y = b.py;
                speeds[i].graphics.clear();
                speeds[i].graphics.lineStyle(1, 0xFF0000);speeds[i].graphics.moveTo(0, 0);speeds[i].graphics.lineTo(b.vx, b.vy);
                speeds[i].graphics.lineStyle(1, 0x00FF00);speeds[i].graphics.moveTo(0, 0);speeds[i].graphics.lineTo(b.ax*40, b.ay*40);
                speeds[i].graphics.lineStyle(1, 0xFF00FF);speeds[i].graphics.moveTo(0, 0);speeds[i].graphics.lineTo(b.tmpx*40, b.tmpy*40);
                sprites[i].rotation = b.v.angle * 180 / Math.PI;
            }
        }

        public function step(draw:Boolean = true) : void {
            ++steps;
            if (draw)
                drawer.graphics.clear();
            //select neighbors
            for (i = 0; i < boids.length; ++i) {
                for (var j:int = i+1; j < boids.length; ++j) {
                    var dist:Vector3 = boids[i].p.subtract(boids[j].p);
                    if (dist.lengthSquared < boids[i].mydistSquared) {
                        if (dist.lengthSquared < boids[i].mindistSquared*4) {
                            ++fails;
                            //move = false;
                        }
                        var angle:Number = Math.abs(boids[i].v.angleBetween(dist));
                        if (angle < boids[i].myr) {
                            //add j to i.neighbors
                        }
                        angle = Math.abs(boids[j].v.angleBetween(dist));
                        if (angle < boids[j].myr) {
                            //add i to j.neighbors
                        }
                    }
                }
            }
            for each (b in boids) {
                b.force(boids,draw,drawer.graphics);
            }
            for each (b in boids) {
                b.update(draw,drawer.graphics);
            }
        }
    }
}

import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.Stage;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.geom.Vector3D;

function sign(arg:Number):int {
    return (arg > 0) ? 1 : ((arg < 0) ? -1 : 0);
}

class Boid {
    // parameters
    public var maxspeed:Number = 32,
                minspeed:Number = 8,
                mindist:Number = 8,
                perception:Number = 75,
                minTime:Number = 10.0/2,
                myr:Number = 2/3 * Math.PI,
                mindistSquared:int,
                mydistSquared:int;
                
                
    public var text:TextField;
                
    // necessary calculated parameters
    private var maxx:int,
                maxy:int,
                minx:int,
                miny:int,
                no:int;
    
    // needed variables
    private var _p:Vector3,
                _v:Vector3,
                _a:Vector3,
                tmp:Vector3,
                rangle:Number;
    
    // variables for functions
    // predictTime:
    private var relP:Vector3,
                relV:Vector3;
    // force:
    private var _aold:Vector3,
                dist:Vector3,
                angle:Number,
                b:Boid,
                time:Number = 0,
                mypos:Vector3,
                hispos:Vector3,
                separation:Vector3,
                alignment:Vector3,
                cohesion:Vector3,
                collision:Vector3,
                collisionLen:Number;
    
    //gets:
    public function get tmpx():Number { return tmp.x; }
    public function get tmpy():Number { return tmp.y; }
    public function get px():Number { return _p.x; }
    public function get py():Number { return _p.y; }
    public function get p():Vector3 { return _p.clone(); }
    public function get vx():Number { return _v.x; }
    public function get vy():Number { return _v.y; }
    public function get v():Vector3 { return _v.clone(); }
    public function get ax():Number { return _a.x; }
    public function get ay():Number { return _a.y; }
    public function get a():Vector3 { return _a.clone(); }
    public function get n():int { return no; }
    
    public function Boid(no2:int = 0, maxx2:int = 400, maxy2:int = 400, px:Number = 200, py:Number = 200, vx:Number = 1, vy:Number = 0) {
        _p = new Vector3(px, py);
        _v = new Vector3(vx, vy);
        _a = new Vector3();
        tmp = new Vector3();
        separation = new Vector3();
        alignment = new Vector3();
        cohesion = new Vector3();
        no = no2;
        maxx = maxx2-perception/2;
        maxy = maxy2-perception/2;
        minx = perception/2;
        miny = perception/2;
        mindistSquared = mindist*mindist;
        mydistSquared = perception*perception;
        rangle = Math.random() * 2 * Math.PI;
    }
    
    private function predictTime(other:Boid) : Number {
        relP = _p.subtract(other.p);
        relV = other.v.subtract(_v);
        return relV.dotProduct(relP) / relV.lengthSquared;
    }
    
    public function force(boids:Array, draw:Boolean, g:Graphics) : void {
        var count:int = 0, count2:int = 0;
        var minTime:Number = this.minTime;
        
        tmp.reset();
        separation.reset();
        alignment.reset();
        cohesion.reset();
        _aold = _a.clone();
        _a.reset();
        
        // Unaligned Collision Avoidance
        for each (b in boids) {
            if (b == this) continue;
            
            dist = b.p.subtract(_p);
            //time = predictTime(b);
            // Neighborhood seleted by angle i dist
            if (dist.lengthSquared < mydistSquared) {
                // Collision!
                if (dist.lengthSquared < mindistSquared*4) {
                    if (draw) {
                        g.lineStyle(1, 0xFF0000);
                        g.drawCircle(_p.x, _p.y, mindist);
                        g.beginFill(0xFF0000);
                        g.drawCircle((_p.x+b.px)/2, (_p.y+b.py)/2, 2);
                        g.endFill();
                        if (b.n < this.n)
                            trace(time.toFixed(4)+" "+(_v.angleBetween(b.v)*180/Math.PI).toFixed(1)+" "+b.n+" "+this.n);
                    }
                }
                angle = Math.abs(_v.angleBetween(dist));
                if (angle < myr) {
                    // Separation
                    separation.decrementBy(dist.scaleBy(perception/dist.lengthSquared));
                    alignment.incrementBy(b.v);
                    ++count;
                    //g.lineStyle(1, 0xFF0000, 0.25);
                    angle = Math.abs(_v.angleBetween(b.v));
                    if (angle < Math.PI/2) {
                        cohesion.incrementBy(b.p);//.decrementBy(b.v);
                        ++count2;
                        //g.lineStyle(1, 0xFFFF00, 0.25);
                    }
                    //g.moveTo(_p.x, _p.y);
                    //g.lineTo(b.p.x, b.p.y);
                }
                time = predictTime(b);
                if ((time < 0.) || (time >= minTime)) continue;
                //if (time < 0.) continue;
                mypos = v.scaleBy(time).incrementBy(p);
                hispos = b.v.scaleBy(time).incrementBy(b.p);
                collision = mypos.subtract(hispos);
                collisionLen = collision.lengthSquared;
                if (collisionLen >= mindistSquared*6.25) continue;
                minTime = time;
                collisionLen = (1/Math.sqrt(collisionLen));
                collision.scaleBy(collisionLen);
                //_a.incrementBy(collision);
                _a = collision.clone();
                text.text = "C";
                if (draw) {
                    g.lineStyle(1, 0x0000FF, 0.4);
                    g.moveTo(_p.x, _p.y);
                    g.lineTo(mypos.x, mypos.y);
                    g.beginFill(0x0000FF, 0.4);
                    g.drawCircle(mypos.x, mypos.y, mindist);
                    g.endFill();
                }
            }
        }
        /*if (_a.isZero()) {
            text.text = "B";
            // Boundaries
            if (_p.x < minx)
                _a.x += 0.4;//0.5*(minx-_p.x)/minx;
            else if (_p.x > maxx)
                _a.x -= 0.4;//0.5*(_p.x-maxx)/minx;
            if (_p.y < miny)
                _a.y += 0.4;//0.5*(miny-_p.y)/miny;
            else if (_p.y > maxy)
                _a.y -= 0.4;//0.5*(_p.y-maxy)/miny;
        }*/
        
        if (_a.isAlmostZero()) {
            //text.text = "F";
            //trace(separation.length);
            //_a.incrementBy(separation.scaleBy(0.5));
            // Alignment
            /*if (count > 0) {
                alignment.scaleBy(1/count).decrementBy(_v).scaleBy(1/(maxspeed*2));
                _a.incrementBy(alignment.scaleBy(2));
            }
            // Cohesion
            if (count2 > 0) {
                cohesion.scaleBy(1/count2).decrementBy(_p).scaleBy(1/(mydist));
                _a.incrementBy(cohesion.scaleBy(8));
            }*/
        }
        
        if (_a.isAlmostZero() && count == 0) {
            /*text.text = "W";
            // Wandering
            rangle += sign(Math.random() - 0.5) * Math.PI / 36;
            _a.incrementBy(v.scaleBy(0.44/_v.length));
            _a.x += 0.45*Math.sin(rangle);
            _a.y += 0.45*Math.cos(rangle);*/
        } else {
            rangle = _a.angle;
        }
        
        // Boundaries
        if (_p.x < minx)
            _a.x += 0.4;//0.5*(minx-_p.x)/minx;
        else if (_p.x > maxx)
            _a.x -= 0.4;//0.5*(_p.x-maxx)/minx;
        if (_p.y < miny)
            _a.y += 0.4;//0.5*(miny-_p.y)/miny;
        else if (_p.y > maxy)
            _a.y -= 0.4;//0.5*(_p.y-maxy)/miny;
            
        // Acceleration tampering...
        _a.decrementBy(_aold);
        var t:Number = _a.length;
        if (t > 0.0001) _a.scaleBy(0.25);
        //max A?
        if (t >= 0.15) _a.scaleBy(0.15/t);
        // Damping factor (1 -> no damping, 0 -> no change)
        _a.scaleBy(0.5);
        _a.incrementBy(_aold);
    }

    public function update(quiet:Boolean, g:Graphics) : void {
        _v.incrementBy(_a);
        _v.scaleBy(1./12);
        _p.incrementBy(_v);
        _v.scaleBy(12.);
        
        // speed limit
        var v:Number = _v.length;
        if (v > maxspeed) {
            _v.scaleBy(maxspeed/v);
        } else if (v < minspeed) {
            _v.scaleBy(minspeed/v);
        } else {
            _v.scaleBy(0.99);
        }
    }
}

class Vector3 {
    private var _x:Number = 0;
    private var _y:Number = 0;
    private var _z:Number = 0;
    
    public function get x():Number { return _x; }
    public function set x(newx:Number):void { _x = newx; }
    public function get y():Number { return _y; }
    public function set y(newy:Number):void { _y = newy; }
    public function get z():Number { return _z; }
    public function set z(newz:Number):void { _z = newz; }
    public function get lengthSquared():Number { return x*x+y*y+z*z; }
    public function get length():Number { return Math.sqrt(lengthSquared); }
    public function get angle():Number { return Math.atan2(_y, _x); }
    
    public function Vector3 (x:Number = 0., y:Number = 0., z:Number = 0.) {
            _x = x;
            _y = y;
            _z = z;
    }
    public function v3D() : Vector3D {
        var ret:Vector3D = new Vector3D(_x, _y, _z);
        return ret;
    }
    public function toString() : String {
        return "("+_x.toFixed(2)+", "+_y.toFixed(2)+", "+_z.toFixed(2)+")";
    }
    public function isNonZero() : Boolean {
        if (_x != 0 || _y != 0 || _z != 0) return true;
        return false;
    }
    public function isZero() : Boolean {
        if (_x == 0 && _y == 0 && _z == 0) return true;
        return false;
    }
    public function isAlmostZero(min:Number = 0.15999) : Boolean {
        if (lengthSquared < min) return true;
        return false;
    }
    public function clone() : Vector3 {
        var ret:Vector3 = new Vector3(_x, _y, _z);
        return ret;
    }
    public function reset() : Vector3 {
        _x = 0;
        _y = 0;
        _z = 0;
        return this;
    }
    public function make(arg:Vector3) : Vector3 {
        _x = arg.x;
        _y = arg.y;
        _z = arg.z;
        return this;
    }
    public function incrementBy(arg:Vector3) : Vector3 {
        _x += arg.x;
        _y += arg.y;
        _z += arg.z;
        return this;
    }
    public function decrementBy(arg:Vector3) : Vector3 {
        _x -= arg.x;
        _y -= arg.y;
        _z -= arg.z;
        return this;
    }
    public function add(arg:Vector3) : Vector3 {
        var result:Vector3 = new Vector3(_x, _y, _z);
        result.x += arg.x;
        result.y += arg.y;
        result.z += arg.z;
        return result;
    }
    public function subtract(arg:Vector3) : Vector3 {
        var result:Vector3 = new Vector3(_x, _y, _z);
        result.x -= arg.x;
        result.y -= arg.y;
        result.z -= arg.z;
        return result;
    }
    public function scaleBy(arg:Number) : Vector3 {
        _x *= arg;
        _y *= arg;
        _z *= arg;
        return this;
    }
    public function scaleTo(arg:Number) : Vector3 {
        var tmp:Number = length;
        if (tmp == 0) return reset();
        return scaleBy(arg/tmp);
    }
    public function normalize() : Vector3 {
        /*var tmp:Number = length;
        if (tmp == 0) return reset();
        return scaleBy(1./tmp);*/
        return scaleTo(1.);
    }
    public function negate() : Vector3 {
        return scaleBy(-1);
    }
    public function dotProduct(arg:Vector3) : Number {
        return _x*arg.x+_y*arg.y+_z*arg.z;
    }
    public function crossProduct(arg:Vector3):Vector3 {
        var result:Vector3 = new Vector3(_y*arg.z-_z*arg.y, _z*arg.x-_x*arg.z, _x*arg.y-_y*arg.x);
        return result;
    }
    public function projectOnto(arg:Vector3):Vector3 {
        this.make(arg.clone().scaleBy(dotProduct(arg)/arg.lengthSquared));
        return this;
    }
    public function distance(arg:Vector3):Number {
        var result:Vector3 = this.subtract(arg);
        return result.length;
    }
    public function distanceSquared(arg:Vector3):Number {
        var result:Vector3 = this.subtract(arg);
        return result.lengthSquared;
    }
    public function rotate(arg:Number):Vector3 {
        var tmpx:Number = _x;
        var tmpy:Number = _y;
        _x = Math.cos(arg)*tmpx - Math.sin(arg)*tmpy;
        _y = Math.sin(arg)*tmpx + Math.cos(arg)*tmpy;
        return this;
    }
    public function rotate90():Vector3 {
        var tmpx:Number = _x;
        _x = -_y;
        _y = tmpx;
        return this;
    }
    public function angleBetween(v:Vector3):Number {
        var result:Number = Math.atan2(_y, _x) - Math.atan2(v.y, v.x);
        if (result < -Math.PI) result += Math.PI*2;
        if (result > Math.PI) result -= Math.PI*2;
        return result;
    }
}

function inittrace(s:Stage): void {
    WTrace.initTrace(s);
}

//global trace function
var trace:Function;

//wtreace class
class WTrace {
    private static var FONT:String = "Lucida Console";
    private static var SIZE:Number = 12;
    private static var TextFields:Array = [];
    private static var trace_stage:Stage;

    public static function initTrace(stg:Stage) : void {
        trace_stage = stg;
        SIZE = stg.stageHeight/50;
        trace = wtrace;
    }

    private static function scrollup() : void {
        // maximum number of lines: 50
        if (TextFields.length > 50) {
            trace_stage.removeChild(TextFields.shift());
        }
        for(var x:Number = TextFields.length - 1; x>=0; --x) {
            (TextFields[x] as TextField).y -= 1.05*SIZE;
        }
    }

    private static function clear() : void {
        while(TextFields.length > 0) {
            trace_stage.removeChild(TextFields.shift());
        }
    }

    public static function wtrace(... args) : void {
        var s:String="";
        var tracefield:TextField;

        if (args.length == 0) {
            clear();
        } else {
            /*for (var i:int = 0; i < args.length; ++i) {
                if (!(args[i] is int) && args[i] is Number) {
                    s += args[i].toFixed(3) + " ";*/
            var i:String;
            for (i in args) {
                if (args[i] is Number) {
                    s += args[i].toFixed(3) + " ";
                } else {
                    s += args[i] + " ";
                }
            }
            
            tracefield = new TextField();
            tracefield.autoSize = "left";
            tracefield.text = s;
            tracefield.y = trace_stage.stageHeight - SIZE;
            
            var tf:TextFormat = new TextFormat(FONT, SIZE);
            tracefield.setTextFormat(tf);
            trace_stage.addChild(tracefield);
            scrollup();
            TextFields.push(tracefield);
        }
    }
}