topical media & game development
lib-flex-animation-code-02-com-foed-SteeredVehicle.ax
lib-flex-animation-code-02-com-foed-SteeredVehicle.ax
(swf
)
[ flash
]
flex
package com.foed
{
import flash.display.Sprite;
public class @ax-lib-flex-animation-code-02-com-foed-SteeredVehicle extends Vehicle
{
private var _maxForce:Number = 1;
private var _steeringForce:Vector2D;
private var _arrivalThreshold:Number = 100;
private var _wanderAngle:Number = 0;
private var _wanderDistance:Number = 10;
private var _wanderRadius:Number = 5;
private var _wanderRange:Number = 1;
private var _pathIndex:int = 0;
private var _pathThreshold:Number = 20;
private var _avoidDistance:Number = 300;
private var _avoidBuffer:Number = 20;
private var _inSightDist:Number = 200;
private var _tooCloseDist:Number = 60;
public function @ax-lib-flex-animation-code-02-com-foed-SteeredVehicle()
{
_steeringForce = new Vector2D();
super();
}
public function set maxForce(value:Number):void
{
_maxForce = value;
}
public function get maxForce():Number
{
return _maxForce;
}
public function set arriveThreshold(value:Number):void
{
_arrivalThreshold = value;
}
public function get arriveThreshold():Number
{
return _arrivalThreshold;
}
public function set wanderDistance(value:Number):void
{
_wanderDistance = value;
}
public function get wanderDistance():Number
{
return _wanderDistance;
}
public function set wanderRadius(value:Number):void
{
_wanderRadius = value;
}
public function get wanderRadius():Number
{
return _wanderRadius;
}
public function set wanderRange(value:Number):void
{
_wanderRange = value;
}
public function get wanderRange():Number
{
return _wanderRange;
}
public function set pathIndex(value:int):void
{
_pathIndex = value;
}
public function get pathIndex():int
{
return _pathIndex;
}
public function set pathThreshold(value:Number):void
{
_pathThreshold = value;
}
public function get pathThreshold():Number
{
return _pathThreshold;
}
public function set avoidDistance(value:Number):void
{
_avoidDistance = value;
}
public function get avoidDistance():Number
{
return _avoidDistance;
}
public function set avoidBuffer(value:Number):void
{
_avoidBuffer = value;
}
public function get avoidBuffer():Number
{
return _avoidBuffer;
}
public function set inSightDist(value:Number):void
{
_inSightDist = value;
}
public function get inSightDist():Number
{
return _inSightDist;
}
public function set tooCloseDist(value:Number):void
{
_tooCloseDist = value;
}
public function get tooCloseDist():Number
{
return _tooCloseDist;
}
override public function update():void
{
_steeringForce.truncate(_maxForce);
_steeringForce = _steeringForce.divide(_mass);
_velocity = _velocity.add(_steeringForce);
_steeringForce = new Vector2D();
super.update();
}
public function seek(target:Vector2D):void
{
var desiredVelocity:Vector2D = target.subtract(_position);
desiredVelocity.normalize();
desiredVelocity = desiredVelocity.multiply(_maxSpeed);
var force:Vector2D = desiredVelocity.subtract(_velocity);
_steeringForce = _steeringForce.add(force);
}
public function flee(target:Vector2D):void
{
var desiredVelocity:Vector2D = target.subtract(_position);
desiredVelocity.normalize();
desiredVelocity = desiredVelocity.multiply(_maxSpeed);
var force:Vector2D = desiredVelocity.subtract(_velocity);
_steeringForce = _steeringForce.subtract(force);
}
public function arrive(target:Vector2D):void
{
var desiredVelocity:Vector2D = target.subtract(_position);
desiredVelocity.normalize();
var dist:Number = _position.dist(target);
if(dist > _arrivalThreshold)
{
desiredVelocity = desiredVelocity.multiply(_maxSpeed);
}
else
{
desiredVelocity = desiredVelocity.multiply(_maxSpeed * dist / _arrivalThreshold);
}
var force:Vector2D = desiredVelocity.subtract(_velocity);
_steeringForce = _steeringForce.add(force);
}
public function pursue(target:Vehicle):void
{
var lookAheadTime:Number = position.dist(target.position) / _maxSpeed;
var predictedTarget:Vector2D = target.position.add(target.velocity.multiply(lookAheadTime));
seek(predictedTarget);
}
public function evade(target:Vehicle):void
{
var lookAheadTime:Number = position.dist(target.position) / _maxSpeed;
var predictedTarget:Vector2D = target.position.subtract(target.velocity.multiply(lookAheadTime));
flee(predictedTarget);
}
public function wander():void
{
var center:Vector2D = velocity.clone().normalize().multiply(_wanderDistance);
var offset:Vector2D = new Vector2D(0);
offset.length = _wanderRadius;
offset.angle = _wanderAngle;
_wanderAngle += Math.random() * _wanderRange - _wanderRange * .5;
var force:Vector2D = center.add(offset);
_steeringForce = _steeringForce.add(force);
}
public function avoid(circles:Array):void
{
for(var i:int = 0; i < circles.length; i++)
{
var circle:Circle = circles[i] as Circle;
var heading:Vector2D = _velocity.clone().normalize();
// vector between circle and vehicle:
var difference:Vector2D = circle.position.subtract(_position);
var dotProd:Number = difference.dotProd(heading);
// if circle is in front of vehicle...
if(dotProd > 0)
{
// vector to represent "feeler" arm
var feeler:Vector2D = heading.multiply(_avoidDistance);
// project difference vector onto feeler
var projection:Vector2D = heading.multiply(dotProd);
// distance from circle to feeler
var dist:Number = projection.subtract(difference).length;
// if feeler intersects circle (plus buffer),
//and projection is less than feeler length,
// we will collide, so need to steer
if(dist < circle.radius + _avoidBuffer &&
projection.length < feeler.length)
{
// calculate a force +/- 90 degrees from vector to circle
var force:Vector2D = heading.multiply(_maxSpeed);
force.angle += difference.sign(_velocity) * Math.PI / 2;
// scale this force by distance to circle.
// the further away, the smaller the force
force = force.multiply(1.0 - projection.length /
feeler.length);
// add to steering force
_steeringForce = _steeringForce.add(force);
// braking force
_velocity = _velocity.multiply(projection.length / feeler.length);
}
}
}
}
public function followPath(path:Array, loop:Boolean = false):void
{
var wayPoint:Vector2D = path [pathIndex];
if(wayPoint == null) return;
if(_position.dist(wayPoint) < _pathThreshold)
{
if(_pathIndex >= path.length - 1)
{
if(loop)
{
_pathIndex = 0;
}
}
else
{
_pathIndex++;
}
}
if(_pathIndex >= path.length - 1 && !loop)
{
arrive(wayPoint);
}
else
{
seek(wayPoint);
}
}
public function flock(vehicles:Array):void
{
var averageVelocity:Vector2D = _velocity.clone();
var averagePosition:Vector2D = new Vector2D();
var inSightCount:int = 0;
for(var i:int = 0; i < vehicles.length; i++)
{
var vehicle:Vehicle = vehicles[i] as Vehicle;
if(vehicle != this && inSight(vehicle))
{
averageVelocity = averageVelocity.add(vehicle.velocity);
averagePosition = averagePosition.add(vehicle.position);
if(tooClose(vehicle)) flee(vehicle.position);
inSightCount++;
}
}
if(inSightCount > 0)
{
averageVelocity = averageVelocity.divide(inSightCount);
averagePosition = averagePosition.divide(inSightCount);
seek(averagePosition);
_steeringForce.add(averageVelocity.subtract(_velocity));
}
}
public function inSight(vehicle:Vehicle):Boolean
{
if(_position.dist(vehicle.position) > _inSightDist) return false;
var heading:Vector2D = _velocity.clone().normalize();
var difference:Vector2D = vehicle.position.subtract(_position);
var dotProd:Number = difference.dotProd(heading);
if(dotProd < 0) return false;
return true;
}
public function tooClose(vehicle:Vehicle):Boolean
{
return _position.dist(vehicle.position) < _tooCloseDist;
}
}
}
(C) Æliens
18/6/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.