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

Boid.as
Flying birds using the Boid algorithm.
Get Adobe Flash player
by ABA 21 Dec 2008
/**
 * Copyright ABA ( http://wonderfl.net/user/ABA )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/kF3y
 */

// Boid.as
//  Flying birds using the Boid algorithm.
package
{
  import flash.display.Sprite;
  import flash.display.BitmapData;
  import flash.display.Bitmap;
  import flash.geom.Rectangle;
  import flash.events.Event;

  [SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]
  public class Boid extends Sprite
  {
    private const SCREEN_WIDTH:int = 465;
    private const SCREEN_HEIGHT:int = 465;
    private const FIELD_SIZE_RATIO:Number = 1.25;
    private const BIRDS_COUNT:int = 64;
    private const SEPARATION_SPEED:Number = 0.1;
    private const SEPARATION_RADIUS:Number = 30;
    private const ALIGNMENT_RATIO:Number = 0.025;
    private const COHESION_RADIUS:Number = 90;
    private const COHESION_RATIO:Number = 0.0005;
    private const FORWARD_VELOCITY:Number = 0.3;
    private const SLOWDOWN_RATIO:Number = 0.9;
    private const CLOUDS_COUNT:int = 32;
    private var buffer:BitmapData= new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
    private var birds:Array = new Array;
    private var clouds:Array = new Array;
    private var rect:Rectangle = new Rectangle;
    private var offset:Vector2 = new Vector2;
    private var centerOffset:Vector2 = new Vector2;
    private var fieldOffset:Vector2 = new Vector2;
    private var fieldSize:Vector2 = new Vector2;
    private var screenPos:Vector2 = new Vector2;

    public function Boid()
    {
      addChild(new Bitmap(buffer));
      fieldSize.x = SCREEN_WIDTH * FIELD_SIZE_RATIO;
      fieldSize.y = SCREEN_HEIGHT * FIELD_SIZE_RATIO;
      var i:int;
      for (i = 0; i < BIRDS_COUNT; i++)
      {
        var b:Bird = new Bird;
        b.pos.x = Math.random() * fieldSize.x;
        b.pos.y = Math.random() * fieldSize.y;
        b.vel.x = b.vel.y = 0;
        b.deg = Math.random() * Math.PI * 2;
        birds.push(b);
      }
      for (i = 0; i < CLOUDS_COUNT; i++)
      {
        var c:Cloud = new Cloud;
        c.pos.x = Math.random() * fieldSize.x;
        c.pos.y = Math.random() * fieldSize.y;
        var a:int = 0x4a + Math.random() * 0x12;
        c.color = a * 0x10000 + a * 0x100 + (a + 0x22);
        c.size = 24 + Math.random() * 36;
        clouds.push(c);
      }
      fieldOffset.x = fieldOffset.y = 0;
      addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }

    private function onEnterFrame(e:Event):void
    {
      buffer.fillRect(buffer.rect, 0x444466);
      setFieldOffset(birds[0].pos);
      for each (var c:Cloud in clouds)
        drawCloud(c);
      for each (var b:Bird in birds)
        updateBirdByBoid(b);
    }

    private function drawCloud(c:Cloud):void
    {
      screenPos.x = c.pos.x - fieldOffset.x;
      screenPos.y = c.pos.y - fieldOffset.y;
      normalizePosInField(screenPos);
      rect.x = screenPos.x - c.size + SCREEN_WIDTH / 2;
      rect.y = screenPos.y - c.size + SCREEN_HEIGHT / 2;
      rect.width = rect.height = c.size * 2;
      buffer.fillRect(rect, c.color);
    }

    private function updateBirdByBoid(b:Bird):void
    {
      var nb:Bird = getNearestBird(b);
      if (nb != null)
      {
        offset.x = nb.pos.x - b.pos.x;
        offset.y = nb.pos.y - b.pos.y;
        normalizePosInField(offset);
        var dist:Number = offset.length;
        offset.normalize();
        if (dist > SEPARATION_RADIUS)
          offset.mul(dist / SEPARATION_RADIUS * SEPARATION_SPEED * 0.33);
        else
          offset.mul(-dist/ SEPARATION_RADIUS * SEPARATION_SPEED);
        b.vel.add(offset);
        b.vel.x += (nb.vel.x - b.vel.x) * ALIGNMENT_RATIO;
        b.vel.y += (nb.vel.y - b.vel.y) * ALIGNMENT_RATIO;
      }
      var op:Vector2 = getCenterOffsetInBirds(b, COHESION_RADIUS);
      offset.x = op.x;
      offset.y = op.y;
      offset.mul(COHESION_RATIO);
      b.vel.x += offset.x;
      b.vel.y += offset.y;
      b.deg += normalizeDeg(Math.atan2(b.vel.x, b.vel.y) - b.deg) * 0.1;
      b.deg = normalizeDeg(b.deg);
      b.vel.x += Math.sin(b.deg) * FORWARD_VELOCITY;
      b.vel.y += Math.cos(b.deg) * FORWARD_VELOCITY;
      b.vel.mul(SLOWDOWN_RATIO);
      b.pos.add(b.vel);
      normalizePosInField(b.pos);
      drawBird(b);
    }

    private function drawBird(b:Bird):void
    {
      screenPos.x = b.pos.x - fieldOffset.x;
      screenPos.y = b.pos.y - fieldOffset.y;
      normalizePosInField(screenPos);
      rect.x = screenPos.x - 3 + SCREEN_WIDTH / 2;
      rect.y = screenPos.y - 3 + SCREEN_HEIGHT / 2;
      rect.width = rect.height = 7;
      buffer.fillRect(rect, 0xaaaaaa);
      rect.width = rect.height = 5;
      rect.x = screenPos.x - 2 + SCREEN_WIDTH / 2 + Math.sin(b.deg + Math.PI / 2) * 6;
      rect.y = screenPos.y - 2 + SCREEN_HEIGHT / 2 + Math.cos(b.deg + Math.PI / 2) * 6;
      var c:int;
      c = (Math.sin(b.deg + Math.PI * 1.8) * 0.6 + 1.4) / 2 * 0xdd;
      buffer.fillRect(rect, c * 0x10000 + c * 0x100 + c);
      rect.x = screenPos.x - 2 + SCREEN_WIDTH / 2 + Math.sin(b.deg - Math.PI / 2) * 6;
      rect.y = screenPos.y - 2 + SCREEN_HEIGHT / 2 + Math.cos(b.deg - Math.PI / 2) * 6;
      c = (Math.sin(b.deg + Math.PI * 2.8) * 0.6 + 1.4) / 2 * 0xdd;
      buffer.fillRect(rect, c * 0x10000 + c * 0x100 + c);
    }

    private function getNearestBird(tb:Bird):Bird
    {
      var nd:Number = 99999, d:Number;
      var nb:Bird = null;
      for each (var b:Bird in birds)
      {
        if (b == tb)
          continue;
        offset.x = b.pos.x - tb.pos.x;
        offset.y = b.pos.y - tb.pos.y;
        normalizePosInField(offset);
        d = offset.length;
        if (d < nd) {
          nd = d;
          nb = b;
        }
      }
      return nb;
    }

    private function getCenterOffsetInBirds(tb:Bird, r:Number):Vector2
    {
      centerOffset.x = centerOffset.y = 0;
      var bc:int = 0;
      for each (var b:Bird in birds)
      {
        offset.x = b.pos.x - tb.pos.x;
        offset.y = b.pos.y - tb.pos.y;
        normalizePosInField(offset);
        if (offset.length < r)
        {
          centerOffset.add(offset);
          bc++;
        }
      }
      centerOffset.div(bc);
      return centerOffset;
    }

    private function setFieldOffset(p:Vector2):void
    {
      fieldOffset.x = p.x;
      fieldOffset.y = p.y;
      normalizePosInField(fieldOffset);
    }

    private function normalizePosInField(p:Vector2):void {
      if (p.x < -fieldSize.x / 2)
        p.x += fieldSize.x;
      else if (p.x >= fieldSize.x / 2)
        p.x -= fieldSize.x;
      if (p.y < -fieldSize.y / 2)
        p.y += fieldSize.y;
      else if (p.y >= fieldSize.y / 2)
        p.y -= fieldSize.y;
    }

    private function normalizeDeg(d:Number):Number {
      if (d > Math.PI)
        return d - Math.PI * 2;
      else if (d < -Math.PI)
        return d + Math.PI * 2;
      else
        return d;
    }
  }
}

class Bird
{
  public var pos:Vector2 = new Vector2;
  public var vel:Vector2 = new Vector2;
  public var deg:Number;
}

class Cloud
{
  public var pos:Vector2 = new Vector2;
  public var color:int;
  public var size:int;
}

class Vector2
{
  public var x:Number;
  public var y:Number;

  public function add(v:Vector2):void
  {
    x += v.x;
    y += v.y;
  }

  public function mul(v:Number):void
  {
    x *= v;
    y *= v;
  }

  public function div(v:Number):void
  {
    x /= v;
    y /= v;
  }

  public function normalize():void
  {
    div(length);
  }

  public function get length():Number
  {
    return Math.sqrt(x * x + y * y);
  }
}