Fish school flocking
forked from Ash game framework boilerplate test (diff: 101)
Different behaviour group settings: - flocking fish - non-moving fish - wanderering fish
ActionScript3 source code
/**
* Copyright Glidias ( http://wonderfl.net/user/Glidias )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/furq
*/
// forked from Glidias's Ash game framework boilerplate test
package
{
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Vector3D;
/**
* http://wonderfl.net/c/fd6Z Refactored to Ash component/system-based game framework
*
* @author Glenn Ko
*/
[SWF(width="1920", height="1080", frameRate="60", backgroundColor="0xddddff")]
public class TestFlockingSystem extends MovieClip
{
public var engine:Engine;
public var ticker:FrameTickProvider;
private static const NUMBOIDS:int = 26;
private var WEIGHTS_DEFAULTED:Array;
private var WEIGHTS_TOTAL:Number;
static public const MIN_SPEED:Number = 1;
static public const MAX_SPEED:Number = 4;
static public const TURN_RATIO:Number = .1;
static public const MIN_DIST:Number = 100;
static public const SENSE_DIST:Number = 340;
public function TestFlockingSystem()
{
super();
engine = new Engine();
ReflectUtil.registerComponents([Pos,Vel,Rot,Flocking,DisplayObject]);
engine.addSystem( new FlockingSystem(), 0 );
engine.addSystem( new DisplayObjectRenderingSystem(this), 1);
ticker = new FrameTickProvider(stage);
ticker.add(tick);
createRandomBoids();
ticker.start();
}
private function tick(time:Number):void
{
engine.update(time);
}
private function recalculateWeights():void {
var weight:Number;
var i:int;
var numUndeclaredWeights:Number = 0;
var totalDeclaredWeight:Number = 0;
var len:int = WEIGHTS_DEFAULTED.length;
for (i = 0; i < len; i++) {
weight = WEIGHTS_DEFAULTED[i];
totalDeclaredWeight += weight;
}
WEIGHTS_TOTAL = totalDeclaredWeight;
}
private function getRaceID(floatRatio:Number):int {
// return floatRatio * raceNum + 1;
floatRatio *= WEIGHTS_TOTAL;
var accum:Number = 0;
var result:int = 0;
var len:int = WEIGHTS_DEFAULTED.length;
for (var i:int = 0; i < len; i++) {
if (floatRatio < accum) { // did not meet requirement
break;
}
accum += WEIGHTS_DEFAULTED[i];
result = i;
}
return result;
}
private function getRandomRaceIndex():int {
return getRaceID(Math.random());
}
private function createRandomBoids():void
{
var tmp:Number = 2.0 * Math.PI / NUMBOIDS;
var tmpw:int = stage.stageWidth / 2, tmph:int = stage.stageHeight / 2;
var flockSettings:FlockSettings = Flocking.createFlockSettings(MIN_DIST, SENSE_DIST, 0, 0, tmpw * 2, tmph * 2, MIN_SPEED, MAX_SPEED*2 , TURN_RATIO);
var nonMovingFlockSettings:FlockSettings = Flocking.createFlockSettings(MIN_DIST, SENSE_DIST, 0, 0, tmpw * 2, tmph * 2, 0.000000001, 0.000000001, 1);
var wanderingFlockSettings:FlockSettings = Flocking.createFlockSettings(MIN_DIST, SENSE_DIST, 0, 0, tmpw * 2, tmph * 2, MIN_SPEED, MAX_SPEED * 2 , TURN_RATIO);
flockSettings.seperationScale = .65;
flockSettings.cohesionScale = 12;
flockSettings.alignmentScale = 4;
nonMovingFlockSettings.seperationScale =1;
nonMovingFlockSettings.alignmentScale = 0;
nonMovingFlockSettings.cohesionScale = 0;
wanderingFlockSettings.cohesionScale = 0;
wanderingFlockSettings.alignmentScale = 0;
wanderingFlockSettings.seperationScale = .5;
var flockSettingsArr:Array = [flockSettings, nonMovingFlockSettings, wanderingFlockSettings];
WEIGHTS_DEFAULTED = [.7, .8, .4];
// WEIGHTS_DEFAULTED = [1, 0, 0];
recalculateWeights();
for (var i:int = 0; i < NUMBOIDS; ++i) {
const ph:Number = i * tmp;
var pos:Pos = new Pos( tmpw + ((i % 4) * 0.2 + 0.3) * tmpw * Math.cos(ph), tmph + ((i % 4) * 0.2 + 0.3) * tmph * Math.sin(ph));
var vel:Vel = new Vel( ((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)));
var rot:Rot = new Rot(0, 0, Math.random() * 2 * Math.PI);
var useFlockSetting:FlockSettings = flockSettingsArr[ getRandomRaceIndex() ] ;
var graphicer:BoidGraphic;
var entity:Entity = new Entity().add(pos).add(rot).add(vel).add( graphicer=new BoidGraphic(), DisplayObject).add( new Flocking().setup(useFlockSetting )) ;
if (useFlockSetting === wanderingFlockSettings ) graphicer.drawDirPointer()
else if (useFlockSetting === flockSettings) graphicer.drawDirPointer(.5);
engine.addEntity(entity);
}
}
}
}
class BoidGraphic extends Shape {
public function BoidGraphic() {
var g:Graphics = 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.mydist, Math.sin(j * b.myr)*b.mydist);
}
g.lineTo(0,0);*/
//g.drawCircle(0, 0, b.mindist);
/* Arrow
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();
*/
///* Circle
g.beginFill(0x0055ff);
g.drawCircle(0, 0, 23*2);
//*/
/*
drawer = new Sprite();
this.addChild(drawer);
*/
}
public function drawDirPointer(scaler:Number=1):void {
var g:Graphics = graphics;
g.lineStyle(0.1, 0);
g.moveTo(0, 0);
g.lineTo(23*2*scaler, 0);
}
}
// -- ASH FRAMEWORK BEGINS BELOW (scroll to bottom for ash-related compiles specific to application!)
//}//package {
//import Engine;
//import Node;
//import NodeList;
//import System;
//}
//package {
//import ash.signals.Signal2;
import flash.utils.Dictionary;
import flash.utils.getQualifiedClassName;
/*public*/ class Entity
{
private static var nameCount : int = 0;
private var _name : String;
public var componentAdded : Signal2;
public var componentRemoved : Signal2;
public var nameChanged : Signal2;
public var previous : Entity;
public var next : Entity;
public var components : Dictionary;
public function Entity( name : String = "" )
{
componentAdded = new Signal2( Entity, Class );
componentRemoved = new Signal2( Entity, Class );
nameChanged = new Signal2( Entity, String );
components = new Dictionary();
if( name )
{
_name = name;
}
else
{
_name = "_entity" + (++nameCount);
}
}
public function get name() : String
{
return _name;
}
public function set name( value : String ) : void
{
if( _name != value )
{
var previous : String = _name;
_name = value;
nameChanged.dispatch( this, previous );
}
}
public function add( component : Object, componentClass : Class = null ) : Entity
{
if ( !componentClass )
{
componentClass = Class( component.constructor );
}
if ( components[ componentClass ] )
{
remove( componentClass );
}
components[ componentClass ] = component;
componentAdded.dispatch( this, componentClass );
return this;
}
public function remove( componentClass : Class ) : *
{
var component : * = components[ componentClass ];
if ( component )
{
delete components[ componentClass ];
componentRemoved.dispatch( this, componentClass );
return component;
}
return null;
}
public function get( componentClass : Class ) : *
{
return components[ componentClass ];
}
public function getAll() : Array
{
var componentArray : Array = new Array();
for each( var component : * in components )
{
componentArray.push( component );
}
return componentArray;
}
public function has( componentClass : Class ) : Boolean
{
return components[ componentClass ] != null;
}
}
//}
//package {
/*public*/ class System
{
public var previous : System;
public var next : System;
public var priority : int = 0;
public function addToEngine( engine : Engine ) : void
{
}
public function removeFromEngine( engine : Engine ) : void
{
}
public function update( time : Number ) : void
{
}
}
//}
//package {
/*public*/ class Node
{
public var entity : Entity;
public var previous : *;
public var next : *;
}
//}
//}
class ListenerNodePool
{
private var tail : ListenerNode;
private var cacheTail : ListenerNode;
public function get():ListenerNode
{
if( tail )
{
var node : ListenerNode = tail;
tail = tail.previous;
node.previous = null;
return node;
}
else
{
return new ListenerNode();
}
}
public function dispose( node : ListenerNode ):void
{
node.listener = null;
node.once = false;
node.next = null;
node.previous = tail;
tail = node;
}
public function cache( node : ListenerNode ) : void
{
node.listener = null;
node.previous = cacheTail;
cacheTail = node;
}
public function releaseCache() : void
{
while( cacheTail )
{
var node : ListenerNode = cacheTail;
cacheTail = node.previous;
node.next = null;
node.previous = tail;
tail = node;
}
}
}
class ListenerNode
{
public var previous : ListenerNode;
public var next : ListenerNode;
public var listener : Function;
public var once : Boolean;
}
/*public*/ class ListIteratingSystem extends System
{
protected var nodeList : NodeList;
protected var nodeClass : Class;
protected var nodeUpdateFunction : Function;
protected var nodeAddedFunction : Function;
protected var nodeRemovedFunction : Function;
public function ListIteratingSystem( nodeClass : Class, nodeUpdateFunction : Function, nodeAddedFunction : Function = null, nodeRemovedFunction : Function = null )
{
this.nodeClass = nodeClass;
this.nodeUpdateFunction = nodeUpdateFunction;
this.nodeAddedFunction = nodeAddedFunction;
this.nodeRemovedFunction = nodeRemovedFunction;
}
override public function addToEngine( engine : Engine ) : void
{
nodeList = engine.getNodeList( nodeClass );
if( nodeAddedFunction != null )
{
for( var node : Node = nodeList.head; node; node = node.next )
{
nodeAddedFunction( node );
}
nodeList.nodeAdded.add( nodeAddedFunction );
}
if( nodeRemovedFunction != null )
{
nodeList.nodeRemoved.add( nodeRemovedFunction );
}
}
override public function removeFromEngine( engine : Engine ) : void
{
if( nodeAddedFunction != null )
{
nodeList.nodeAdded.remove( nodeAddedFunction );
}
if( nodeRemovedFunction != null )
{
nodeList.nodeRemoved.remove( nodeRemovedFunction );
}
nodeList = null;
}
override public function update( time : Number ) : void
{
for( var node : Node = nodeList.head; node; node = node.next )
{
nodeUpdateFunction( node, time );
}
}
}
//}
//package {
//import ash.signals.Signal1;
/*public*/ class NodeList
{
public var head : *;
public var tail : *;
public var nodeAdded : Signal1;
public var nodeRemoved : Signal1;
public function NodeList()
{
nodeAdded = new Signal1( Node );
nodeRemoved = new Signal1( Node );
}
public function add( node : Node ) : void
{
if( ! head )
{
head = tail = node;
node.next = node.previous = null;
}
else
{
tail.next = node;
node.previous = tail;
node.next = null;
tail = node;
}
nodeAdded.dispatch( node );
}
public function remove( node : Node ) : void
{
if ( head == node)
{
head = head.next;
}
if ( tail == node)
{
tail = tail.previous;
}
if (node.previous)
{
node.previous.next = node.next;
}
if (node.next)
{
node.next.previous = node.previous;
}
nodeRemoved.dispatch( node );
}
public function removeAll() : void
{
while( head )
{
var node : Node = head;
head = node.next;
node.previous = null;
node.next = null;
nodeRemoved.dispatch( node );
}
tail = null;
}
public function get empty() : Boolean
{
return head == null;
}
public function swap( node1 : Node, node2 : Node ) : void
{
if( node1.previous == node2 )
{
node1.previous = node2.previous;
node2.previous = node1;
node2.next = node1.next;
node1.next = node2;
}
else if( node2.previous == node1 )
{
node2.previous = node1.previous;
node1.previous = node2;
node1.next = node2.next;
node2.next = node1;
}
else
{
var temp : Node = node1.previous;
node1.previous = node2.previous;
node2.previous = temp;
temp = node1.next;
node1.next = node2.next;
node2.next = temp;
}
if( head == node1 )
{
head = node2;
}
else if( head == node2 )
{
head = node1;
}
if( tail == node1 )
{
tail = node2;
}
else if( tail == node2 )
{
tail = node1;
}
if( node1.previous )
{
node1.previous.next = node1;
}
if( node2.previous )
{
node2.previous.next = node2;
}
if( node1.next )
{
node1.next.previous = node1;
}
if( node2.next )
{
node2.next.previous = node2;
}
}
public function insertionSort( sortFunction : Function ) : void
{
if( head == tail )
{
return;
}
var remains : Node = head.next;
for( var node : Node = remains; node; node = remains )
{
remains = node.next;
for( var other : Node = node.previous; other; other = other.previous )
{
if( sortFunction( node, other ) >= 0 )
{
if( node != other.next )
{
if ( tail == node)
{
tail = node.previous;
}
node.previous.next = node.next;
if (node.next)
{
node.next.previous = node.previous;
}
node.next = other.next;
node.previous = other;
node.next.previous = node;
other.next = node;
}
break;
}
}
if( !other )
{
if ( tail == node)
{
tail = node.previous;
}
node.previous.next = node.next;
if (node.next)
{
node.next.previous = node.previous;
}
node.next = head;
head.previous = node;
node.previous = null;
head = node;
}
}
}
public function mergeSort( sortFunction : Function ) : void
{
if( head == tail )
{
return;
}
var lists : Vector.<Node> = new Vector.<Node>;
var start : Node = head;
var end : Node;
while( start )
{
end = start;
while( end.next && sortFunction( end, end.next ) <= 0 )
{
end = end.next;
}
var next : Node = end.next;
start.previous = end.next = null;
lists.push( start );
start = next;
}
while( lists.length > 1 )
{
lists.push( merge( lists.shift(), lists.shift(), sortFunction ) );
}
tail = head = lists[0];
while( tail.next )
{
tail = tail.next;
}
}
private function merge( head1 : Node, head2 : Node, sortFunction : Function ) : Node
{
var node : Node;
var head : Node;
if( sortFunction( head1, head2 ) <= 0 )
{
head = node = head1;
head1 = head1.next;
}
else
{
head = node = head2;
head2 = head2.next;
}
while( head1 && head2 )
{
if( sortFunction( head1, head2 ) <= 0 )
{
node.next = head1;
head1.previous = node;
node = head1;
head1 = head1.next;
}
else
{
node.next = head2;
head2.previous = node;
node = head2;
head2 = head2.next;
}
}
if( head1 )
{
node.next = head1;
head1.previous = node;
}
else
{
node.next = head2;
head2.previous = node;
}
return head;
}
}
//}
//package {
import flash.utils.Dictionary;
/*public*/ class SignalBase
{
public var head : ListenerNode;
public var tail : ListenerNode;
private var nodes : Dictionary;
private var listenerNodePool : ListenerNodePool;
private var toAddHead : ListenerNode;
private var toAddTail : ListenerNode;
private var dispatching : Boolean;
private var _numListeners : int = 0;
public function SignalBase()
{
nodes = new Dictionary( true );
listenerNodePool = new ListenerNodePool();
}
protected function startDispatch() : void
{
dispatching = true;
}
protected function endDispatch() : void
{
dispatching = false;
if( toAddHead )
{
if( !head )
{
head = toAddHead;
tail = toAddTail;
}
else
{
tail.next = toAddHead;
toAddHead.previous = tail;
tail = toAddTail;
}
toAddHead = null;
toAddTail = null;
}
listenerNodePool.releaseCache();
}
public function get numListeners() : int
{
return _numListeners;
}
public function add( listener : Function ) : void
{
if( nodes[ listener ] )
{
return;
}
var node : ListenerNode = listenerNodePool.get();
node.listener = listener;
nodes[ listener ] = node;
addNode( node );
}
public function addOnce( listener : Function ) : void
{
if( nodes[ listener ] )
{
return;
}
var node : ListenerNode = listenerNodePool.get();
node.listener = listener;
node.once = true;
nodes[ listener ] = node;
addNode( node );
}
protected function addNode( node : ListenerNode ) : void
{
if( dispatching )
{
if( !toAddHead )
{
toAddHead = toAddTail = node;
}
else
{
toAddTail.next = node;
node.previous = toAddTail;
toAddTail = node;
}
}
else
{
if ( !head )
{
head = tail = node;
}
else
{
tail.next = node;
node.previous = tail;
tail = node;
}
}
_numListeners++;
}
public function remove( listener : Function ) : void
{
var node : ListenerNode = nodes[ listener ];
if ( node )
{
if ( head == node)
{
head = head.next;
}
if ( tail == node)
{
tail = tail.previous;
}
if ( toAddHead == node)
{
toAddHead = toAddHead.next;
}
if ( toAddTail == node)
{
toAddTail = toAddTail.previous;
}
if (node.previous)
{
node.previous.next = node.next;
}
if (node.next)
{
node.next.previous = node.previous;
}
delete nodes[ listener ];
if( dispatching )
{
listenerNodePool.cache( node );
}
else
{
listenerNodePool.dispose( node );
}
_numListeners--;
}
}
public function removeAll() : void
{
while( head )
{
var node : ListenerNode = head;
head = head.next;
delete nodes[ node.listener ];
listenerNodePool.dispose( node );
node.previous = null;
node.next = null;
}
tail = null;
toAddHead = null;
toAddTail = null;
_numListeners = 0;
}
}
//}
//package {
//import ash.signals.Signal0;
import flash.utils.Dictionary;
/*public*/ class Engine
{
private var entityNames : Dictionary;
private var entityList : EntityList;
private var systemList : SystemList;
private var families : Dictionary;
public var updating : Boolean;
public var updateComplete : Signal0;
public var familyClass : Class = ComponentMatchingFamily;
public function Engine()
{
entityList = new EntityList();
entityNames = new Dictionary();
systemList = new SystemList();
families = new Dictionary();
updateComplete = new Signal0();
}
public function addEntity( entity : Entity ) : void
{
if( entityNames[ entity.name ] )
{
throw new Error( "The entity name " + entity.name + " is already in use by another entity." );
}
entityList.add( entity );
entityNames[ entity.name ] = entity;
entity.componentAdded.add( componentAdded );
entity.componentRemoved.add( componentRemoved );
entity.nameChanged.add( entityNameChanged );
for each( var family : IFamily in families )
{
family.newEntity( entity );
}
}
public function removeEntity( entity : Entity ) : void
{
entity.componentAdded.remove( componentAdded );
entity.componentRemoved.remove( componentRemoved );
entity.nameChanged.remove( entityNameChanged );
for each( var family : IFamily in families )
{
family.removeEntity( entity );
}
delete entityNames[ entity.name ];
entityList.remove( entity );
}
private function entityNameChanged( entity : Entity, oldName : String ) : void
{
if( entityNames[ oldName ] == entity )
{
delete entityNames[ oldName ];
entityNames[ entity.name ] = entity;
}
}
public function getEntityByName( name : String ) : Entity
{
return entityNames[ name ];
}
public function removeAllEntities() : void
{
while( entityList.head )
{
removeEntity( entityList.head );
}
}
public function get entities() : Vector.<Entity>
{
var entities : Vector.<Entity> = new Vector.<Entity>();
for( var entity : Entity = entityList.head; entity; entity = entity.next )
{
entities.push( entity );
}
return entities;
}
private function componentAdded( entity : Entity, componentClass : Class ) : void
{
for each( var family : IFamily in families )
{
family.componentAddedToEntity( entity, componentClass );
}
}
private function componentRemoved( entity : Entity, componentClass : Class ) : void
{
for each( var family : IFamily in families )
{
family.componentRemovedFromEntity( entity, componentClass );
}
}
public function getNodeList( nodeClass : Class ) : NodeList
{
if( families[nodeClass] )
{
return IFamily( families[nodeClass] ).nodeList;
}
var family : IFamily = new familyClass( nodeClass, this );
families[nodeClass] = family;
for( var entity : Entity = entityList.head; entity; entity = entity.next )
{
family.newEntity( entity );
}
return family.nodeList;
}
public function releaseNodeList( nodeClass : Class ) : void
{
if( families[nodeClass] )
{
families[nodeClass].cleanUp();
}
delete families[nodeClass];
}
public function addSystem( system : System, priority : int ) : void
{
system.priority = priority;
system.addToEngine( this );
systemList.add( system );
}
public function getSystem( type : Class ) : System
{
return systemList.get( type );
}
public function get systems() : Vector.<System>
{
var systems : Vector.<System> = new Vector.<System>();
for( var system : System = systemList.head; system; system = system.next )
{
systems.push( system );
}
return systems;
}
public function removeSystem( system : System ) : void
{
systemList.remove( system );
system.removeFromEngine( this );
}
public function removeAllSystems() : void
{
while( systemList.head )
{
removeSystem( systemList.head );
}
}
public function update( time : Number ) : void
{
updating = true;
for( var system : System = systemList.head; system; system = system.next )
{
system.update( time );
}
updating = false;
updateComplete.dispatch();
}
}
//package {
/*public*/ class Signal0 extends SignalBase
{
public function Signal0()
{
}
public function dispatch() : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener();
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
class ReflectUtil {
private static var CACHE:Dictionary = new Dictionary();
public static var HACHE_COMPONENTS:Dictionary = new Dictionary();
public static function registerComponents(arrClasses:Array):void {
var i:int = arrClasses.length;
while (--i > -1) {
HACHE_COMPONENTS[getQualifiedClassName(arrClasses[i])] = arrClasses[i];
}
}
public static function getFields(nodeClass:Class, arrOfClasses:Array):Dictionary {
if (CACHE[nodeClass]) return CACHE[nodeClass];
var variables : XMLList = describeType( nodeClass ).factory.variable;
var components:Dictionary = new Dictionary();
var i:int = arrOfClasses.length;
var hash:Object = { };
while (--i > -1) {
hash[ getQualifiedClassName(arrOfClasses[i]) ] = arrOfClasses[i];
}
for each ( var atom:XML in variables )
{
if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
{
var componentClass : Class = hash[ atom.@type.toString()] || HACHE_COMPONENTS[atom.@type.toString()];
if (componentClass == null) throw new Error("Could not find component class>" + atom.@type + ", for "+nodeClass);
components[componentClass] = atom.@name.toString();
}
}
CACHE[nodeClass] = components;
return components;
}
}
//package {
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
/*public*/ class ComponentMatchingFamily implements IFamily
{
private var nodes : NodeList;
private var entities : Dictionary;
private var nodeClass : Class;
private var components : Dictionary;
private var nodePool : NodePool;
private var engine : Engine;
public function ComponentMatchingFamily( nodeClass : Class, engine : Engine )
{
this.nodeClass = nodeClass;
this.engine = engine;
init();
}
private function init() : void
{
nodes = new NodeList();
entities = new Dictionary();
components = new Dictionary();
nodePool = new NodePool( nodeClass, components );
nodePool.dispose( nodePool.get() );
try {
var dict:Dictionary = nodeClass["_getFields"]();
}
catch (e:Error) {
var variables : XMLList = describeType( nodeClass ).factory.variable;
for each ( var atom:XML in variables )
{
if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
{
var componentClass : Class = ReflectUtil.HACHE_COMPONENTS[ atom.@type.toString()];
if (componentClass == null) throw new Error("Component class is undefined! "+atom.@type);
components[componentClass] = atom.@name.toString();
}
}
dict = new Dictionary();
}
for each(var key:* in dict) {
components[key] = dict[key];
}
}
public function get nodeList() : NodeList
{
return nodes;
}
public function newEntity( entity : Entity ) : void
{
addIfMatch( entity );
}
public function componentAddedToEntity( entity : Entity, componentClass : Class ) : void
{
addIfMatch( entity );
}
public function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void
{
if( components[componentClass] )
{
removeIfMatch( entity );
}
}
public function removeEntity( entity : Entity ) : void
{
removeIfMatch( entity );
}
private function addIfMatch( entity : Entity ) : void
{
if( !entities[entity] )
{
var componentClass : *;
for ( componentClass in components )
{
if ( !entity.has( componentClass ) )
{
return;
}
}
var node : Node = nodePool.get();
node.entity = entity;
for ( componentClass in components )
{
node[components[componentClass]] = entity.get( componentClass );
}
entities[entity] = node;
nodes.add( node );
}
}
private function removeIfMatch( entity : Entity ) : void
{
if( entities[entity] )
{
var node : Node = entities[entity];
delete entities[entity];
nodes.remove( node );
if( engine.updating )
{
nodePool.cache( node );
engine.updateComplete.add( releaseNodePoolCache );
}
else
{
nodePool.dispose( node );
}
}
}
private function releaseNodePoolCache() : void
{
engine.updateComplete.remove( releaseNodePoolCache );
nodePool.releaseCache();
}
public function cleanUp() : void
{
for( var node : Node = nodes.head; node; node = node.next )
{
delete entities[node.entity];
}
nodes.removeAll();
}
}
//}
//package {
/*public*/ class Signal2 extends SignalBase
{
private var type1 : Class;
private var type2 : Class;
public function Signal2( type1 : Class, type2 : Class )
{
this.type1 = type1;
this.type2 = type2;
}
public function dispatch( object1 : *, object2 : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object1, object2 );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
/*
* Based on ideas used in Robert Penner's AS3-signals - https://github.com/robertpenner/as3-signals
*/
class Signal3 extends SignalBase
{
private var type1 : Class;
private var type2 : Class;
private var type3 : Class;
public function Signal3( type1 : Class, type2 : Class, type3 : Class )
{
this.type1 = type1;
this.type2 = type2;
this.type3 = type3;
}
public function dispatch( object1 : *, object2 : *, object3 : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object1, object2, object3 );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
class SignalAny extends SignalBase
{
protected var classes : Array;
public function SignalAny( ...classes )
{
this.classes = classes;
}
public function dispatch( ...objects ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener.apply( null, objects );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//package {
/*public*/ class Signal1 extends SignalBase
{
private var type : Class;
public function Signal1( type : Class )
{
this.type = type;
}
public function dispatch( object : * ) : void
{
startDispatch();
var node : ListenerNode;
for ( node = head; node; node = node.next )
{
node.listener( object );
if( node.once )
{
remove( node.listener );
}
}
endDispatch();
}
}
//}
interface IFamily {
function get nodeList() : NodeList;
function newEntity( entity : Entity ) : void;
function removeEntity( entity : Entity ) : void;
function componentAddedToEntity( entity : Entity, componentClass : Class ) : void;
function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void;
function cleanUp() : void;
}
interface ITickProvider
{
function get playing() : Boolean;
function add( listener : Function ) : void;
function remove( listener : Function ) : void;
function start() : void;
function stop() : void;
}
class EntityList
{
public var head : Entity;
public var tail : Entity;
public function add( entity : Entity ) : void
{
if( ! head )
{
head = tail = entity;
entity.next = entity.previous = null;
}
else
{
tail.next = entity;
entity.previous = tail;
entity.next = null;
tail = entity;
}
}
public function remove( entity : Entity ) : void
{
if ( head == entity)
{
head = head.next;
}
if ( tail == entity)
{
tail = tail.previous;
}
if (entity.previous)
{
entity.previous.next = entity.next;
}
if (entity.next)
{
entity.next.previous = entity.previous;
}
// N.B. Don't set node.next and node.previous to null because that will break the list iteration if node is the current node in the iteration.
}
public function removeAll() : void
{
while( head )
{
var entity : Entity = head;
head = head.next;
entity.previous = null;
entity.next = null;
}
tail = null;
}
}
class SystemList
{
public var head : System;
public var tail : System;
public function add( system : System ) : void
{
if( ! head )
{
head = tail = system;
system.next = system.previous = null;
}
else
{
for( var node : System = tail; node; node = node.previous )
{
if( node.priority <= system.priority )
{
break;
}
}
if( node == tail )
{
tail.next = system;
system.previous = tail;
system.next = null;
tail = system;
}
else if( !node )
{
system.next = head;
system.previous = null;
head.previous = system;
head = system;
}
else
{
system.next = node.next;
system.previous = node;
node.next.previous = system;
node.next = system;
}
}
}
public function remove( system : System ) : void
{
if ( head == system)
{
head = head.next;
}
if ( tail == system)
{
tail = tail.previous;
}
if (system.previous)
{
system.previous.next = system.next;
}
if (system.next)
{
system.next.previous = system.previous;
}
// N.B. Don't set system.next and system.previous to null because that will break the list iteration if node is the current node in the iteration.
}
public function removeAll() : void
{
while( head )
{
var system : System = head;
head = head.next;
system.previous = null;
system.next = null;
}
tail = null;
}
public function get( type : Class ) : System
{
for( var system : System = head; system; system = system.next )
{
if ( system is type )
{
return system;
}
}
return null;
}
}
//package
//{
import flash.utils.Dictionary;
class NodePool
{
private var tail : Node;
private var nodeClass : Class;
private var cacheTail : Node;
private var components : Dictionary;
/**
* Creates a pool for the given node class.
*/
public function NodePool( nodeClass : Class, components : Dictionary )
{
this.nodeClass = nodeClass;
this.components = components;
}
/**
* Fetches a node from the pool.
*/
internal function get() : Node
{
if ( tail )
{
var node : Node = tail;
tail = tail.previous;
node.previous = null;
return node;
}
else
{
return new nodeClass();
}
}
/**
* Adds a node to the pool.
*/
internal function dispose( node : Node ) : void
{
for each( var componentName : String in components )
{
node[ componentName ] = null;
}
node.entity = null;
node.next = null;
node.previous = tail;
tail = node;
}
/**
* Adds a node to the cache
*/
internal function cache( node : Node ) : void
{
node.previous = cacheTail;
cacheTail = node;
}
/**
* Releases all nodes from the cache into the pool
*/
internal function releaseCache() : void
{
while( cacheTail )
{
var node : Node = cacheTail;
cacheTail = node.previous;
dispose( node );
}
}
}
//}
//package {
//import ash.signals.Signal1;
import flash.display.DisplayObject;
import flash.events.Event;
import flash.utils.getTimer;
/*public*/ class FrameTickProvider extends Signal1 implements ITickProvider
{
private var displayObject : DisplayObject;
private var previousTime : Number;
private var maximumFrameTime : Number;
private var isPlaying : Boolean = false;
public var timeAdjustment : Number = 1;
public function FrameTickProvider( displayObject : DisplayObject, maximumFrameTime : Number = Number.MAX_VALUE )
{
super( Number );
this.displayObject = displayObject;
this.maximumFrameTime = maximumFrameTime;
}
public function start() : void
{
previousTime = getTimer();
displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
isPlaying = true;
}
public function stop() : void
{
isPlaying = false;
displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
}
private function dispatchTick( event : Event ) : void
{
var temp : Number = previousTime;
previousTime = getTimer();
var frameTime : Number = ( previousTime - temp ) / 1000;
if( frameTime > maximumFrameTime )
{
frameTime = maximumFrameTime;
}
dispatch( frameTime * timeAdjustment );
}
public function get playing() : Boolean
{
return isPlaying;
}
}
//}
//package ash.tick
//{
import flash.display.DisplayObject;
import flash.events.Event;
//public
class FixedTickProvider extends Signal1 implements ITickProvider
{
private var displayObject : DisplayObject;
private var frameTime : Number;
private var isPlaying : Boolean = false;
public var timeAdjustment : Number = 1;
public function FixedTickProvider( displayObject : DisplayObject, frameTime : Number )
{
super( Number );
this.displayObject = displayObject;
this.frameTime = frameTime;
}
public function start() : void
{
displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
isPlaying = true;
}
public function stop() : void
{
isPlaying = false;
displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
}
private function dispatchTick( event : Event ) : void
{
dispatch( frameTime * timeAdjustment );
}
public function get playing() : Boolean
{
return isPlaying;
}
}
//}
// -- Ash framework ends here
// ash-compiled classes
// --- Ash-compiled classes
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
class DisplayObjectRenderingSystem extends System {
private var scene:DisplayObjectContainer;
private var nodeList:NodeList;
public function DisplayObjectRenderingSystem(scene:DisplayObjectContainer):void {
this.scene = scene;
}
public function onAddedNode(node:DisplayNode):void {
scene.addChild( node.object );
}
override public function addToEngine(engine:Engine ):void {
super.addToEngine(engine);
nodeList = engine.getNodeList(DisplayNode);
nodeList.nodeAdded.add(onAddedNode);
nodeList.nodeRemoved.add(onRemovedNode);
}
public function onRemovedNode(node:DisplayNode):void {
scene.removeChild( node.object);
}
override public function update(time:Number):void {
const RAD_TO_DEG:Number = 180 / Math.PI;
for (var r:DisplayNode = nodeList.head as DisplayNode; r != null; r = r.next as DisplayNode) {
r.object.x = r.pos.x;
r.object.y = r.pos.y;
r.object.rotation = r.rot.z * RAD_TO_DEG;
}
//if (renderEngine != null) renderEngine.render();
}
}
class DisplayNode extends Node {
public var object:DisplayObject;
public var pos:Pos;
public var rot:Rot;
public function DisplayNode() {
}
}
class Vec3 {
public function Vec3(x : Number = 0,y : Number = 0,z : Number = 0) : void {
this.x = x;
this.y = y;
this.z = z;
}
public function toString() : String {
return "Vec3(" + this.x + ", " + this.y + ", " + this.z + ")";
}
public function distanceTo(v : Vec3) : Number {
var dx : Number = this.x - v.x;
var dy : Number = this.y - v.y;
var dz : Number = this.z - v.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
public function removeComponent(axis : Vec3) : void {
var scalar : Number = this.dotProduct(axis);
this.x = this.x - axis.x * scalar;
this.y = this.y - axis.y * scalar;
this.z = this.z - axis.z * scalar;
}
public function setLength(val : Number) : void {
var k : Number = val / this.length();
this.x *= k;
this.y *= k;
this.z *= k;
}
protected function normalizeWithSquared(squaredLength : Number) : void {
this.scale(1 / Math.sqrt(squaredLength));
}
public function normalize() : void {
this.scale(1 / this.length());
}
public function assignAddition(v1 : Vec3,v2 : Vec3) : void {
this.x = v1.x + v2.x;
this.y = v1.y + v2.y;
this.z = v1.z + v2.z;
}
public function copyFrom(source : Vec3) : void {
this.x = source.x;
this.y = source.y;
this.z = source.z;
}
public function saveTo(result : Vec3) : void {
result.x = this.x;
result.y = this.y;
result.z = this.z;
}
public function set(param1 : Number,param2 : Number,param3 : Number) : void {
this.x = param1;
this.y = param2;
this.z = param3;
}
public function reset() : void {
this.x = this.y = this.z = 0;
}
public function transformTransposed3(m : Mat3) : void {
this.x = m.a * this.x + m.e * this.y + m.i * this.z;
this.y = m.b * this.x + m.f * this.y + m.j * this.z;
this.z = m.c * this.x + m.g * this.y + m.k * this.z;
}
public function transform3(m : Mat3) : void {
this.x = m.a * this.x + m.b * this.y + m.c * this.z;
this.y = m.e * this.x + m.f * this.y + m.g * this.z;
this.z = m.i * this.x + m.j * this.y + m.k * this.z;
}
public function reverse() : void {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
}
public function scale(k : Number) : void {
this.x *= k;
this.y *= k;
this.z *= k;
}
public function diff(a : Vec3,b : Vec3) : void {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
}
public function sum(a : Vec3,b : Vec3) : void {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
}
public function subtract(v : Vec3) : void {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
}
public function addScaled(k : Number,v : Vec3) : void {
this.x += k * v.x;
this.y += k * v.y;
this.z += k * v.z;
}
public function add(v : Vec3) : void {
this.x += v.x;
this.y += v.y;
this.z += v.z;
}
public function crossProductSet(v : Vec3) : void {
this.x = this.y * v.z - this.z * v.y;
this.y = this.z * v.x - this.x * v.z;
this.z = this.x * v.y - this.y * v.x;
}
public function isZeroVector() : Boolean {
return this.lengthSqr() == 0;
}
public function clone() : Vec3 {
return new Vec3(this.x,this.y,this.z);
}
public function crossProduct(v : Vec3) : Vec3 {
return new Vec3(this.y * v.z - this.z * v.y,this.z * v.x - this.x * v.z,this.x * v.y - this.y * v.x);
}
public function dotProduct(v : Vec3) : Number {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
public function lengthSqr() : Number {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
public function length() : Number {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
public var z : Number;
public var y : Number;
public var x : Number;
static public var ZERO : Vec3 = new Vec3(0,0,0);
static public var X_AXIS : Vec3 = new Vec3(1,0,0);
static public var Y_AXIS : Vec3 = new Vec3(0,1,0);
static public var Z_AXIS : Vec3 = new Vec3(0,0,1);
static public var RIGHT : Vec3 = new Vec3(1,0,0);
static public var LEFT : Vec3 = new Vec3(-1,0,0);
static public var FORWARD : Vec3 = new Vec3(0,1,0);
static public var BACK : Vec3 = new Vec3(0,-1,0);
static public var UP : Vec3 = new Vec3(0,0,1);
static public var DOWN : Vec3 = new Vec3(0,0,-1);
static public function copy(v : Vec3) : Vec3 {
return new Vec3(v.x,v.y,v.z);
}
static public function createCross(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.y * v2.z - v1.z * v2.y,v1.z * v2.x - v1.x * v2.z,v1.x * v2.y - v1.y * v2.x);
}
static public function createAdd(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.x + v2.x,v1.y + v2.y,v1.z + v2.z);
}
static public function createSubtract(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
}
static public function createScale(v : Vec3,scaleAmt : *) : Vec3 {
return new Vec3(v.x * scaleAmt,v.y * scaleAmt,v.z * scaleAmt);
}
static public function createProjection(v : Vec3,axis : Vec3) : Vec3 {
var scalar : Number = Vec3.dot(v,axis);
return new Vec3(v.x - axis.x * scalar,v.y - axis.y * scalar,v.z - axis.z * scalar);
}
static public function dot(v1 : Vec3,v2 : Vec3) : Number {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
static public function lengthOf(v : Vec3) : Number {
return Math.sqrt(Vec3.squareLengthOf(v));
}
static public function squareLengthOf(v : Vec3) : Number {
return v.x * v.x + v.y * v.y + v.z * v.z;
}
static public function writeCross(v1 : Vec3,v2 : Vec3,output : Vec3) : void {
output.x = v1.y * v2.z - v1.z * v2.y;
output.y = v1.z * v2.x - v2.z * v1.x;
output.z = v1.x * v2.y - v1.y * v2.x;
}
static public function writeProjection(v : Vec3,axis : Vec3,output : Vec3) : void {
var scalar : Number = Vec3.dot(v,axis);
output.x = v.x - axis.x * scalar;
output.y = v.y - axis.y * scalar;
output.z = v.z - axis.z * scalar;
}
static public function writeSubtract(output : Vec3,input : Vec3) : void {
output.x -= input.x;
output.y -= input.y;
output.z -= input.z;
}
static public function writeAdd(output : Vec3,input : Vec3) : void {
output.x += input.x;
output.y += input.y;
output.z += input.z;
}
static public function writeScale(output : Vec3,scaleAmt : Number) : void {
output.x *= scaleAmt;
output.y *= scaleAmt;
output.z *= scaleAmt;
}
}
//package {
/*public*/ class FlockingNode extends Node {
public var vel : Vel;
public var rot : Rot;
public var pos : Pos;
public var f : Flocking;
}
//}
//package {
import flash.geom.Vector3D;
/*public*/ class Vec3Utils {
static public function copy(v : Vec3) : Vec3 {
return new Vec3(v.x,v.y,v.z);
}
static public function createCross(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.y * v2.z - v1.z * v2.y,v1.z * v2.x - v1.x * v2.z,v1.x * v2.y - v1.y * v2.x);
}
static public function createAdd(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.x + v2.x,v1.y + v2.y,v1.z + v2.z);
}
static public function createSubtract(v1 : Vec3,v2 : Vec3) : Vec3 {
return new Vec3(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
}
static public function createScale(v : Vec3,scaleAmt : Number) : Vec3 {
return new Vec3(v.x * scaleAmt,v.y * scaleAmt,v.z * scaleAmt);
}
static public function createProjection(v : Vec3,axis : Vec3) : Vec3 {
var scalar : Number = Vec3Utils.dot(v,axis);
return new Vec3(v.x - axis.x * scalar,v.y - axis.y * scalar,v.z - axis.z * scalar);
}
static public function matchValues(output : Vec3,withValue : Vec3) : void {
output.x = withValue.x;
output.y = withValue.y;
output.z = withValue.z;
}
static public function matchValuesVector3D(output : Vec3,withValue : flash.geom.Vector3D) : void {
output.x = withValue.x;
output.y = withValue.y;
output.z = withValue.z;
}
static public function dot(v1 : Vec3,v2 : Vec3) : Number {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
static public function writeCross(v1 : Vec3,v2 : Vec3,output : Vec3) : void {
output.x = v1.y * v2.z - v1.z * v2.y;
output.y = v1.z * v2.x - v2.z * v1.x;
output.z = v1.x * v2.y - v1.y * v2.x;
}
static public function writeProjection(v : Vec3,axis : Vec3,output : Vec3) : void {
var scalar : Number = Vec3Utils.dot(v,axis);
output.x = v.x - axis.x * scalar;
output.y = v.y - axis.y * scalar;
output.z = v.z - axis.z * scalar;
}
static public function normalize(v : Vec3) : void {
var sc : Number = 1 / Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
v.x *= sc;
v.y *= sc;
v.z *= sc;
}
static public function subtract(output : Vec3,input : Vec3) : void {
output.x -= input.x;
output.y -= input.y;
output.z -= input.z;
}
static public function add(output : Vec3,input : Vec3) : void {
output.x += input.x;
output.y += input.y;
output.z += input.z;
}
static public function scale(output : Vec3,scaleAmt : Number) : void {
output.x *= scaleAmt;
output.y *= scaleAmt;
output.z *= scaleAmt;
}
static public function writeSubtract(output : Vec3,v1 : Vec3,v2 : Vec3) : void {
output.x = v1.x - v2.x;
output.y = v1.y - v2.y;
output.z = v1.z - v2.z;
}
static public function getLength(v : Vec3) : Number {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
}
//}
//package {
/*public*/ class Mat3 {
public function Mat3(a : Number = 1,b : Number = 0,c : Number = 0,e : Number = 0,f : Number = 1,g : Number = 0,i : Number = 0,j : Number = 0,k : Number = 1) : void {
this.a = a;
this.b = b;
this.c = c;
this.e = e;
this.f = f;
this.g = g;
this.i = i;
this.j = j;
this.k = k;
}
public function toString() : String {
return "[Mat3 (" + this.a + ", " + this.b + ", " + this.c + "), (" + this.e + ", " + this.f + ", " + this.g + "), (" + this.i + ", " + this.j + ", " + this.k + ")]";
}
public function setFromAxisAngle(axis : Vec3,angle : Number) : void {
var c1 : Number = Math.cos(angle);
var s : Number = Math.sin(angle);
var t : Number = 1 - c1;
var x : Number = axis.x;
var y : Number = axis.y;
var z : Number = axis.z;
this.a = t * x * x + c1;
this.b = t * x * y - z * s;
this.c = t * x * z + y * s;
this.e = t * x * y + z * s;
this.f = t * y * y + c1;
this.g = t * y * z - x * s;
this.i = t * x * z - y * s;
this.j = t * y * z + x * s;
this.k = t * z * z + c1;
}
public function setRotation(rx : Number,ry : Number,rz : Number) : void {
var cosX : Number = Math.cos(rx);
var sinX : Number = Math.sin(rx);
var cosY : Number = Math.cos(ry);
var sinY : Number = Math.sin(ry);
var cosZ : Number = Math.cos(rz);
var sinZ : Number = Math.sin(rz);
var cosZsinY : Number = cosZ * sinY;
var sinZsinY : Number = sinZ * sinY;
this.a = cosZ * cosY;
this.b = cosZsinY * sinX - sinZ * cosX;
this.c = cosZsinY * cosX + sinZ * sinX;
this.e = sinZ * cosY;
this.f = sinZsinY * sinX + cosZ * cosX;
this.g = sinZsinY * cosX - cosZ * sinX;
this.i = -sinY;
this.j = cosY * sinX;
this.k = cosY * cosX;
}
public function writeToEulerAngles(angles : Vec3) : void {
if(-1 < this.i && this.i < 1) {
angles.x = Math.atan2(this.j,this.k);
angles.y = -Math.asin(this.i);
angles.z = Math.atan2(this.e,this.a);
}
else {
angles.x = 0;
angles.y = ((this.i <= -1)?Math.PI:-Math.PI);
angles.y *= 0.5;
angles.z = Math.atan2(-this.b,this.f);
}
}
public function copyFrom(m : Mat3) : void {
this.a = m.a;
this.b = m.b;
this.c = m.c;
this.e = m.e;
this.f = m.f;
this.g = m.g;
this.i = m.i;
this.j = m.j;
this.k = m.k;
}
public function toSkewSymmetric(v : Vec3) : void {
this.a = this.f = this.k = 0;
this.b = -v.z;
this.c = v.y;
this.e = v.z;
this.g = -v.x;
this.i = -v.y;
this.j = v.x;
}
public function transpose() : void {
var tmp : Number = this.b;
this.b = this.e;
this.e = tmp;
tmp = this.c;
this.c = this.i;
this.i = tmp;
tmp = this.g;
this.g = this.j;
this.j = tmp;
}
public function subtract(m : Mat3) : void {
this.a -= m.a;
this.b -= m.b;
this.c -= m.c;
this.e -= m.e;
this.f -= m.f;
this.g -= m.g;
this.i -= m.i;
this.j -= m.j;
this.k -= m.k;
}
public function add(m : Mat3) : void {
this.a += m.a;
this.b += m.b;
this.c += m.c;
this.e += m.e;
this.f += m.f;
this.g += m.g;
this.i += m.i;
this.j += m.j;
this.k += m.k;
}
public function prependTransposed(m : Mat3) : void {
this.a = this.a * m.a + this.b * m.b + this.c * m.c;
this.b = this.a * m.e + this.b * m.f + this.c * m.g;
this.c = this.a * m.i + this.b * m.j + this.c * m.k;
this.e = this.e * m.a + this.f * m.b + this.g * m.c;
this.f = this.e * m.e + this.f * m.f + this.g * m.g;
this.g = this.e * m.i + this.f * m.j + this.g * m.k;
this.i = this.i * m.a + this.j * m.b + this.k * m.c;
this.j = this.i * m.e + this.j * m.f + this.k * m.g;
this.k = this.i * m.i + this.j * m.j + this.k * m.k;
}
public function prepend(m : Mat3) : void {
this.a = this.a * m.a + this.b * m.e + this.c * m.i;
this.b = this.a * m.b + this.b * m.f + this.c * m.j;
this.c = this.a * m.c + this.b * m.g + this.c * m.k;
this.e = this.e * m.a + this.f * m.e + this.g * m.i;
this.f = this.e * m.b + this.f * m.f + this.g * m.j;
this.g = this.e * m.c + this.f * m.g + this.g * m.k;
this.i = this.i * m.a + this.j * m.e + this.k * m.i;
this.j = this.i * m.b + this.j * m.f + this.k * m.j;
this.k = this.i * m.c + this.j * m.g + this.k * m.k;
}
public function append(m : Mat3) : void {
this.a = m.a * this.a + m.b * this.e + m.c * this.i;
this.b = m.a * this.b + m.b * this.f + m.c * this.j;
this.c = m.a * this.c + m.b * this.g + m.c * this.k;
this.e = m.e * this.a + m.f * this.e + m.g * this.i;
this.f = m.e * this.b + m.f * this.f + m.g * this.j;
this.g = m.e * this.c + m.f * this.g + m.g * this.k;
this.i = m.i * this.a + m.j * this.e + m.k * this.i;
this.j = m.i * this.b + m.j * this.f + m.k * this.j;
this.k = m.i * this.c + m.j * this.g + m.k * this.k;
}
public function invert_with_determinant(det : Number) : void {
this.a = (this.f * this.k - this.g * this.j) * det;
this.b = (this.c * this.g - this.b * this.k) * det;
this.c = (this.b * this.g - this.c * this.f) * det;
this.e = (this.g * this.i - this.e * this.k) * det;
this.f = (this.a * this.k - this.c * this.i) * det;
this.g = (this.c * this.e - this.a * this.g) * det;
this.i = (this.e * this.j - this.f * this.i) * det;
this.j = (this.b * this.i - this.a * this.j) * det;
this.k = (this.a * this.f - this.b * this.e) * det;
}
public function invert() : void {
this.invert_with_determinant(this.determinant());
}
public function transformVec3To3D(vin : Vec3,vout : Vec3) : void {
vout.x = this.a * vin.x + this.b * vin.y + this.c * vin.z;
vout.y = this.e * vin.x + this.f * vin.y + this.g * vin.z;
vout.z = this.i * vin.x + this.j * vin.y + this.k * vin.z;
}
public function transformVectorTransposed(vin : Vec3,vout : Vec3) : void {
vout.x = this.a * vin.x + this.e * vin.y + this.i * vin.z;
vout.y = this.b * vin.x + this.f * vin.y + this.j * vin.z;
vout.z = this.c * vin.x + this.g * vin.y + this.k * vin.z;
}
public function transformVector(vin : Vec3,vout : Vec3) : void {
vout.x = this.a * vin.x + this.b * vin.y + this.c * vin.z;
vout.y = this.e * vin.x + this.f * vin.y + this.g * vin.z;
vout.z = this.i * vin.x + this.j * vin.y + this.k * vin.z;
}
public function clone() : Mat3 {
return new Mat3(this.a,this.b,this.c,this.e,this.f,this.g,this.i,this.j,this.k);
}
public function identity() : void {
this.a = this.f = this.k = 1;
this.b = this.c = this.e = this.g = this.i = this.j = 0;
}
public function determinant() : Number {
return 1 / (-this.c * this.f * this.i + this.b * this.g * this.i + this.c * this.e * this.j - this.a * this.g * this.j - this.b * this.e * this.k + this.a * this.f * this.k);
}
public var k : Number;
public var j : Number;
public var i : Number;
public var g : Number;
public var f : Number;
public var e : Number;
public var c : Number;
public var b : Number;
public var a : Number;
static public var IDENTITY : Mat3 = new Mat3();
static public var ZERO : Mat3 = new Mat3(0,0,0,0,0,0,0,0,0);
}
//}
//package {
/*public*/ class FlockingSystem extends System {
public function FlockingSystem() : void {
super();
this.relP = new Vec3();
this.relV = new Vec3();
this.dist = new Vec3();
this.hispos = new Vec3();
this.mypos = new Vec3();
this.hispos = new Vec3();
this.collision = new Vec3();
}
protected function predictTime(cur : FlockingNode,other : FlockingNode) : Number {
Vec3Utils.writeSubtract(this.relP,cur.pos,other.pos);
Vec3Utils.writeSubtract(this.relV,other.vel,cur.vel);
return this.relV.dotProduct(this.relP) / this.relV.lengthSqr();
}
protected function angleBetween(me : Vec3,v : Vec3) : Number {
var result : Number = Math.atan2(me.y,me.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;
}
protected function isAlmostZero(a : Vec3,min : Number = 0.15999) : Boolean {
return a.lengthSqr() < min;
}
protected function getAngle(vec : Vec3) : Number {
return Math.atan2(vec.y,vec.x);
}
protected function sign(arg : Number) : Number {
return ((arg > 0)?1:((arg < 0)?-1:0));
}
public override function update(sec : Number) : void {
var count : int;
var count2 : int;
var cur : FlockingNode = this.nodeList.head;
var time : Number;
var collisionLen : Number;
var curF : Flocking;
var curS : FlockSettings;
while(cur != null) {
count = 0;
count2 = 0;
curF = cur.f;
curS = curF.settings;
var minTime : Number = curF.minTime;
var other : FlockingNode;
curF.separation.reset();
curF.alignment.reset();
curF.cohesion.reset();
curF._aold.copyFrom(curF._a);
curF._a.reset();
other = cur.previous;
while(other != null) {
Vec3Utils.writeSubtract(this.dist,other.pos,cur.pos);
time = this.predictTime(cur,other);
if(this.dist.lengthSqr() < curS.mydistSquared) {
curF.angle = Math.abs(this.angleBetween(cur.vel,this.dist));
if(curF.angle < 2 / 3 * Math.PI) {
this.dist.scale(curS.mydist / this.dist.lengthSqr());
curF.separation.subtract(this.dist);
curF.alignment.add(other.vel);
++count;
curF.angle = Math.abs(this.angleBetween(cur.vel,other.vel));
if(curF.angle < Math.PI / 2) {
curF.cohesion.add(other.pos);
++count2;
}
}
}
if(!(time < 0. || time >= minTime)) {
this.mypos.copyFrom(cur.vel);
this.mypos.scale(time);
this.mypos.add(cur.pos);
this.hispos.copyFrom(other.vel);
this.hispos.scale(time);
this.hispos.add(other.pos);
Vec3Utils.writeSubtract(this.collision,this.mypos,this.hispos);
collisionLen = this.collision.lengthSqr();
if(!(collisionLen >= curS.mindistSquared)) {
minTime = time;
collisionLen = 1 / Math.sqrt(collisionLen);
this.collision.scale(collisionLen);
curF._a.copyFrom(this.collision);
}
}
other = other.previous;
}
other = cur.next;
while(other != null) {
Vec3Utils.writeSubtract(this.dist,other.pos,cur.pos);
time = this.predictTime(cur,other);
if(this.dist.lengthSqr() < curS.mindistSquared) {
curF.angle = Math.abs(this.angleBetween(cur.vel,this.dist));
if(curF.angle < 2 / 3 * Math.PI) {
this.dist.scale(curS.mydist / this.dist.lengthSqr());
curF.separation.subtract(this.dist);
curF.alignment.add(other.vel);
++count;
curF.angle = Math.abs(this.angleBetween(cur.vel,other.vel));
if(curF.angle < Math.PI / 2) {
curF.cohesion.add(other.pos);
++count2;
}
}
}
if(!(time < 0. || time >= minTime)) {
this.mypos.copyFrom(cur.vel);
this.mypos.scale(time);
this.mypos.add(cur.pos);
this.hispos.copyFrom(other.vel);
this.hispos.scale(time);
this.hispos.add(other.pos);
Vec3Utils.writeSubtract(this.collision,this.mypos,this.hispos);
collisionLen = this.collision.lengthSqr();
if(!(collisionLen >= curS.mindistSquared)) {
minTime = time;
collisionLen = 1 / Math.sqrt(collisionLen);
this.collision.scale(collisionLen);
curF._a.copyFrom(this.collision);
}
}
other = other.next;
}
var _a : Vec3 = curF._a;
if(this.isAlmostZero(_a)) {
curF.separation.scale(curS.seperationScale);
_a.add(curF.separation);
if(count > 0) {
curF.alignment.scale(1 / count);
curF.alignment.subtract(cur.vel);
curF.alignment.scale(1 / (curS.maxspeed * 2));
curF.alignment.scale(curS.alignmentScale);
_a.add(curF.alignment);
}
if(count2 > 0) {
curF.cohesion.scale(1 / count2);
curF.cohesion.subtract(cur.pos);
curF.cohesion.scale(1 / curS.mydist);
curF.cohesion.scale(curS.cohesionScale);
_a.add(curF.cohesion);
}
}
if(this.isAlmostZero(_a) && count == 0) {
curF.rangle += this.sign(Math.random() - 0.5) * Math.PI / 36;
_a.addScaled(0.44 / Vec3Utils.getLength(cur.vel),cur.vel);
_a.x += 0.45 * Math.sin(curF.rangle);
_a.y += 0.45 * Math.cos(curF.rangle);
}
else curF.rangle = this.getAngle(_a);
if(cur.pos.x < curS.minx) _a.x += 0.4;
else if(cur.pos.x > curS.maxx) _a.x -= 0.4;
if(cur.pos.y < curS.miny) _a.y += 0.4;
else if(cur.pos.y > curS.maxy) _a.y -= 0.4;
if(curS.turnAccelRatio > 0) {
_a.subtract(curF._aold);
var t : Number = _a.length();
if(t > 0.0001) _a.scale(curS.turnAccelRatio);
if(t >= curS.turnAccelRatio) _a.scale(curS.turnAccelRatio / t);
_a.add(curF._aold);
}
cur = cur.next;
}
cur = this.nodeList.head;
while(cur != null) {
curF = cur.f;
curS = curF.settings;
Vec3Utils.add(cur.vel,curF._a);
Vec3Utils.scale(cur.vel,1. / 12);
cur.rot.z = this.getAngle(cur.vel);
Vec3Utils.add(cur.pos,cur.vel);
Vec3Utils.scale(cur.vel,12.);
var v : Number = Vec3Utils.getLength(cur.vel);
if(v > curS.maxspeed) Vec3Utils.scale(cur.vel,curS.maxspeed / v);
else if(v < curS.minspeed) Vec3Utils.scale(cur.vel,curS.minspeed / v);
else Vec3Utils.scale(cur.vel,0.99);
cur = cur.next;
}
}
public override function addToEngine(engine : Engine) : void {
this.nodeList = engine.getNodeList(FlockingNode);
}
protected var collision : Vec3;
protected var mypos : Vec3;
protected var hispos : Vec3;
protected var dist : Vec3;
protected var relV : Vec3;
protected var relP : Vec3;
protected var nodeList : NodeList;
static protected var myr : Number = 2 / 3 * Math.PI;
static protected var scalerSpeed : Number = 1. / 12;
}
//}
//package {
/*public*/ class Pos extends Vec3 {
public function Pos(x : Number = 0,y : Number = 0,z : Number = 0) : void {
super(x,y,z);
}
}
//}
//package {
/*public*/ class Flocking {
public function Flocking() : void {
}
public function setup(flockSettings : FlockSettings) : Flocking {
this.settings = flockSettings;
this.rangle = Math.random() * 2 * Math.PI;
this.minTime = 10.0 / 3;
this.separation = new Vec3();
this.alignment = new Vec3();
this.cohesion = new Vec3();
this._a = new Vec3();
this._aold = new Vec3();
return this;
}
public var settings : FlockSettings;
public var rangle : Number;
public var minTime : Number;
public var angle : Number;
public var _aold : Vec3;
public var _a : Vec3;
public var cohesion : Vec3;
public var alignment : Vec3;
public var separation : Vec3;
static public function createFlockSettings(minDist : Number,senseDistance : Number,minx : Number = 0,miny : Number = 0,maxx2 : Number = 400,maxy2 : Number = 400,minspeed : Number = 8,maxspeed : Number = 32,turnAccelRatio : Number = 0) : FlockSettings {
var me : FlockSettings = new FlockSettings();
me.minspeed = minspeed;
me.maxspeed = maxspeed;
me.turnAccelRatio = turnAccelRatio;
me.mydist = senseDistance;
me.mydistSquared = senseDistance * senseDistance;
me.mindistSquared = minDist * minDist;
me.maxx = maxx2 - senseDistance;
me.maxy = maxy2 - senseDistance;
me.minx = senseDistance;
me.miny = senseDistance;
return me;
}
}
//}
//package {
/*public*/ class Rot extends Vec3 {
public function Rot(x : Number = 0,y : Number = 0,z : Number = 0) : void {
super(x,y,z);
}
}
//}
//package {
/*public*/ class Vel extends Vec3 {
public function Vel(x : Number = 0,y : Number = 0,z : Number = 0) : void {
super(x,y,z);
}
}
//}
//package {
/*public*/ class FlockSettings {
public function FlockSettings() : void {
}
public var turnAccelRatio : Number;
public var minspeed : Number;
public var maxspeed : Number;
public var mydist : Number;
public var mydistSquared : Number;
public var mindistSquared : Number;
public var miny : Number;
public var minx : Number;
public var maxy : Number;
public var maxx : Number;
public var seperationScale:Number = .5;
public var cohesionScale:Number = 8;
public var alignmentScale:Number = 2;
}
//}