/**
* Copyright yd_niku ( http://wonderfl.net/user/yd_niku )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/lQVc
*/
// Programming Game AI by Exampleの写経です
// @see http://www.oreilly.co.jp/books/9784873113395/
package {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.utils.*;
[SWF(frameRate=60)]
public class FlashTest extends Sprite {
private var _time:Number = 0;
private var _elements:Vector.<BaseGameEntity> = new Vector.<BaseGameEntity>();
public function FlashTest() {
var world:GameWorld = new GameWorld();
/*
world:GameWorld,
position:Vector3D, rotation:Number, velocity:Vector3D, mass:Number,
maxForce:Number, maxSpeed:Number, maxTurnRate:Number, scale:Number
*/
var vehicle:Vehicle = new Vehicle(
world, new Vector3D(100, 100), 30, new Vector3D(5,4), 100,
100, 100, 10, 1
);
_elements.push( vehicle );
addChild( vehicle );
addEventListener( Event.ENTER_FRAME, enterframe );
_time = getTimer();
}
private function enterframe ( e:Event ):void {
var currentTime:Number = getTimer();
var timeElasped:Number = currentTime - _time;
timeElasped *= 0.05;
for each( var element:BaseGameEntity in _elements ) {
element.update(timeElasped);
element.render();
}
_time = currentTime;
}
}
}
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.errors.*;
class Vector2DUtil {
// bがaに対して時計回り方向にあるようなら正の値、
// 反時計回り方向にあるなら負の値を返す
public static function sign( a:Vector3D, b:Vector3D ):int {
var cross:Vector3D = a.crossProduct( b);
return cross.z == 0 ? 0: cross.z > 0 ? -1 : 1;
}
// 垂直なベクトルを返す
public static function perp( a:Vector3D ):Vector3D {
return new Vector3D( a.y, a.x );
}
}
class BaseGameEntity extends Sprite {
private static var _nextId:int = 0;
private static function get nextId():int { return _nextId++; }
private var _id:int;
public function get id():int { return _id; }
public static const DEFAULT_ENTITY_TYPE:int = -1;
private var _entityType:int;
public function get entityType():int{ return _entityType; }
public function set entityType( value:int):void { _entityType= value; }
private var _bTag:Boolean = false;
public function get isTagged():Boolean { return _bTag; }
public function tag():void { _bTag = true; }
public function unTag():void { _bTag = false; }
protected var _position:Vector3D;
public function get position():Vector3D { return _position; }
public function set position( value:Vector3D ):void { _position = value; }
protected var _scale:Vector3D= new Vector3D(1.0,1.0, 0);
public function get scale():Vector3D { return _scale; }
public function set scale( value:Vector3D ):void {
_radius *= Math.max( value.x, value.y )/Math.max( _scale.x, _scale.y );
_scale= value;
}
public function set scaleByNumber( value:Number ):void {
_radius *= ( value/Math.max(_scale.x, _scale.y) );
_scale.x = _scale.y = value;
}
protected var _radius:Number = 0.0;
public function get radius():Number { return _radius; }
public function set radius( value:Number ):void { _radius= value; }
private function setId( $id:int ):void {
_id = $id;
}
public function BaseGameEntity( entityType:int, position:Vector3D = null, radius:Number=0 ) {
super();
setId( nextId );
_entityType = entityType;
_position = position || new Vector3D();
_radius = radius;
render();
}
public function update( timeElapsed:Number ):void { throw new IllegalOperationError("You must override to 'update' method."); }
public function render():void { throw new IllegalOperationError("You must override to 'render' method."); }
//virtual bool HandleMessage(const Telegram& msg){return false;}
////entities should be able to read/write their data to a stream
//virtual void Write(std::ostream& os)const{}
//virtual void Read (std::ifstream& is){}
//public function update():void { throw new IllegalOperationError("You must override to 'update' method."); }
}
class MovingEntity extends BaseGameEntity {
protected var _velocity:Vector3D;
public function get velocity():Vector3D{ return _velocity; }
public function set velocity( value:Vector3D) :void { _velocity = value; }
public function get isSpeedMaxOut():Boolean { return _maxSpeed*_maxSpeed >= _velocity.lengthSquared; }
public function get speed():Number{ return _velocity.length; }
public function get speedSqrt():Number{ return _velocity.lengthSquared; }
protected var _heading:Vector3D;
public function get heading():Vector3D { return _heading; }
public function set heading( value:Vector3D ) :void {
if( ( value.lengthSquared - 1.0 ) < 0.00001 ) return;
_heading= value;
_side = Vector2DUtil.perp( _heading );
}
protected var _side:Vector3D;
public function get side():Vector3D{ return _side; }
protected var _mass:Number;
public function get mass():Number{ return _mass; }
protected var _maxSpeed:Number;
public function get maxSpeed():Number{ return _maxSpeed; }
public function set maxSpeed( value:Number) :void { _maxSpeed= value; }
protected var _maxForce:Number;
public function get maxForce():Number{ return _maxForce; }
public function set maxForce( value:Number) :void { _maxForce= value; }
protected var _maxTurnRate:Number;
public function get maxTurnRate():Number{ return _maxTurnRate; }
public function set maxTurnRate( value:Number) :void { _maxTurnRate= value; }
public function rotateHeadingToFacePosition( target:Vector3D ):Boolean {
var toTarget:Vector3D = target.subtract( _position );
toTarget.normalize();
var angle:Number = Math.acos( _heading.dotProduct(toTarget) );
if (angle < 0.00001) return true;
if (angle > _maxTurnRate ) angle = _maxTurnRate;
var rotationMatrix:Matrix3D = new Matrix3D();
//notice how the direction of rotation has to be determined when creating
//the rotation matrix
rotationMatrix.appendRotation( angle * Vector2DUtil.sign( _heading, toTarget ), Vector3D.Z_AXIS );
_heading = rotationMatrix.transformVector( _heading );
_velocity = rotationMatrix.transformVector( _velocity );
//finally recreate m_vSide
_side = Vector2DUtil.perp( _heading );
return false;
}
public function MovingEntity(
pos:Vector3D, radius:Number, velocity:Vector3D,
maxSpeed:Number, heading:Vector3D, mass:Number, scale:Vector3D,
maxTurnRate:Number, maxForce:Number
){
super( 0, pos, radius );
_velocity= velocity;
_heading= heading;
_mass = mass;
_scale = scale;
_side = Vector2DUtil.perp( _heading );
_maxSpeed= maxSpeed;
_maxForce= maxForce;
_maxTurnRate = maxTurnRate;
}
}
class GameWorld {}
class SteeringBehavior {
private var _vehicle:Vehicle;
public function SteeringBehavior( agent:Vehicle ) {
_vehicle = agent;
}
public function calcurate():Vector3D { return new Vector3D(-5, -3, 0); }
}
class Vehicle extends MovingEntity {
private var _world:GameWorld;
private var _steering:SteeringBehavior;
//private var _headingSmoother;
private var _smoothHeading:Vector3D = new Vector3D(); ;
public function get smoothHeading():Vector3D{ return _smoothHeading; }
private var _smoothHeadingOn:Boolean = false;
public function get isSmoothingOn():Boolean { return _smoothHeadingOn; }
public function smoothingOn():void { _smoothHeadingOn = true; }
public function smoothingOff():void { _smoothHeadingOn = false; }
public function toggleSmoothing():void { _smoothHeadingOn = !_smoothHeadingOn; }
private var _timeElapsed:Number = 0.0;
public function get timeElapsed():Number { return _timeElapsed; }
public function Vehicle(
world:GameWorld,
position:Vector3D, rotation:Number, velocity:Vector3D, mass:Number,
maxForce:Number, maxSpeed:Number, maxTurnRate:Number, scale:Number
) {
super( position, scale, velocity, maxSpeed,
new Vector3D( Math.sin(rotation),-Math.cos(rotation)),
mass, new Vector3D(scale,scale), maxTurnRate, maxForce );
_world = world;
_steering = new SteeringBehavior(this);
}
private function drawBody():void{
graphics.beginFill( 0x0000CC );
graphics.drawCircle( 0, 0, 20 );
graphics.endFill();
}
public function initializeBuffer():void {}
public override function update( $timeElapsed:Number ):void {
_timeElapsed = $timeElapsed;
var oldPosition:Vector3D = _position.clone();
var steeringForce:Vector3D = _steering.calcurate();
steeringForce.scaleBy( _timeElapsed/_mass );
_velocity.incrementBy( steeringForce );
var limit :Number = Math.min( _maxSpeed, _velocity.length );
_velocity.normalize();
_velocity.scaleBy( limit );
var addPos:Vector3D = _velocity.clone();
addPos.scaleBy( _timeElapsed );
_position.incrementBy( addPos );
if( _velocity.lengthSquared < 0.0000001 ) {
var newHeading:Vector3D = _velocity.clone();
newHeading.normalize();
_heading = newHeading;
_side = Vector2DUtil.perp( _heading );
}
/*
//EnforceNonPenetrationConstraint(this, World()->Agents());
//treat the screen as a toroid
WrapAround( position, world.cxClient(), world.cyClient());
//update the vehicle's current cell if space partitioning is turned on
if ( _steering.isSpacePartitioningOn() )
{
world.cellSpace().UpdateEntity(this, oldPosition );
}
if (isSmoothingOn())
{
_smoothedHeading = _headingSmoother.update(Heading());
}*/
}
public override function render():void {
this.x = _position.x;
this.y = _position.y;
this.scaleX = _scale.x;
this.scaleY = _scale.y;
this.graphics.clear();
drawBody();
if( _velocity) {
graphics.moveTo(0,0);
graphics.lineStyle(0, 0xFF0000 );
graphics.lineTo( _velocity.x, _velocity.y );
}
}
}