TKTank
このコードを新着タンク一覧http://flash-games.wonderfl.net/tank/list/new
に表示させるにはinfinite-tank-entry
というタグをつけてください
/**
* Copyright tkinjo ( http://wonderfl.net/user/tkinjo )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/mxWv
*/
// forked from 9re's Wonderfl Tank Game Tank Sample 1
// このコードを新着タンク一覧http://flash-games.wonderfl.net/tank/list/new
// に表示させるにはinfinite-tank-entry
// というタグをつけてください
package
{
import flash.display.*;
import flash.geom.*;
import flash.events.*;
import flash.filters.*;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import net.wonderfl.game.infinity_tank.development.*;
import net.wonderfl.game.infinity_tank.core.*;
import net.wonderfl.math.*;
[SWF(backgroundColor="#000000")]
/**
* @author 9re
*/
public class Tank extends TankBase
{
/** ==================================================
* constants
* ==================================================
*/
/** --------------------------------------------------
* action constant
*/
private const INTERCEPT_ITERATION:Number = 10;
private const INTERCEPT_ACCURACY:Number = 1;
/** --------------------------------------------------
* draw & common constant
*/
private const DEBUG:Boolean = false;
private const INTERCEPT:Boolean = true;
private const BULLET_RENDERER_URL:String = "http://swf.wonderfl.net/swf/usercode/2/2a/2a1c/2a1c4faef5a34a6cb98cd005db878f576fe8d2e4.swf";//"http://swf.wonderfl.net/swf/usercode/0/04/046c/046cee45b4334c4f2dac8dfa7ec9ea2b2b2eb27d.swf";
private const STAGE_WIDTH :Number = 600;
private const STAGE_HEIGHT:Number = 550;
private const TANK_WIDTH :Number = 50;
private const TANK_HEIGHT :Number = 30;
private const TANK_RADIUS :Number = 25;
private const GUN_WIDTH :Number = 24;
private const GUN_HEIGHT :Number = 10;
private const AFTERIMAGE_COLOR_TRANSFORM:ColorTransform = new ColorTransform(1, 1, 1, 1 - 1 / 4);
private const TANK_BODY_GLOW_FILTER:GlowFilter = new GlowFilter( 0xffffff, 1, 8, 8, 0.2 );
/** ==================================================
* variables
* ==================================================
*/
/** --------------------------------------------------
* action variables
*/
private var moveForward:Boolean = true;
private var intercept:Intercept;
private var linearIntercept:Intercept;
private var circularIntercept:Intercept;
/** --------------------------------------------------
* draw & common variables
*/
public function get stageWidth():Number { return STAGE_WIDTH; };
public function get stageHeight():Number { return STAGE_HEIGHT };
public function get tankRadius():Number { return TANK_RADIUS };
private var tankWidth:Number;
private var tankHeight:Number;
private var tankBody:Sprite;
private var gun:Sprite;
private var previousStageBitmapData:BitmapData;
private var drawTankMatrix:Matrix = new Matrix();
/** ==================================================
* methods
* ==================================================
*/
/**
*
*/
public function Tank()
{
Wonderfl.disable_capture();
//Wonderfl.capture_delay(1);
_bulletRenderer = BULLET_RENDERER_URL;
}
/**
*
*/
//override protected function init():void {
protected function init():void {
//super._init();
__init();
// draw
tankWidth = TANK_WIDTH;
tankHeight = TANK_HEIGHT;
createTankBody();
if ( DEBUG )
createDebugObject();
drawTankMatrix = new Matrix();
// action
linearIntercept = new LinearIntercept( tkBattleScene, INTERCEPT_ITERATION, INTERCEPT_ACCURACY );
circularIntercept = new CircularIntercept( tkBattleScene, INTERCEPT_ITERATION, INTERCEPT_ACCURACY );
}
/* 速度の測定に使用
private var maxLinearVelocity:Number = 0;
private var maxAngularVelocity:Number = 0;
//*/
/**
* 常に攻撃
*
* @return
*/
public function attackAction():int {
var action:int = Command.DO_NOTHING;
if ( INTERCEPT ) {
// intercept
if( Math.abs( enemyTank.angularVelocity ) <= 1 / 180 * Math.PI )
intercept = linearIntercept;
else
intercept = circularIntercept;
intercept.calculate();
}
var turnGunRight:Number;
if( INTERCEPT )
turnGunRight = intercept.turnGunRight;
else
turnGunRight = Angle.normalizeRadianM180toP180( myTank.enemyTankBearingFromGunHeading );
// set fire
if ( INTERCEPT )
/*
if (
!tkBattleScene.bulletSpeed ||
(
Math.abs(turnGunRight) <= intercept.threshold &&
!intercept.targetBumpAgainstAWall
)
)//*/
action += Command.FIRE;
else
action += Command.FIRE;
//trace( Math.abs(turnGunRight) * 180 / Math.PI , intercept.threshold * 180 / Math.PI );
// set turn gun
if( turnGunRight > 0 )
action += Command.GUN_TURN_RIGHT;
else
action += Command.GUN_TURN_LEFT;
return action;
}
/**
* 敵に対して ENEMY_BEARING_FROM_FLANKING 度に構える
*
* @return
*/
public function avoidAction():int {
var action:int = Command.DO_NOTHING;
action = antiGravityMoveAction();
return action;
}
private function antiGravityMoveAction():int {
// enemyTank
var myTankPosition:WVector2D = myTank.position;
var enemyTankPosition:WVector2D = enemyTank.position;
var enemyTankGravity:GravityPoint = new GravityPoint( enemyTankPosition.x, enemyTankPosition.y, -1000 );
var enemyTankForce:WVector2D = enemyTankGravity.force( myTankPosition );
var forceX:Number = enemyTankForce.x;
var forceY:Number = enemyTankForce.y;
// wall
var topWallGravity:GravityPoint = new GravityPoint( myTankPosition.x, 0, -1000 );
var rightWallGravity:GravityPoint = new GravityPoint( stageWidth, myTankPosition.y, -1000 );
var bottomWallGravity:GravityPoint = new GravityPoint( myTankPosition.x, stageHeight, -1000 );
var leftWallGravity:GravityPoint = new GravityPoint( 0, myTankPosition.y, -1000 );
var topWallForce:WVector2D = topWallGravity.force( myTankPosition );
var rightWallForce:WVector2D = rightWallGravity.force( myTankPosition );
var bottomWallForce:WVector2D = bottomWallGravity.force( myTankPosition );
var leftWallForce:WVector2D = leftWallGravity.force( myTankPosition );
forceY += topWallForce.y;
forceX += rightWallForce.x;
forceY += bottomWallForce.y;
forceX += leftWallForce.x;
// bullet
var enemyTankBullets:Vector.<BoundBox> = enemyTank.bullets;
for( var i:int = 0; i < enemyTankBullets.length; i++ ) {
var enemyTankBulletGravity:GravityPoint = new GravityPoint( enemyTankBullets[i].position.x, enemyTankBullets[i].position.y, -500 );
var enemyTankBulletForce:WVector2D = enemyTankBulletGravity.force( myTankPosition );
forceX += enemyTankBulletForce.x;
forceY += enemyTankBulletForce.y;
}
return goTo( new WVector2D( myTankPosition.x - forceX, myTankPosition.y - forceY ) );
}
private function goTo( destination:WVector2D ):int {
var action:int = Command.DO_NOTHING;
var myTankHeading:Number = myTank.heading;
var myTankPosition:WVector2D = myTank.position;
var destinationFromMyTank:WVector2D = destination.copy();
destinationFromMyTank.subtract(myTankPosition);
var destinationBearingFromMyTank:Number = Math.atan2(destinationFromMyTank.y, destinationFromMyTank.x);
destinationBearingFromMyTank = Angle.normalizeRadian0to360(destinationBearingFromMyTank);
var destinationBearingFromMyTankHeading:Number = destinationBearingFromMyTank - myTankHeading;
destinationBearingFromMyTankHeading = Angle.normalizeRadian0to360( destinationBearingFromMyTankHeading );
var turnRight:Number = Angle.normalizeRadianM180toP180( destinationBearingFromMyTankHeading );
if( Math.abs( turnRight ) < Angle.RADIAN_90_DEGREE ) {
action += Command.TANK_MOVE_FORWARD;
} else {
action += Command.TANK_MOVE_BACKWARD;
turnRight = Angle.normalizeRadianM180toP180( destinationBearingFromMyTankHeading + Angle.RADIAN_180_DEGREE );
}
if( turnRight > Angle.RADIAN_0_DEGREE )
action += Command.TANK_TURN_RIGHT;
else
action += Command.TANK_TURN_LEFT;
return action;
}
/**
* 敵の銃弾に当たれば方向転換
*/
override public function hit():void {
moveForward = !moveForward;
}
/** ==================================================
* draw
* ==================================================
*/
/**
*
* @param tankBodyBitmapData
*/
//override public function drawStageBitmapData(stageBitmapData:BitmapData):void
public function drawStageBitmapData(stageBitmapData:BitmapData):BitmapData
{
if( !DEBUG )
gun.rotation = _scene.myGunAngle * 180 / Math.PI;
else
gun.rotation = myTank.gunBearingFromHeading * 180 / Math.PI;
// ----------
if ( previousStageBitmapData == null )
previousStageBitmapData = new BitmapData( stageBitmapData.width, stageBitmapData.height, true, 0 );
previousStageBitmapData.colorTransform(previousStageBitmapData.rect, AFTERIMAGE_COLOR_TRANSFORM );
// -----
drawTankMatrix.identity();
if( !DEBUG ) {
drawTankMatrix.rotate( _scene.myTankAngle );
drawTankMatrix.translate( _scene.myTankPosition.x, _scene.myTankPosition.y );
} else {
drawTankMatrix.rotate( myTank.heading );
drawTankMatrix.translate( myTank.position.x, myTank.position.y );
}
previousStageBitmapData.draw(tankBody, drawTankMatrix, null, null, null, true);
// -----
stageBitmapData.colorTransform( stageBitmapData.rect, CLEAR_COLOR_TRANSFORM );
stageBitmapData.draw(previousStageBitmapData);
// ----------
if ( DEBUG ) {
var debugBitmapData:BitmapData = new BitmapData( stageBitmapData.width, stageBitmapData.height, true, 0 );
debugBitmapData = drawDebugBitmapData( debugBitmapData );
stageBitmapData.draw(debugBitmapData);
}
// -----
return stageBitmapData;
}
/**
*
*/
private function createTankBody():void {
// draw tankBody
tankBody = new Sprite();
// body
var gradientMatrix:Matrix = new Matrix();
gradientMatrix.createGradientBox( TANK_WIDTH + 10, TANK_HEIGHT + 10, 0, -5 - ( TANK_WIDTH / 2 ), -5 - ( TANK_HEIGHT / 2 ) );
tankBody.graphics.lineStyle( 1, 0xffffff, 0.5, true );
tankBody.graphics.beginGradientFill(
GradientType.RADIAL,
new Array( 0x666666, 0x333333, 0x000000 ),
new Array( 1, 1, 1 ),
new Array( 0, 160, 255 ),
gradientMatrix
);
tankBody.graphics.drawRoundRect( -( TANK_WIDTH / 2 ), -( TANK_HEIGHT / 2 ), TANK_WIDTH, TANK_HEIGHT, 10, 10 );
// direction mark
tankBody.graphics.beginFill( 0xffffff );
tankBody.graphics.moveTo( 3, 0 );
tankBody.graphics.lineTo( -3, 3 );
tankBody.graphics.lineTo( -3, -3 );
tankBody.graphics.endFill();
tankBody.filters = new Array( TANK_BODY_GLOW_FILTER );
// gun
gun = new Sprite();
gun.graphics.beginFill( 0xffffff );
gun.graphics.drawEllipse( 9, -1, 2, 2 );
gun.graphics.endFill();
tankBody.addChild( gun );
}
/** --------------------------------------------------
* debug
*/
private const DEBUG_POINT_RADIUS:Number = 30;
private const DEBUG_AFTERIMAGE_COLOR_TRANSFORM:ColorTransform = new ColorTransform(1, 1, 1, 1 - 1 / 16);
private var debugPointSprite:Sprite;
private var debugTextField:TextField;
private var debugMatrix:Matrix;
private var previousDebugBitmapData:BitmapData;
private function createDebugObject():void {
debugPointSprite = new Sprite();
debugPointSprite.graphics.lineStyle( 1, 0x00ff00, 0.2 );
debugPointSprite.graphics.drawCircle( 0, 0, DEBUG_POINT_RADIUS );
debugPointSprite.graphics.moveTo( 0, -DEBUG_POINT_RADIUS );
debugPointSprite.graphics.lineTo( 0, DEBUG_POINT_RADIUS );
debugPointSprite.graphics.moveTo( -DEBUG_POINT_RADIUS, 0 );
debugPointSprite.graphics.lineTo( DEBUG_POINT_RADIUS, 0 );
debugTextField = new TextField();
debugTextField.textColor = 0xffffff;
debugTextField.autoSize = TextFieldAutoSize.LEFT;
debugMatrix = new Matrix();
}
private function drawDebugBitmapData( debugBitmapData:BitmapData ):BitmapData {
if ( intercept == null )
return debugBitmapData;
if ( previousDebugBitmapData == null )
previousDebugBitmapData = new BitmapData( debugBitmapData.width, debugBitmapData.height, true, 0 );
previousDebugBitmapData.colorTransform(previousDebugBitmapData.rect, DEBUG_AFTERIMAGE_COLOR_TRANSFORM );
// -----
/*
// myTank.position
debugMatrix.identity();
debugMatrix.translate( myTank.position.x, myTank.position.y );
previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
//*//*
// enemyTank.position
debugMatrix.identity();
debugMatrix.translate( enemyTank.position.x, enemyTank.position.y );
previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
//*/
// intercept.impactPoint
debugMatrix.identity();
debugMatrix.translate( intercept.impactPoint.x, intercept.impactPoint.y );
previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
// -----
debugBitmapData.draw( previousDebugBitmapData );
// -----
debugTextField.text = "";/*
debugTextField.appendText("myTank.position.x = " + myTank.position.x + "\n" );
debugTextField.appendText("myTank.position.y = " + myTank.position.y + "\n" );
debugTextField.appendText("myTank.positionFromPreviousPosition.x = " + myTank.positionFromPreviousPosition.x + "\n" );
debugTextField.appendText("myTank.positionFromPreviousPosition.y = " + myTank.positionFromPreviousPosition.y + "\n" );
debugTextField.appendText("myTank.velocity.x = " + myTank.velocity.x + "\n" );
debugTextField.appendText("myTank.velocity.y = " + myTank.velocity.y + "\n" );
debugTextField.appendText("myTank.velocity.length = " + myTank.velocity.length + "\n" );
debugTextField.appendText("myTank.angularVelocity = " + myTank.angularVelocity * 180 / Math.PI + "\n" );
debugTextField.appendText("enemyTank.position.x = " + enemyTank.position.x + "\n" );
debugTextField.appendText("enemyTank.position.y = " + enemyTank.position.y + "\n" );
debugTextField.appendText("enemyTank.positionFromPreviousPosition.x = " + enemyTank.positionFromPreviousPosition.x + "\n" );
debugTextField.appendText("enemyTank.positionFromPreviousPosition.y = " + enemyTank.positionFromPreviousPosition.y + "\n" );
debugTextField.appendText("enemyTank.velocity.x = " + enemyTank.velocity.x + "\n" );
debugTextField.appendText("enemyTank.velocity.y = " + enemyTank.velocity.y + "\n" );
debugTextField.appendText("enemyTank.velocity.length = " + enemyTank.velocity.length + "\n" );
debugTextField.appendText("enemyTank.angularVelocity = " + enemyTank.angularVelocity * 180 / Math.PI + "\n" );
debugTextField.appendText("tkBattleScene.bulletSpeed = " + tkBattleScene.bulletSpeed + "\n" );
debugTextField.appendText("intercept = " + intercept + "\n" );//*/
debugTextField.appendText("intercept.impactTime = " + intercept.impactTime + "\n" );
debugTextField.appendText("intercept.impactPoint.x = " + intercept.impactPoint.x + "\n" );
debugTextField.appendText("intercept.impactPoint.y = " + intercept.impactPoint.y + "\n" );
// -----
debugBitmapData.draw( debugTextField );
// -----
return debugBitmapData;
}
/** ==================================================
* TKTankBase
* ==================================================
*/
/** --------------------------------------------------
* draw constant
*/
protected const CLEAR_COLOR_TRANSFORM:ColorTransform = new ColorTransform( 1, 1, 1, 0 );
/** --------------------------------------------------
* action variable
*/
protected var tkBattleScene:TKBattleScene;
protected var myTank:MyTank;
protected var enemyTank:EnemyTank;
private var battleMode:Boolean;
/*
public function get stageWidth():Number {
return 0;
}
public function get stageHeight():Number {
return 0;
}
protected function init():void {
super._init();
tkBattleScene = new TKBattleScene( _scene, this, stageWidth, stageHeight );
}
//*/
/**
*
*/
protected function __init():void {
tkBattleScene = new TKBattleScene( _scene, this, stageWidth, stageHeight, tankRadius );
myTank = tkBattleScene.myTank;
enemyTank = tkBattleScene.enemyTank;
}
/**
*
* @return
*/
//override final public function action():int {
override public function action():int {
//if( !_world.battleMode )
if ( !battleMode )
battleMode = true;
var action:int = Command.DO_NOTHING;
if( _scene.enemyTankPosition == null )
return action;
if( tkBattleScene == null )
_init();
tkBattleScene.run();
// -----
action += attackAction();
action += avoidAction();
return action;
}
/*
public function fireAction():int {
var action:int = Command.DO_NOTHING;
return action;
}
public function turnTankAction():int {
var action:int = Command.DO_NOTHING;
return action;
}
public function moveTankAction():int {
var action:int = Command.DO_NOTHING;
return action;
}
public function turnGunAction():int {
var action:int = Command.DO_NOTHING;
return action;
}
//*/
/** --------------------------------------------------
* draw
* --------------------------------------------------
*/
/**
*
* @param bitmapData
*/
//override final public function draw(bitmapData:BitmapData):void
override public function draw(bitmapData:BitmapData):void
{
if( tkBattleScene == null )
init();
//if( !_world.battleMode )
if( !battleMode )
tkBattleScene.run();
// -----
var stageBitmapData:BitmapData = new BitmapData( bitmapData.width, bitmapData.height, true, 0 );
stageBitmapData.draw( bitmapData );
stageBitmapData = drawStageBitmapData( stageBitmapData );
bitmapData.colorTransform( bitmapData.rect, CLEAR_COLOR_TRANSFORM );
bitmapData.draw( stageBitmapData );
}
/*
public function drawStageBitmapData(stageBitmapData:BitmapData):BitmapData {
return null;
}
//*/
}
}
/** ==================================================
* library
* ==================================================
*/
import net.wonderfl.game.infinity_tank.development.*;
import net.wonderfl.math.*;
import flash.display.*;
import flash.geom.*;
/**
* helper class
*/
class TKBattleScene {
/* ==================================================
* field
* ==================================================
*/
private var battleScene:BattleScene;
private var tank:TankBase;
/* ==================================================
* property
* ==================================================
*/
/* --------------------------------------------------
* -
*/
private var _bulletSpeed:Number;
public function get bulletSpeed():Number {
return _bulletSpeed;
}
private var _robotRadius:Number;
public function get robotRadius():Number {
return _robotRadius;
}
/* --------------------------------------------------
* stage
*/
private var _stageWidth:Number;
public function get stageWidth():Number {
return _stageWidth;
}
private var _stageHeight:Number;
public function get stageHeight():Number {
return _stageHeight;
}
private var _stageCenter:WVector2D;
public function get stageCenter():WVector2D {
return _stageCenter;
}
private var _stageDiagonal:Number;
public function get stageDiagonal():Number {
return _stageDiagonal;
}
/* --------------------------------------------------
* tank
*/
private var _myTank:MyTank;
public function get myTank():MyTank {
return _myTank;
}
private var _enemyTank:EnemyTank;
public function get enemyTank():EnemyTank {
return _enemyTank;
}
/* --------------------------------------------------
* enemyTank property
*/
public function get enemyBulletCount():int {
return battleScene.enemyBulletCount;
}
private var _enemyBullets:Vector.<BoundBox>;
public function get enemyBullets():Vector.<BoundBox> {
return _enemyBullets;
}
public function get enemyTankHeading():Number {
var enemyTankAngle:Number;
try {
enemyTankAngle = angleRegularization( battleScene.enemyTankAngle );
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return enemyTankAngle;
}
}
public function get enemyTankAngularVelocity():Number {
var enemyTankAngularVelocity:Number;
try {
enemyTankAngularVelocity = battleScene.enemyTankAngularVelocity;
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return enemyTankAngularVelocity;
}
}
public function get enemyTankVelocity():WVector2D {
var enemyTankVelocity:WVector2D = new WVector2D( 0, 0 );
try {
enemyTankVelocity = battleScene.enemyTankLinearVelocity;
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return enemyTankVelocity;
}
}
public function get enemyTankPosition():WVector2D {
var enemyTankPosition:WVector2D = new WVector2D( 0, 0 );
try {
enemyTankPosition = battleScene.enemyTankPosition;
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return enemyTankPosition;
}
}
/* --------------------------------------------------
* myTank property
*/
public function get myBulletCount():int {
return battleScene.myBulletCount;
}
private var _myBullets:Vector.<BoundBox>;
public function get myBullets():Vector.<BoundBox> {
return _myBullets;
}
public function get myGunBearingFromMyTankHeading():Number {
return angleRegularization( battleScene.myGunAngle );
}
public function get myGunAngularVelocity():Number {
return battleScene.myGunAngularVelocity;
}
public function get myTankHeading() :Number{
return angleRegularization( battleScene.myTankAngle );
}
public function get myTankAngularVelocity():Number {
return battleScene.myTankAngularVelocity;
}
public function get myTankVelocity():WVector2D {
var myTankVelocity:WVector2D = new WVector2D( 0, 0 );
try {
myTankVelocity = battleScene.myTankLinearVelocity;
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return myTankVelocity;
}
}
public function get myTankPosition():WVector2D {
var myTankPosition:WVector2D = new WVector2D( 0, 0 );
try {
myTankPosition = battleScene.myTankPosition;
} catch( e:Error ) {
//trace( "error : get enemyTankPosition" );
} finally {
return myTankPosition;
}
}
/* ==================================================
* method
* ==================================================
*/
public function TKBattleScene( battleScene:BattleScene, tank:TankBase, stageWidth:Number, stageHeight:Number, robotRadius:Number ):void {
this.battleScene = battleScene;
this.tank = tank;
_stageWidth = stageWidth;
_stageHeight = stageHeight;
_stageCenter = new WVector2D( stageWidth / 2, stageHeight / 2 );
_stageDiagonal = Math.sqrt( stageWidth * stageWidth + stageHeight * stageHeight );
_robotRadius = robotRadius;
_myTank = new MyTank( this );
_enemyTank = new EnemyTank( this );
}
private var previousBulletPosition:WVector2D;
public function run():void {
var i:int;
// enemyBullets
_enemyBullets = new Vector.<BoundBox>( enemyBulletCount );
var enemyBullet:BoundBox = battleScene.enemyBulletList;
for( i = 0; i < enemyBulletCount; i++ ) {
_enemyBullets[i] = enemyBullet;
if( enemyBullet.next != null )
enemyBullet = enemyBullet.next;
}
// myBullets
_myBullets = new Vector.<BoundBox>( myBulletCount );
var myBullet:BoundBox = battleScene.myBulletList;
for( i = 0; i < myBulletCount; i++ ) {
_myBullets[i] = myBullet;
if( myBullet.next != null )
myBullet = myBullet.next;
}
// tank update
enemyTank.update(); //enemyTank の update が先
myTank.update(); //myTank は enemyTank の情報も内部で処理する
// 後で修正する
// bulletSpeed
if ( !_bulletSpeed && battleScene.myBulletList ) {
//_bulletSpeed = battleScene.myBulletList.linearVelocity.length;
if ( previousBulletPosition == null ) {
previousBulletPosition = battleScene.myBulletList.position;
return;
}
var bulletVelocity:WVector2D = battleScene.myBulletList.position;
bulletVelocity.subtract(previousBulletPosition);
_bulletSpeed = bulletVelocity.length;
//trace( _bulletSpeed, battleScene.myBulletList.linearVelocity.length );
}
}
public function nearWall( tankAndWallDistance:Number, position:WVector2D ):Boolean {
if(
position != null &&
(
position.x <= tankAndWallDistance || stageWidth - tankAndWallDistance <= position.x ||
position.y <= tankAndWallDistance || stageHeight - tankAndWallDistance <= position.y
)
) {
return true;
}
return false;
}
private function angleRegularization( value:Number ):Number {
value = value % ( 2 * Math.PI );
if( value < 0 )
value = ( 2 * Math.PI ) + value;
return value;
}
}
class Angle {
public static const RADIAN_0_DEGREE:Number = 0;
public static const RADIAN_90_DEGREE:Number = Math.PI / 2;
public static const RADIAN_180_DEGREE:Number = Math.PI;
public static const RADIAN_270_DEGREE:Number = Math.PI / 2 * 3;
public static const RADIAN_360_DEGREE:Number = 2 * Math.PI;
public static function normalizeRadian0to360( value:Number ):Number {
value %= Angle.RADIAN_360_DEGREE;
if( value < RADIAN_0_DEGREE )
value += Angle.RADIAN_360_DEGREE;
return value;
}
public static function normalizeRadianM180toP180( value:Number ):Number {
value = normalizeRadian0to360( value );
if( value > RADIAN_180_DEGREE )
value -= Angle.RADIAN_360_DEGREE;
return value;
}
}
class Intercept {
/* ==================================================
* field
* ==================================================
*/
protected var tkBattleScene:TKBattleScene;
protected var iteration:Number;
protected var accuracy:Number;
protected var target:ModelBase;
protected var myTank:MyTank;
/* ==================================================
* property
* ==================================================
*/
private var _turnGunRight:Number;
public function get turnGunRight():Number {
return _turnGunRight;
}
private var _impactTime:Number;
public function get impactTime():Number {
return _impactTime;
}
private var _impactPoint:WVector2D;
public function get impactPoint():WVector2D {
return _impactPoint;
}
private var _threshold:Number;
public function get threshold():Number {
return _threshold;
}
private var _targetBumpAgainstAWall:Boolean;
public function get targetBumpAgainstAWall():Boolean {
return _targetBumpAgainstAWall;
}
/* ==================================================
* method
* ==================================================
*/
public function Intercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
this.tkBattleScene = tkBattleScene;
this.iteration = iteration;
this.accuracy = accuracy;
this.myTank = tkBattleScene.myTank;
this.target = tkBattleScene.enemyTank;
}
public function calculate():void {
// impactTime
_impactTime = getImpactTime();
// impactPoint
_impactPoint = getTargetEstimatedPosition(impactTime);
// targetPositionFromMyTank
var myTankPosition:WVector2D = myTank.position;
var targetPositionFromMyTank:WVector2D = impactPoint.copy();
targetPositionFromMyTank.subtract(myTankPosition);
// goalBulletHeading
var goalBulletHeading:Number = Math.atan2(targetPositionFromMyTank.y,targetPositionFromMyTank.x);
goalBulletHeading = Angle.normalizeRadian0to360( goalBulletHeading );
// turnBulletHeading
var myGunHeading:Number = myTank.gunHeading;
_turnGunRight = goalBulletHeading - myGunHeading;
_turnGunRight = Angle.normalizeRadianM180toP180( _turnGunRight );
// threshold
_threshold = Math.atan( target.radius / targetPositionFromMyTank.length );
// targetBumpAgainstAWall
if (
impactPoint.x < 0 || tkBattleScene.stageWidth < impactPoint.x ||
impactPoint.y < 0 || tkBattleScene.stageHeight < impactPoint.y
)
_targetBumpAgainstAWall = true;
else
_targetBumpAgainstAWall = false;
}
private function getImpactTime():Number {
var estimatedMaxImpactTime:Number = estimatedMaxImpactTime();
var time:Vector.<Number> = Vector.<Number>([ 0, estimatedMaxImpactTime ]);
var distance:Vector.<Number> = Vector.<Number>([ estimatedDistanceFromBulletToTarget( time[0] ), estimatedDistanceFromBulletToTarget( time[1] ) ]);
var iterationI:int = ( estimatedMaxImpactTime >= 5 ) ? estimatedMaxImpactTime : 5;
var timeI:Vector.<Number> = new Vector.<Number>(iterationI);
var distanceI:Vector.<Number> = new Vector.<Number>(iterationI);
timeI[0] = time[0];
timeI[iterationI - 1] = time[1];
distanceI[0] = distance[0];
distanceI[iterationI - 1] = distance[1];
var j:int;
// timeI and distanceI
for ( j = 1; j < iterationI - 1; j++ ) {
timeI[j] = time[0] + ( ( ( time[1] - time[0] ) / iterationI ) * j );
distanceI[j] = estimatedDistanceFromBulletToTarget( timeI[j] );
}
// minDistanceIIndex
var minDistanceIIndex:int = 0;
for ( j = 1; j < iterationI; j++ ) {
if( distanceI[minDistanceIIndex] > distanceI[j] )
minDistanceIIndex = j;
}
if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
return timeI[minDistanceIIndex];
/*trace(estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]));
if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
return timeI[minDistanceIIndex];
//*/
// impactTime
var impactTime:Number;
if( minDistanceIIndex == 0 )
impactTime = getImpactTimeSecant( timeI[minDistanceIIndex], timeI[minDistanceIIndex + 1] );
else if( minDistanceIIndex == iterationI - 1 )
impactTime = getImpactTimeSecant( timeI[minDistanceIIndex - 1], timeI[minDistanceIIndex] );
else {
if( distanceI[minDistanceIIndex - 1] - distanceI[minDistanceIIndex] < distanceI[minDistanceIIndex + 1] - distanceI[minDistanceIIndex] )
impactTime = getImpactTimeSecant( timeI[minDistanceIIndex - 1], timeI[minDistanceIIndex] );
else
impactTime = getImpactTimeSecant( timeI[minDistanceIIndex], timeI[minDistanceIIndex + 1] );
}
/*
if( impactTime != 0 )
return impactTime;
//*///*
if( estimatedDistanceFromBulletToTarget( impactTime ) < accuracy )
return impactTime;
//*/
// minDistanceIIndex2
var minDistanceIIndex2:int = ( minDistanceIIndex > 1 ) ? 0 : minDistanceIIndex + 2;
for ( j = 0; j < iterationI; j++ ) {
if( minDistanceIIndex - 1 <= j && j <= minDistanceIIndex + 1 )
continue;
if( distanceI[minDistanceIIndex2] > distanceI[j] )
minDistanceIIndex2 = j;
}
// impactTime2
var impactTime2:Number;
if( minDistanceIIndex2 == 0 )
impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2], timeI[minDistanceIIndex2 + 1] );
else if( minDistanceIIndex2 == iterationI - 1 )
impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2 - 1], timeI[minDistanceIIndex2] );
else {
if( distanceI[minDistanceIIndex2 - 1] - distanceI[minDistanceIIndex2] < distanceI[minDistanceIIndex2 + 1] - distanceI[minDistanceIIndex2] )
impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2 - 1], timeI[minDistanceIIndex2] );
else
impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2], timeI[minDistanceIIndex2 + 1] );
}
/*
if( impactTime2 != 0 )
return impactTime2;
//*///*
if( estimatedDistanceFromBulletToTarget( impactTime2 ) < accuracy )
return impactTime2;
//*/
if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
return timeI[minDistanceIIndex];
//trace(0, impactTime, estimatedDistanceFromBulletToTarget( impactTime ));
//trace(1, impactTime2, estimatedDistanceFromBulletToTarget( impactTime2 ));
return 0;
}
/**
* 割線法で近似解を求める。
* 二分法とはさみうち法は、解く方程式の性質により区間[a,b]内に解を保持することができないので使用不可。
* ニュートン法は、解く方程式を微分した式の解を求める関数を用意するのが面倒。
*
* @return
*/
private function getImpactTimeSecant( time0:Number, time1:Number ):Number {
var distance0:Number = estimatedDistanceFromBulletToTarget( time0 );
for( var i:Number = 0; i < iteration; i++ ) {
var distance1:Number = estimatedDistanceFromBulletToTarget( time1 );
var time2:Number = time1 - distance1 * ( time1 - time0 ) / ( distance1 - distance0 );
time0 = time1;
distance0 = distance1;
time1 = time2;
if( distance0 < accuracy )
return time0;
}
//return 0;
return time0;
}
private function estimatedMaxImpactTime():Number {
// targetPositionFromMyTank
var targetPositionFromMyTank:WVector2D = target.position;
targetPositionFromMyTank.subtract( myTank.position );
// targetVelocity
var targetVelocity:WVector2D = target.velocity;
var targetSpeed:Number = targetVelocity.length;
var bulletSpeed:Number = tkBattleScene.bulletSpeed;
var time:Number = 0;
if( bulletSpeed > targetSpeed ) {
// T = distanceFromTargetPositionToMyTank / bulletSpeed
time = targetPositionFromMyTank.length / bulletSpeed;
// T * enemyTankSpeed + Tx * enemyTankSpeed = Tx * bulletSpeed
time += Math.abs( ( time * targetSpeed ) / ( bulletSpeed - targetSpeed ) );
}
// 改良の余地あり(弾が壁に当たるまでの時間にするなど)
if ( bulletSpeed <= targetSpeed || time > tkBattleScene.stageDiagonal / bulletSpeed )
time = tkBattleScene.stageDiagonal / bulletSpeed;
return time;
}
private function estimatedDistanceFromBulletToTarget(time:Number):Number {
var bulletSpeed:Number = tkBattleScene.bulletSpeed;
var myTankPosition:WVector2D = myTank.position;
var estimatedDistanceFromBulletToTarget:WVector2D = getTargetEstimatedPosition(time);
estimatedDistanceFromBulletToTarget.subtract( myTankPosition );
return Math.abs( ( estimatedDistanceFromBulletToTarget.length - myTank.radius ) - bulletSpeed * time );
}
/* --------------------------------------------------
* abstract
*/
protected function getTargetEstimatedPosition(time:Number):WVector2D {
return new WVector2D( 0, 0 );
}
}
class LinearIntercept extends Intercept {
public function LinearIntercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
super( tkBattleScene, iteration, accuracy );
}
override protected function getTargetEstimatedPosition(time:Number):WVector2D {
var targetPosition:WVector2D = target.position;
var targetVelocity:WVector2D = target.velocity;
var x:Number = targetPosition.x + targetVelocity.x * time;
var y:Number = targetPosition.y + targetVelocity.y * time;
return new WVector2D( x, y );
}
}
class CircularIntercept extends Intercept {
public function CircularIntercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
super( tkBattleScene, iteration, accuracy );
}
// 式が微妙な感じなので暇があれば修正する
override protected function getTargetEstimatedPosition(time:Number):WVector2D {
var pointA:Point = new Point( target.getPosition(0).x, target.getPosition(0).y );
var pointB:Point = new Point( target.getPosition(1).x, target.getPosition(1).y );
var pointC:Point = new Point( target.getPosition(2).x, target.getPosition(2).y );
var centerPoint:Point = getCenterPoint( pointA, pointB, pointC );
var radius:Number = getRadius( centerPoint, pointA );
var pointAFromPointB:Point = pointA.subtract(pointB);
var angularVelocity:Number = Math.asin( pointAFromPointB.length / ( 2 * radius ) ) * 2 ;
if( target.angularVelocity < 0 ) {
angularVelocity *= -1;
radius *= -1;
}
//double angularVelocity = target.velocity().length() / radius;
var targetBearingFromCircleCenter:Number = Angle.normalizeRadian0to360( Math.atan2( pointAFromPointB.y, pointAFromPointB.x ) + Angle.RADIAN_270_DEGREE );
var nextTargetHeading:Number = ( targetBearingFromCircleCenter + angularVelocity * time ) % Angle.RADIAN_360_DEGREE;
var x:Number = pointA.x + radius * ( Math.cos(nextTargetHeading) - Math.cos(targetBearingFromCircleCenter) );
var y:Number = pointA.y + radius * ( Math.sin(nextTargetHeading) - Math.sin(targetBearingFromCircleCenter) );
return new WVector2D( x, y );
}
}
class ModelBase {
/* ==================================================
* field
* ==================================================
*/
protected var tkBattleScene:TKBattleScene;
/* ==================================================
* property
* ==================================================
*/
/* --------------------------------------------------
*
*/
protected var _radius:Number;
public function get radius():Number {
return _radius;
}
/* --------------------------------------------------
* basic
*/
protected var _positions:Vector.<WVector2D> = new Vector.<WVector2D>(10);
public function getPosition( index:int ):WVector2D {
return _positions[index].copy();
}
protected var _position:WVector2D = new WVector2D( 0, 0 );
public function get position():WVector2D {
return _position.copy();
}
protected var _previousHeading:Number;
public function get previousHeading():Number {
return _previousHeading;
}
protected var _heading:Number;
public function get heading():Number {
return _heading;
}
protected var _velocity:WVector2D = new WVector2D( 0, 0 );
public function get velocity():WVector2D {
return _positionFromPreviousPosition.copy();//_velocity.copy();
}
protected var _angularVelocity:Number;
public function get angularVelocity():Number {
return _heading - _previousHeading;//_angularVelocity;
}
/* --------------------------------------------------
*
*/
protected var _positionFromPreviousPosition:WVector2D = new WVector2D( 0, 0 );
public function get positionFromPreviousPosition():WVector2D {
return _positionFromPreviousPosition.copy();
}
protected var _movedDirection:Number;
public function get movedDirection():Number {
return _movedDirection;
}
/* ==================================================
* method
* ==================================================
*/
public function ModelBase( tkBattleScene:TKBattleScene ):void {
this.tkBattleScene = tkBattleScene;
for( var i:uint = 0; i < _positions.length; i++ )
_positions[i] = new WVector2D( 0, 0 );
}
public function update():void {
// positions
for( var i:uint = 0; i < _positions.length - 1; i++ )
_positions[_positions.length - ( 1 + i ) ] = _positions[_positions.length - ( 2 + i )];
_positions[0] = position;
// positionFromPreviousPosition
_positionFromPreviousPosition = position;
_positionFromPreviousPosition.subtract( getPosition(1) );
// movedDirection
_movedDirection = Math.atan2( velocity.y, velocity.x );
_movedDirection = Angle.normalizeRadian0to360( _movedDirection );
/*
_movedDirection = Math.atan2( movedDistance.y, movedDistance.x );
_movedDirection = Angle.normalizeRadian0to360( _movedDirection );
//*/
}
}
class TankModel extends ModelBase {
/* ==================================================
* property
* ==================================================
*/
/* --------------------------------------------------
* sub class calculate
*/
protected var _bullets:Vector.<BoundBox>;
public function get bullets():Vector.<BoundBox> {
return _bullets;
}
protected var _gunBearingFromHeading:Number;
public function get gunBearingFromHeading():Number {
return _gunBearingFromHeading;
}
/* --------------------------------------------------
* this class calculate
*/
protected var _gunHeading:Number;
public function get gunHeading():Number {
return _gunHeading;
}
protected var _bearingFromStageCenter:Number;
public function get bearingFromStageCenter():Number {
return _bearingFromStageCenter;
}
protected var _movedDirectionFromHeading:Number;
public function get movedDirectionFromHeading():Number {
return _movedDirectionFromHeading;
}
protected var _velocityFromHeading:Number;
public function get velocityFromHeading():Number {
return _velocityFromHeading;
}
/* ==================================================
* method
* ==================================================
*/
public function TankModel( tkBattleScene:TKBattleScene ):void {
super( tkBattleScene );
_radius = tkBattleScene.robotRadius;
}
public override function update():void {
super.update();
// gunHeading
_gunHeading = heading + gunBearingFromHeading;
_gunHeading = Angle.normalizeRadian0to360( _gunHeading );
// bearingFromStageCenter
var stageCenter:WVector2D = tkBattleScene.stageCenter;
var positionFromStageCenter:WVector2D = position;
positionFromStageCenter.subtract(stageCenter);
_bearingFromStageCenter = Math.atan2( positionFromStageCenter.y, positionFromStageCenter.x );
_bearingFromStageCenter = Angle.normalizeRadian0to360( _bearingFromStageCenter );
// movedDirectionFromHeading
_movedDirectionFromHeading = movedDirection - heading;
_movedDirectionFromHeading = Angle.normalizeRadian0to360( _movedDirectionFromHeading );
// velocityFromHeading
_velocityFromHeading = ( movedDirectionFromHeading + Angle.RADIAN_90_DEGREE ) % Angle.RADIAN_360_DEGREE;
if( 0 <= _velocityFromHeading && _velocityFromHeading < Math.PI )
_velocityFromHeading = velocity.length;
else
_velocityFromHeading = -velocity.length;
}
}
class EnemyTank extends TankModel {
/* ==================================================
* method
* ==================================================
*/
public function EnemyTank( tkBattleScene:TKBattleScene ):void {
super( tkBattleScene );
}
public override function update():void {
_bullets = tkBattleScene.enemyBullets;
_previousHeading = heading;
_position = tkBattleScene.enemyTankPosition;
_heading = tkBattleScene.enemyTankHeading;
_velocity = tkBattleScene.enemyTankVelocity;
_angularVelocity = tkBattleScene.enemyTankAngularVelocity;
super.update();
}
}
class MyTank extends TankModel {
/* ==================================================
* field
* ==================================================
*/
protected var enemyTank:EnemyTank;
/* ==================================================
* property
* ==================================================
*/
/* --------------------------------------------------
* enemyTank
*/
protected var _enemyTankPositionFromPosition:WVector2D = new WVector2D( 0, 0 );
public function get enemyTankPositionFromPosition():WVector2D {
return _enemyTankPositionFromPosition.copy();
}
protected var _enemyTankBearingFromPosition:Number;
public function get enemyTankBearingFromPosition():Number {
return _enemyTankBearingFromPosition;
}
protected var _enemyTankBearingFromHeading:Number;
public function get enemyTankBearingFromHeading():Number {
return _enemyTankBearingFromHeading;
}
protected var _enemyTankBearingFromGunHeading:Number;
public function get enemyTankBearingFromGunHeading():Number {
return _enemyTankBearingFromGunHeading;
}
/* ==================================================
* method
* ==================================================
*/
public function MyTank( tkBattleScene:TKBattleScene ):void {
super( tkBattleScene );
}
override public function update():void {
if( enemyTank == null )
enemyTank = tkBattleScene.enemyTank;
_previousHeading = heading;
_bullets = tkBattleScene.myBullets;
_position = tkBattleScene.myTankPosition;
_heading = tkBattleScene.myTankHeading;
_velocity = tkBattleScene.myTankVelocity;
_angularVelocity = tkBattleScene.myTankAngularVelocity;
_gunBearingFromHeading = tkBattleScene.myGunBearingFromMyTankHeading;
super.update();
// positionFromEnemyTankPosition
_enemyTankPositionFromPosition = enemyTank.position;
_enemyTankPositionFromPosition.subtract( position );
// enemyTankBearing
_enemyTankBearingFromPosition = Math.atan2( enemyTankPositionFromPosition.y, enemyTankPositionFromPosition.x );
if( _enemyTankBearingFromPosition < 0 )
_enemyTankBearingFromPosition = ( _enemyTankBearingFromPosition + Angle.RADIAN_360_DEGREE ) % Angle.RADIAN_360_DEGREE;
// enemyTankBearingFromGunHeading
_enemyTankBearingFromGunHeading = enemyTankBearingFromPosition - gunHeading;
_enemyTankBearingFromGunHeading = Angle.normalizeRadian0to360( _enemyTankBearingFromGunHeading );
// enemyTankBearingFromHeading
_enemyTankBearingFromHeading = enemyTankBearingFromPosition - heading;
_enemyTankBearingFromHeading = Angle.normalizeRadian0to360( _enemyTankBearingFromHeading );
}
}
class GravityPoint extends WVector2D {
private var _power:Number;
public function get power():Number {
return _power;
}
public function force( point:WVector2D ):WVector2D {
var positionFromPoint:WVector2D = copy();
positionFromPoint.subtract( point );
var forceLength:Number = power / Math.pow( positionFromPoint.length, 2 );
var angle:Number = Angle.normalizeRadianM180toP180(Math.PI/2 - Math.atan2(point.y - y, point.x - x));
var forceX:Number = Math.sin(angle) * forceLength;
var forceY:Number = Math.cos(angle) * forceLength;
return new WVector2D( forceX, forceY );
}
public function GravityPoint( x:Number, y:Number, power:Number):void {
super(x,y);
_power = power;
}
}
function getRadius( centerPoint:Point, pointA:Point ):Number {
var pointAFromCenterPoint:Point = pointA.subtract(centerPoint);
var radius:Number = pointAFromCenterPoint.length;
return radius;
}
function getCenterPoint( pointA:Point, pointB:Point, pointC:Point ):Point {
var lineSegmentABPerpendicularBisectorEquationAB:Point;
var lineSegmentBCPerpendicularBisectorEquationAB:Point;
var lineSegmentABPerpendicularBisectorSlope:Number = -1 / slope( pointA, pointB );
var lineSegmentBCPerpendicularBisectorSlope:Number = -1 / slope( pointB, pointC );
var lineABMiddlePoint:Point = middlePoint( pointA, pointB );
var lineBCMiddlePoint:Point = middlePoint( pointB, pointC );
var lineSegmentABPerpendicularBisectorB:Number = linearEquationsSolutionB( lineABMiddlePoint, lineSegmentABPerpendicularBisectorSlope );
var lineSegmentBCPerpendicularBisectorB:Number = linearEquationsSolutionB( lineBCMiddlePoint, lineSegmentBCPerpendicularBisectorSlope );
lineSegmentABPerpendicularBisectorEquationAB = new Point(lineSegmentABPerpendicularBisectorSlope, lineSegmentABPerpendicularBisectorB);
lineSegmentBCPerpendicularBisectorEquationAB = new Point(lineSegmentBCPerpendicularBisectorSlope, lineSegmentBCPerpendicularBisectorB);
var centerPoint:Point = simultaneousEquationsSolutionXY( lineSegmentABPerpendicularBisectorEquationAB, lineSegmentBCPerpendicularBisectorEquationAB );
return centerPoint;
}
function slope(point1:Point, point2:Point):Number {
var x1:Number = point1.x;
var y1:Number = point1.y;
var x2:Number = point2.x;
var y2:Number = point2.y;
var a:Number = ( y2 - y1 ) / ( x2 - x1 );
return a;
}
function middlePoint(point1:Point, point2:Point):Point {
var x1:Number = point1.x;
var y1:Number = point1.y;
var x2:Number = point2.x;
var y2:Number = point2.y;
var x:Number = ( x1 + x2 ) / 2;
var y:Number = ( y1 + y2 ) / 2;
return new Point(x, y);
}
function linearEquationsSolutionB(point:Point, a:Number):Number {
var x:Number = point.x;
var y:Number = point.y;
var b:Number = y - a * x;
return b;
}
function simultaneousEquationsSolutionXY(point1:Point, point2:Point):Point {
var a1:Number = point1.x;
var b1:Number = point1.y;
var a2:Number = point2.x;
var b2:Number = point2.y;
var x:Number = ( b1 - b2 ) / ( a2 - a1 );
var y:Number = a1 * x + b1;
return new Point(x,y);
}