UFO_Tank
forked from TankTemplate (diff: 487)
このコードを新着タンク一覧http://flash-games.wonderfl.net/tank/list/new に表示させるにはinfinite-tank-entry というタグをつけてください
♥0 |
Line 332 |
Modified 2010-01-01 19:34:16 |
MIT License
archived:2017-03-10 14:09:31
| (replaced)
ActionScript3 source code
/**
* Copyright cpu_t ( http://wonderfl.net/user/cpu_t )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/bLKh
*/
// forked from cpu_t's TankTemplate
// forked from cpu_t's EscapeTank
// 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.Graphics;
import flash.display.Sprite;
import flash.display.BitmapData;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.InterpolationMethod;
import net.wonderfl.game.infinity_tank.development.*;
import net.wonderfl.math.*;
import net.wonderfl.utils.SequentialLoader;
/**
* @author 9re
*/
// クラス名は必ずTankにして、TankBaseクラスを拡張して下さい
// http://flash-games.wonderfl.net/static/asdoc/net/wonderfl/game/infinity_tank/tank/TankBase.html
public class Tank extends TankBase
{
private var _spTank:Sprite;
private var _spBattery:Sprite;
private var _spUFO:Sprite;
private var _spOutput:Sprite;
private var _spOutputField:Sprite;
private var _ctfm:ColorTransform;
private var _mat:Matrix = new Matrix(1, 0, 0, 1, 5, 15);
private var _wmat:WMatrix2D = new WMatrix2D;
private var g:Graphics;
private const CenterPos:WVector2D = new WVector2D(300, 275);
private var goForward:Boolean = true;
private var moveAvoid:Boolean = false;
private var targetPos:WVector2D = new WVector2D(300, 275);
private var avoidCount:Number = 0;
private var moveTargetAngle:Number = 0;
public function Tank()
{
// 弾のクラスのurlをセットしてください
// 弾のドキュメントクラスはBulletRendererBaseクラスを拡張したBulletRendererクラスである必要があります
// 弾の一覧は、http://flash-games.wonderfl.net/tank/list/bullets
// にあります.
// http://flash-games.wonderfl.net/static/asdoc/net/wonderfl/game/infinity_tank/development/BulletRendererBase.html
_bulletRenderer = "http://swf.wonderfl.net/swf/usercode/0/04/046c/046cee45b4334c4f2dac8dfa7ec9ea2b2b2eb27d.swf";
_spTank = new Sprite;
_spBattery = new Sprite;
_spUFO = new Sprite;
_spOutput = new Sprite;
_spOutputField = new Sprite;
initDraw();
_ctfm = new ColorTransform(1, 1, 1, 0);
super();
}
// ※タンクの動きのメイン関数
// 5フレームに1度呼ばれるcallback
// 現在のゲームの状態を見るには、_sceneオブジェクトのプロパティーを 調べます.
// @see http://flash-games.wonderfl.net/static/asdoc/net/wonderfl/game/infinity_tank/development/BattleScene.html
override public function action():int
{
// Command.FIRE 弾を撃つ
// Command.GUN_TURN_RIGHT 砲台を右旋回
// Command.GUN_TURN_LEFT 砲台を左旋回
// Command.TANK_MOVE_FORWARD 前進
// Command.TANK_MOVE_BACKWARD 後進
// Command.TANK_TURN_LEFT 左に旋回
// Command.TANK_TURN_RIGHT 右に旋回
// command.addCommand(Command.コマンド);//実行する動きに追加
// return command.action; この関数の最後で実行する
// _spOutputをクリア
g = _spOutput.graphics;
g.clear();
g = _spOutputField.graphics;
g.clear();
var command:Command = new Command;
// AI設定部分
var t:Number;
var tempVec:WVector2D;
// 色々取得
var myPos:WVector2D = _scene.myTankPosition;
var enemyPos:WVector2D = _scene.enemyTankPosition;
var enemyVec:WVector2D = _scene.enemyTankLinearVelocity;
tempVec = enemyPos.copy();
tempVec.subtract(myPos);
var enemyDistance:Number = tempVec.length;
// 敵の弾で、自機に当たりそうな物をalertEnemyShotに格納
var enemyShot:BoundBox = _scene.enemyBulletList;
var alertEnemyShot:Vector.<BoundBox> = new Vector.<BoundBox>;
while (enemyShot != null) {
var enemyShotPos:WVector2D = enemyShot.position.copy();
var enemyShotDirection:WVector2D = enemyShot.linearVelocity.copy();
// 弾が自機に向かっていたら、alertEnemyShotにpush
tempVec = myPos.copy();
tempVec.subtract(enemyShotPos);
if (WMath.dot2D(tempVec, enemyShotDirection) > 0) {
alertEnemyShot.push(enemyShot);
}
// 次の弾を取得
enemyShot = enemyShot.next;
}
var _alert:Number = 9999;
for each(var alertShot:BoundBox in alertEnemyShot) {
// 自機に当たりそうかどうか
if (checkCrclLine(myPos, 80, alertShot.position, alertShot.linearVelocity)) {
// 自機に一番近い弾を見る
tempVec = myPos.copy();
tempVec.subtract(alertShot.position);
if (tempVec.length < _alert) {
_alert = tempVec.length;
// 中央付近を維持している
if (checkCrclPoint(CenterPos, 250, myPos)) {
// さっきまで逃げていなかったなら方向を計算
if (moveAvoid == false) {
moveAvoid = true;
// 自機が弾道のどちら側にいるか
t = Math.sin(WMath.modulo2PI(vecToRad(tempVec) - vecToRad(alertShot.linearVelocity)));
if (WMath.modulo2PI(Math.sin(_scene.myTankAngle) - vecToRad(alertShot.linearVelocity)) < 0) {
if (t > 0) goForward = true;
else goForward = false;
} else {
if (t < 0) goForward = true;
else goForward = false;
}
}
}else {
moveAvoid = true;
// 中央から離れすぎている
tempVec = myPos.copy();
tempVec.subtract(CenterPos);
if (Math.cos(WMath.modulo2PI(_scene.myTankAngle - vecToRad(tempVec))) < 0) goForward = true;
else goForward = false;
}
}
}
}
// どの弾にも当たりそうにない
if (_alert == 9999) {
// 逃げない
moveAvoid = false;
}
// 逃げるならとりあえず逃げる
if (moveAvoid) {
command.addCommand(getForward());
}else {
tempVec = myPos.copy();
tempVec.subtract(CenterPos);
if (Math.cos(WMath.modulo2PI(_scene.myTankAngle - vecToRad(tempVec))) < 0) goForward = true;
else goForward = false;
if (!checkCrclPoint(CenterPos, 150, myPos) || (enemyDistance < 200))
command.addCommand(getForward());
}
// 敵位置予想
tempVec = enemyPos.copy();
tempVec.add(enemyVec);
// 敵と水平な角度を目指す
tempVec.subtract(CenterPos);
moveTargetAngle = WMath.modulo2PI(vecToRad(tempVec) + 90 * Math.PI / 180);
// 自機旋回
t = WMath.modulo2PI(moveTargetAngle - _scene.myTankAngle);
if (t < Math.PI / 2 || (t > Math.PI && t < Math.PI * 3 / 2)) command.addCommand(Command.TANK_TURN_RIGHT);
else command.addCommand(Command.TANK_TURN_LEFT);
/*
var myPos:WVector2D = _scene.myTankPosition;
// 敵の弾を取得
var enemyShot:BoundBox = _scene.enemyBulletList;
var alertEnemyShotDirection:Vector.<WVector2D> = new Vector.<WVector2D>;
while (enemyShot != null) {
var enemyShotPos:WVector2D = enemyShot.position;
var enemyShotDirection:WVector2D = new WVector2D(Math.cos(enemyShot.rotation), Math.sin(enemyShot.rotation));
// 敵の弾に当たりそうな場合、alertEnemyShotに追加
if (checkCrclLine(myPos, 60, enemyShot.position, enemyShotDirection)) {
alertEnemyShotDirection.push(enemyShotDirection);
g = _spOutputField.graphics;
g.lineStyle(1, 0x800080, 1.0);
g.beginFill(0x000000, 0.0);
g.drawCircle(enemyShotPos.x, enemyShotPos.y, 30);
g.lineStyle(1, 0x800080, 1.0);
g.moveTo(enemyShotPos.x, enemyShotPos.y);
enemyShotDirection.scale(100);
enemyShotPos.add(enemyShotDirection);
g.lineTo(enemyShotPos.x, enemyShotPos.y);
g.lineStyle(1, 0xffffff, 0.5);
g.beginFill(0x000000, 0.0);
g.drawCircle(myPos.x, myPos.y, 60);
}
enemyShot = enemyShot.next;
}
var _moveflg:Boolean = true;
// 自機の速度を取得
var mySpeed:WVector2D = _scene.myTankLinearVelocity;
var CenterPos:WVector2D = new WVector2D(300, 275);
var temp:WVector2D;
var m:WMatrix2D = new WMatrix2D;
avoidCount--;
if (avoidCount < 0) {
// targetPosを更新
if (alertEnemyShotDirection.length > 0) {
// 逃げるように移動する
var alertShotVec:WVector2D = new WVector2D(0, 0);
for each(var v:WVector2D in alertEnemyShotDirection) {
alertShotVec.add(v);
}
alertShotVec.normalize();
m.setRotationMatrix(90 * Math.PI / 180);
m.scale(CenterPos.length);
alertShotVec.applyMatrix(m);
if (WMath.dot2D(mySpeed, alertShotVec) < 0) {
m.identity();
m.setRotationMatrix(180 * Math.PI / 180);
alertShotVec.applyMatrix(m);
}
temp = alertShotVec.copy();
m.identity();
m.setRotationMatrix(180 * Math.PI / 180);
temp.applyMatrix(m);
alertShotVec.add(myPos);
temp.add(myPos);
alertShotVec.subtract(CenterPos);
temp.subtract(CenterPos);
if (temp.length * 2 < alertShotVec.length) {
alertShotVec = temp;
}
alertShotVec.add(CenterPos);
targetPos = alertShotVec.copy();
moveAvoid = true;
avoidCount = 5;
} else {
moveAvoid = false;
var zeroVec:WVector2D = new WVector2D(0, 0);
if (checkCrclLine(CenterPos, 100, myPos, zeroVec)) {
targetPos = myPos;
_moveflg = false;
var myAngle:Number = _scene.myTankAngle;
var enemyAngle:Number = Math.atan2(_scene.enemyTankLinearVelocity.y, _scene.enemyTankLinearVelocity.x);
if (WMath.modulo2PI(enemyAngle + (90 * Math.PI / 180) - myAngle) <= Math.PI) command.addCommand(Command.TANK_TURN_RIGHT)
else command.addCommand(Command.TANK_TURN_LEFT);
} else {
var enemyPos:WVector2D = _scene.enemyTankPosition;
var directionPos:WVector2D = CenterPos.copy();
directionPos.subtract(enemyPos);
directionPos.normalize();
directionPos.scale(200);
directionPos.add(enemyPos);
if (checkCrclLine(directionPos, 100, myPos, zeroVec)) {
targetPos = myPos;
}else{
targetPos = directionPos;
}
}
}
}
shot(command);
if(_moveflg) move(command);
*/
shot(command);
// command.actionがフラグとなっている
return command.action;
}
private var cnt:int = 0;
private var lastEnemyPos:WVector2D = new WVector2D(300, 275);
private var shotSpeed:Number = 200;
private var shotCount:Number = 1;
private var lastShotNum:int = 0;
// 攻撃用関数
private function shot(command:Command):void {
// 弾を撃っていたら、現在の弾のスピードをshotSpeedに反映
if (_scene.myBulletCount > lastShotNum) {
var myShot:BoundBox = _scene.myBulletList;
while (myShot != null) {
shotCount++;
shotSpeed = shotSpeed * (shotCount - 1) / shotCount + myShot.linearVelocity.length / shotCount;
myShot = myShot.next;
}
}
lastShotNum = _scene.myBulletCount;
// 色々取得、設定
var myPos:WVector2D = _scene.myTankPosition;
var enemyPos:WVector2D = _scene.enemyTankPosition;
var shotDirection:WVector2D = enemyPos.copy();
shotDirection.subtract(myPos);
var enemyDistance:Number = shotDirection.length;
var baseEnemyVec:WVector2D = enemyPos.copy();
baseEnemyVec.subtract(lastEnemyPos);
lastEnemyPos = enemyPos.copy();
var enemyVec:WVector2D;
var _fire:Boolean = true;
g = _spOutputField.graphics;
g.lineStyle(1, 0xffff00, 0.1);
g.beginFill(0x000000, 0.0);
g.moveTo(myPos.x, myPos.y);
g.lineTo(enemyPos.x, enemyPos.y);
// 弾が届くまでの時間
var reachTime:Number = enemyDistance / shotSpeed;
var lastTime:Number = 0;
var _times:Number = 0;
// 算出した時間分だけ敵の位置を進めて再計算
// 誤差が小さくなるか、100回繰り返したら抜ける
while (reachTime - lastTime > 0.01 && _times < 100) {
_times++;
enemyVec = baseEnemyVec.copy();
_wmat.identity();
_wmat.scale(reachTime - lastTime);
enemyVec.applyMatrix(_wmat);
enemyPos.add(enemyVec);
g.lineTo(enemyPos.x, enemyPos.y);
shotDirection.add(enemyVec);
enemyDistance = shotDirection.length;
lastTime = reachTime;
reachTime = enemyDistance / shotSpeed;
/*
// 予測位置が画面外になる場合、攻撃しない
if ((enemyPos.x < 0 || enemyPos.x > 600 ||
enemyPos.y < 0 || enemyPos.y > 550) && enemyDistance > 200) {
_fire = false;
break;
}
*/
}
g.lineStyle(1, 0xff8000, 0.1);
g.endFill();
// 予想した敵の位置に砲台を向ける
var shotAngle:Number = Math.atan2(shotDirection.y, shotDirection.x);
shotAngle = WMath.modulo2PI(shotAngle - (_scene.myTankAngle + _scene.myGunAngle));
if (shotAngle > 5 * Math.PI / 180 && shotAngle < (360 - 5) * Math.PI / 180) {
if (shotAngle <= Math.PI) command.addCommand(Command.GUN_TURN_RIGHT);
else command.addCommand(Command.GUN_TURN_LEFT);
}
// だいたい合うか、近くに居れば攻撃
if ((((shotAngle < 10 * Math.PI / 180) || (shotAngle > (360 - 10) * Math.PI / 180)) && _fire) ||
(enemyDistance < 100 && ((shotAngle < 30 * Math.PI / 180) || (shotAngle > (360 - 30) * Math.PI / 180)))) {
command.addCommand(Command.FIRE);
}
}
// 移動用関数
private function move(command:Command):void {
var myPos:WVector2D = _scene.myTankPosition;
var myAngle:Number = _scene.myTankAngle;
if (!goForward)
myAngle += 180 * Math.PI / 180;
// 目標点を設定
var directionPos:WVector2D = targetPos.copy();
// ランダム性を持たせる
var rndVec:WVector2D = new WVector2D(Math.random() - 0.5, Math.random() - 0.5);
rndVec.normalize();
rndVec.scale(50);
directionPos.add(rndVec);
// 目標地点に赤い円を描画
g = _spOutputField.graphics;
g.lineStyle(1, 0xff0000, 1.0);
g.beginFill(0xff0000, 0.5);
g.drawCircle(directionPos.x, directionPos.y , 5);
// 目標点を自機から見た方向で計算
directionPos.subtract(myPos);
var distance:Number = directionPos.length;
// 向かう方向に線を引く
g = _spOutput.graphics;
g.lineStyle(1, moveAvoid?0xff8000:0x00ffff, 0.5);
g.moveTo(0, 0);
g.lineTo(directionPos.x, directionPos.y);
directionPos.normalize();
var direction:Number = Math.atan2(directionPos.y, directionPos.x);
// 向かう方向と現在の方向の差を計算
var tempAngle:Number = WMath.modulo2PI(direction - myAngle);
tempAngle-= 180 * Math.PI / 180;
// 一定以上差が大きければ進行方向を逆転
if ((tempAngle < 60 * Math.PI / 180) && (tempAngle > -60 * Math.PI / 180)) {
turn();
myAngle += 180 * Math.PI / 180;
}
// 向かう方向から自機の方向を引く
tempAngle = WMath.modulo2PI(direction - myAngle);
// 一定以上差が小さければ前進
var CenterPos:WVector2D = new WVector2D(300, 275);
var r:Number = CenterPos.length;
var _t:Number = 90 * distance / r;
if ((tempAngle < _t * Math.PI / 180) || (tempAngle > (360 - _t) * Math.PI / 180)) {
command.addCommand(getForward());
}
// PI未満なら右旋回
if (tempAngle < Math.PI) command.addCommand(getRight());
else command.addCommand(getLeft());
}
// 進行方向を逆転する
private function turn():void {
goForward = !goForward;
}
private function getForward():int {
return goForward ? Command.TANK_MOVE_FORWARD : Command.TANK_MOVE_BACKWARD;
}
private function getRight():int {
return goForward ? Command.TANK_TURN_RIGHT : Command.TANK_TURN_LEFT;
}
private function getLeft():int {
return goForward ? Command.TANK_TURN_LEFT : Command.TANK_TURN_RIGHT;
}
// WVector2Dを角度(ラジアン)に変換する
private function vecToRad(vec:WVector2D):Number {
return Math.atan2(vec.y, vec.x);
}
// 円と点の判定
private function checkCrclPoint(crclPos:WVector2D, crclRadius:Number, PointPos:WVector2D):Boolean {
var temp:WVector2D = crclPos.copy();
temp.subtract(PointPos);
if (temp.length < crclRadius) return true;
else return false;
}
// 円と半直線の交差判定
private function checkCrclLine(crclPos:WVector2D, crclRadius:Number, linePos:WVector2D, lineVec:WVector2D):Boolean {
var temp:WVector2D = lineVec.copy();
temp.normalize();
// 半直線の始点から円の中心へのベクトル
var C:WVector2D = crclPos.copy();
C.subtract(linePos);
// Cと半直線のベクトルの内積
var dot:Number = WMath.dot2D(C, temp);
// dot < 0 のとき、円の中心に向かって線が伸びていない
if (dot < 0){
if (C.length < crclRadius) // Cの長さが円の半径より小さければ交差
return true;
else
return false;
} else {
//円の中心から直線に降ろした足のベクトルを計算
temp.scale(dot);
C.subtract(temp);
if (C.length < crclRadius) //足の長さが半径より小さければ交差
return true;
else
return false;
}
}
// ※被弾時に演出を入れたい時に編集してください。
// called when a bullet hits this tank
override public function hit():void {
// 被弾したときに呼ばれるcallbackです.
}
// ※発射時に演出を入れたい時に編集してください。
// called when this tank fires
override public function fire():void
{
// 弾が発射される時に呼ばれるcallbackです.
}
private function initDraw():void
{
var g:Graphics;
g = _spBattery.graphics;
g.lineStyle(1, 0x00ffc0, 1.0);
g.beginFill(0x00ffc0, 1.0);
g.drawCircle(30, 0, 2);
g.endFill();
/*
g.lineStyle(1, 0x000000, 1.0);
g.beginFill(0xffffff, 0.5);
g.drawCircle(0, 0, 7.5);
g.endFill();
g.beginFill(0xc0c0c0, 0.5);
g.drawRect( -4, -2, 25, 4);
g.endFill();
*/
g = _spTank.graphics;
g.lineStyle(1, 0x00ff00, 0.5);
g.moveTo( -25, -12);
g.lineTo( -25, -15);
g.lineTo( -22, -15);
g.lineTo( -25, -15);
g.moveTo(22, -15);
g.lineTo(25, -15);
g.lineTo(25, -12);
g.lineTo(25, -15);
g.moveTo(25, 12);
g.lineTo(25, 15);
g.lineTo(22, 15);
g.lineTo(25, 15);
g.moveTo( -22, 15);
g.lineTo( -25, 15);
g.lineTo( -25, 12);
g.lineTo( -25, 15);
g.endFill();
g.lineStyle(1, 0x00ff00, 0.5);
g.beginFill(0xffffff, 0.0);
g.moveTo(23, -4);
g.lineTo(28, 0);
g.lineTo(23, 4);
g.lineStyle(1, 0x000000, 0.0);
g.endFill();
/*
g.lineStyle(1, 0xffffff, 1.0);
g.beginFill(0x808080, 1.0);
var m:Matrix = new Matrix;
m.identity();
m.scale(1.0 / 1638.4 * 25 * 2, 1);
g.beginGradientFill(GradientType.LINEAR, [0x8080ff, 0xffff80, 0xff8080], [1.0, 1.0, 1.0], [0, 128, 255],
m , SpreadMethod.REPEAT, InterpolationMethod.LINEAR_RGB, 0.0);
g.drawRoundRect( -25, -15, 50, 30, 15, 15);
g.endFill();
g.beginFill(0xffa0a0, 1.0);
g.moveTo(15, -8);
g.lineTo(23, 0);
g.lineTo(15, 8);
*/
g = _spUFO.graphics;
g.lineStyle(1, 0xffffff);
g.beginFill(0xc0c0c0);
g.drawCircle(-13, 6, 5.5);
g.drawCircle(0, 8, 5.5);
g.drawCircle(13, 6, 5.5);
g.endFill();
g.lineStyle(1, 0xffffff);
g.beginFill(0xc0c0c0);
g.drawEllipse(-19, -4, 38, 12);
g.endFill();
g.lineStyle(1, 0xffffff);
g.beginFill(0xc0c0c0);
g.drawEllipse(-8, -12, 16, 14);
g.endFill();
_spTank.addChild(_spBattery);
}
// ※タンクの向きを描画している関数。修正の必要はありません。
// 毎フレームごとに呼ばれる描画callback
// stageと同じ大きさのBitmapDataが渡されます.
// 現在のゲームの状態を見るには、_sceneオブジェクトのプロパティーを
// 調べます.
// @see http://flash-games.wonderfl.net/static/asdoc/net/wonderfl/game/infinity_tank/development/BattleScene.html
override public function draw(bitmapData:BitmapData):void
{
// 砲台の向きをセットします.
// 砲台はタンクのSpriteの子供のスプライトです.
_spBattery.rotation = _scene.myGunAngle * 180 / Math.PI;
// 単位行列にセット
_mat.identity();
// タンクの傾きをセット
_mat.rotate(_scene.myTankAngle);
// タンクの位置に平行移動. タンクのグラフィックは中心座標となっています.
_mat.translate(_scene.myTankPosition.x, _scene.myTankPosition.y);
// 一度BitmapDataをクリア
bitmapData.colorTransform(bitmapData.rect, _ctfm);
// BitmapDataを描画
bitmapData.draw(_spTank, _mat, null, null, null, true);
// _spOutput, _spUFOを描画
_mat.identity();
_mat.translate(_scene.myTankPosition.x, _scene.myTankPosition.y);
bitmapData.draw(_spUFO, _mat, null, null, null, true);
bitmapData.draw(_spOutput, _mat, null, null, null, true);
_mat.identity();
bitmapData.draw(_spOutputField, _mat, null, null, null, true);
}
}
}
