シューティングっぽい動きで避ける
frameRate="60"は本気モード
TODO アルゴリズムの抜けを修正
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3LQx
*/
// forked from uwi's forked from: avoidance function
// forked from uwi's avoidance function
package {
import com.flashdynamix.utils.SWFProfiler;
import flash.display.*;
import flash.geom.*;
import flash.events.*;
import flash.filters.*;
import flash.text.TextField;
import flash.utils.getTimer;
// frameRate="60"は本気モード
// TODO アルゴリズムの抜けを修正
[SWF(frameRate="30", backgroundColor="#000000")]
public class FlashTest extends Sprite {
private var _bullets : Array;
private var _ct : int;
private var _myx : Point;
private var _nhit : int;
private const R_BULLET : Number = 30.0;
private const R_SELF : Number = 5.0;
private const INFD : Number = Math.pow((R_BULLET + R_SELF), 2);
private var _space : BitmapData;
private var _bmdbullet : BitmapData;
private var _bmdself : BitmapData;
private var _tf : TextField;
private const P0 : Point = new Point(0, 0);
private const WEAKBLUR : BitmapFilter = new BlurFilter(3.0, 3.0);
private const W : int = 400;
private const H : int = 465;
public function FlashTest() {
Wonderfl.capture_delay(5);
SWFProfiler.init(this);
// 宇宙
_space = new BitmapData(400, 465, false, 0x000000);
var bmpspace : Bitmap = new Bitmap(_space);
addChild(bmpspace);
// 弾描画
var shBullet : Shape = new Shape();
var gb : Graphics = shBullet.graphics;
gb.lineStyle(0.0, 0x999999);
var mat : Matrix = new Matrix();
mat.createGradientBox(R_BULLET * 2, R_BULLET * 2, 0, 0, 0);
gb.beginGradientFill("radial", [0xffffff, 0xaaaaaa], [1, 1], [0x00, 0xff], mat);
gb.drawCircle(R_BULLET, R_BULLET, R_BULLET);
gb.endFill();
_bmdbullet = new BitmapData(R_BULLET * 2, R_BULLET * 2, true, 0x00000000);
_bmdbullet.draw(shBullet);
_bmdbullet.applyFilter(_bmdbullet, _bmdbullet.rect, P0, WEAKBLUR);
// 自機描画
var shSelf : Shape = new Shape();
var gs : Graphics = shSelf.graphics;
gs.lineStyle(1.0, 0x0000ff);
gs.beginFill(0x3333ff);
gs.drawCircle(R_SELF, R_SELF, R_SELF);
gs.endFill();
_bmdself = new BitmapData(R_SELF * 2, R_SELF * 2, true, 0x00000000);
_bmdself.draw(shSelf);
_myx = new Point(200, 400);
_bullets = [];
_ct = 0;
_nhit = 0;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_tf = new TextField();
_tf.autoSize = "left";
_tf.textColor = 0xffffff;
_tf.borderColor = 0xffffff;
_tf.border = true;
_tf.x = 400;
addChild(_tf);
}
private var _gct : int = 0; // デバッグ用
private var _ans : int = 0;
private function onEnterFrame(e : Event) : void
{
draw();
judge();
_ct++;
if(_ct % 3 == 0){
addBullet();
// addBullet();
// addBullet();
}
if(_ct % 3 == 0){
var s : int = getTimer();
_ans = calcAvoidanceAtRoot(_myx.x, _myx.y, 2);
var g : int = getTimer();
_gct = g - s;
}
// step
_myx.x += PTN_MOVE[_ans][0];
_myx.y += PTN_MOVE[_ans][1];
if(_myx.x < 0 || _myx.x >= W || _myx.y < 0 || _myx.y >= H){
_myx.x -= PTN_MOVE[_ans][0];
_myx.y -= PTN_MOVE[_ans][1];
}
for each(var b : Bullet in _bullets){
b.x.x += b.v.x;
b.x.y += b.v.y;
}
}
private function removeBullet(i : int) : void
{
if(i < _bullets.length - 1){
_bullets[i] = _bullets.pop();
i--;
}else{
_bullets.pop();
}
}
private function judge() : void
{
for(var i : int = 0;i < _bullets.length;i++){
var b : Bullet = _bullets[i];
if(
(b.x.x - _myx.x) * (b.x.x - _myx.x) +
(b.x.y - _myx.y) * (b.x.y - _myx.y)
< INFD){
_nhit++;
removeBullet(i);
continue;
}
if(b.x.x < 0 - R_BULLET || b.x.x > W + R_BULLET || b.x.y < 0 - R_BULLET || b.x.y > H + R_BULLET){
removeBullet(i);
}
}
}
private function draw() : void
{
_space.lock();
_space.fillRect(_space.rect, 0x000000);
// self
_space.copyPixels(_bmdself, _bmdself.rect,
new Point(_myx.x - R_SELF, _myx.y - R_SELF));
// bullets
for each(var b : Bullet in _bullets){
_space.copyPixels(_bmdbullet, _bmdbullet.rect,
new Point(b.x.x - R_BULLET, b.x.y - R_BULLET));
}
_space.unlock();
_tf.text = "time : \n" + _ct + "\n\nhit : \n" + _nhit + "\n\nalgo : \n" + _gct + "ms";
}
private function addBullet() : void
{
var x : Point = new Point(Math.random() * 400, 0);
var v : Point = new Point(Math.random() * 8 - 4, Math.random() * 5 + 5);
var b : Bullet = new Bullet();
b.x = x;
b.v = v;
_bullets.push(b);
}
/*
// 低速移動・高速移動
public static const PTN_MOVE : Array = [
[0, 0],
[3, 0], [6, 0],
[0, 3], [0, 6],
[-3, 0], [-6, 0],
[0, -3], [0, -6],
[2, 2], [4, 4],
[-2, 2], [-4, 4],
[-2, -2], [-4, -4],
[2, -2], [4, -4]
];
*/
// 高速移動オンリー
public static const PTN_MOVE : Array = [
[0, 0],
[6, 0],
[0, 6],
[-6, 0],
[0, -6],
[4, 4],
[-4, 4],
[-4, -4],
[4, -4]
];
// 決定間隔
public static const INTERVAL : int = 3;
// 回避アルゴリズム
// @param x 回避者位置
// @param depth 読むパスの深さ
public function calcAvoidanceAtRoot(x : Number, y : Number, depth : int) : int
{
var i : int;
var t : Number;
var mincs : Array = new Array(PTN_MOVE.length);
// 壁への衝突
for(i = 0;i < PTN_MOVE.length;i++){
var minc : Number = Number.MAX_VALUE;
if(PTN_MOVE[i][0] > 0){
t = (W - x) / PTN_MOVE[i][0];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][0] < 0){
t = (x) / -PTN_MOVE[i][0];
if(t < minc)minc = t;
}
if(PTN_MOVE[i][1] > 0){
t = (H - y) / PTN_MOVE[i][1];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][1] < 0){
t = (y) / -PTN_MOVE[i][1];
if(t < minc)minc = t;
}
mincs[i] = minc;
}
// 弾への衝突
for each(var b : Bullet in _bullets){
var rx : Number = b.x.x - x;
var ry : Number = b.x.y - y;
for(i = 0;i < PTN_MOVE.length;i++){
var rvx : Number = b.v.x - PTN_MOVE[i][0];
var rvy : Number = b.v.y - PTN_MOVE[i][1];
t = mint(rx, ry, rvx, rvy);
if(t > 0){
var d2 : Number = mind2(rx, ry, rvx, rvy, t);
if(d2 < INFD){
t -= Math.sqrt((INFD - d2) / (rvx * rvx + rvy * rvy));
if(t < mincs[i])mincs[i] = t;
}
}
}
}
var mc : Number;
var maxminc : Number = 0.0;
var maxmini : int = 0;
// var maxmini : int = Math.random() * PTN_MOVE.length; // 詰みの防止
for(i = 0;i < PTN_MOVE.length;i++){
var maxt : int = int(mincs[i] / INTERVAL) * INTERVAL;
if(maxt <= INTERVAL)continue; // 衝突寸前なのは再帰しても意味がない
if(depth > 1){
mc = maxt + calcAvoidanceAtNode(
x + PTN_MOVE[i][0] * maxt,
y + PTN_MOVE[i][1] * maxt,
maxt,
depth - 1
);
}else if(depth == 1){
mc = maxt + calcAvoidanceAtLeaf(
x + PTN_MOVE[i][0] * maxt,
y + PTN_MOVE[i][1] * maxt,
maxt
);
}else {
mc = maxt;
}
if(mc > maxminc){
maxminc = mc;
maxmini = i;
}
}
return maxmini;
}
private function calcAvoidanceAtNode(x : Number, y : Number, delay : int, depth : int) : Number
{
var i : int;
var t : Number;
var mincs : Array = new Array(PTN_MOVE.length);
// 壁への衝突
for(i = 0;i < PTN_MOVE.length;i++){
var minc : Number = Number.MAX_VALUE;
if(PTN_MOVE[i][0] > 0){
t = (W - x) / PTN_MOVE[i][0];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][0] < 0){
t = (x) / -PTN_MOVE[i][0];
if(t < minc)minc = t;
}
if(PTN_MOVE[i][1] > 0){
t = (H - y) / PTN_MOVE[i][1];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][1] < 0){
t = (y) / -PTN_MOVE[i][1];
if(t < minc)minc = t;
}
mincs[i] = minc;
}
// 弾への衝突
for each(var b : Bullet in _bullets){
var rx : Number = b.x.x - x + b.v.x * delay;
var ry : Number = b.x.y - y + b.v.y * delay;
for(i = 0;i < PTN_MOVE.length;i++){
var rvx : Number = b.v.x - PTN_MOVE[i][0];
var rvy : Number = b.v.y - PTN_MOVE[i][1];
t = mint(rx, ry, rvx, rvy);
if(t > 0){
var d2 : Number = mind2(rx, ry, rvx, rvy, t);
if(d2 < INFD){
t -= Math.sqrt((INFD - d2) / (rvx * rvx + rvy * rvy));
if(t < mincs[i])mincs[i] = t;
}
}
}
}
var mc : Number;
var maxminc : Number = 0.0;
for(i = 0;i < PTN_MOVE.length;i++){
var maxt : int = int(mincs[i] / INTERVAL) * INTERVAL;
if(maxt <= INTERVAL)continue;
if(depth > 1){
mc = mincs[i] + calcAvoidanceAtNode(
x + PTN_MOVE[i][0] * maxt,
y + PTN_MOVE[i][1] * maxt,
delay + maxt,
depth - 1
);
}else{
mc = mincs[i] + calcAvoidanceAtLeaf(
x + PTN_MOVE[i][0] * maxt,
y + PTN_MOVE[i][1] * maxt,
delay + maxt
);
}
if(mc > maxminc)maxminc = mc;
}
return maxminc;
}
private function calcAvoidanceAtLeaf(x : Number, y : Number, delay : int) : Number
{
var i : int;
var t : Number;
var minc : Number = Number.MAX_VALUE;
// 壁への衝突
for(i = 0;i < PTN_MOVE.length;i++){
if(PTN_MOVE[i][0] > 0){
t = (W - x) / PTN_MOVE[i][0];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][0] < 0){
t = (x) / -PTN_MOVE[i][0];
if(t < minc)minc = t;
}
if(PTN_MOVE[i][1] > 0){
t = (H - y) / PTN_MOVE[i][1];
if(t < minc)minc = t;
}else if(PTN_MOVE[i][1] < 0){
t = (y) / -PTN_MOVE[i][1];
if(t < minc)minc = t;
}
}
// 弾への衝突
for each(var b : Bullet in _bullets){
var rx : Number = b.x.x - x + b.v.x * delay;
var ry : Number = b.x.y - y + b.v.y * delay;
for(i = 0;i < PTN_MOVE.length;i++){
var rvx : Number = b.v.x - PTN_MOVE[i][0];
var rvy : Number = b.v.y - PTN_MOVE[i][1];
t = mint(rx, ry, rvx, rvy);
if(t > 0){
var d2 : Number = mind2(rx, ry, rvx, rvy, t);
if(d2 < INFD){
t -= Math.sqrt((INFD - d2) / (rvx * rvx + rvy * rvy));
if(t < minc)minc = t;
}
}
}
}
return minc;
}
private function mint(x : Number, y : Number, vx : Number, vy : Number) : Number
{
if(vx * vx + vy * vy < 0.01){
return 0.0;
}
return -(x * vx + y * vy) / (vx * vx + vy * vy);
}
private function mind2(x : Number, y : Number, vx : Number, vy : Number, t : Number) : Number
{
var xx : Number = x + vx * t;
var yy : Number = y + vy * t;
return xx * xx + yy * yy;
}
}
}
import flash.geom.Point;
class Bullet
{
public var x : Point;
public var v : Point;
}