命中
たまにナチュラルに外すのがわからない
@author Uwi
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/nHzQ
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
/**
* たまにナチュラルに外すのがわからない
* @author Uwi
*/
[SWF(frameRate="60")]
public class Main extends Sprite
{
public var _self : Self;
public var _enemies : Array;
public var _bullets : Array;
private var _bmdEnemy : BitmapData;
private var _bmdBullet : BitmapData;
private var _bmdSelf : BitmapData;
private var _field : BitmapData;
private const W : Number = stage.stageWidth;
private const H : Number = stage.stageHeight;
private var _tf : TextField;
private var _boundary : Rectangle = new Rectangle(0, 0, W, H); // 敵機移動境界
private var _dir : int = 0; // 移動方向 -1, 0, 1のどれか
private var _t : int = 0; // グローバルカウント
private var _nHit : uint = 0;
private var _nAll : uint = 0;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
trace(W, H);
_field = new BitmapData(W, H, false, 0xffffff);
addChild(new Bitmap(_field));
_bmdEnemy = new BitmapData(20, 20, true, 0xffffff);
_bmdBullet = new BitmapData(10, 10, true, 0xffffff);
_bmdSelf = new BitmapData(20, 20, true, 0xffffff);
_bmdEnemy.draw(makeCircleShape(10, 0xff0000));
_bmdBullet.draw(makeCircleShape(5, 0x000000));
_bmdSelf.draw(makeCircleShape(10, 0x0000ff));
_self = new Self(W / 2, H / 2, 0, 10, 0.2, 5, 10, 5);
_enemies = [];
_bullets = [];
_tf = new TextField();
addChild(_tf);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e : Event) : void
{
_t++;
if (_t % 15 == 0) {
// 敵機生成
// (exx, exy)->(cx, cy)に行くように
var exx : Number = Math.random() * W;
var exy : Number = Math.random() * H;
var cx : Number = W / 2 - 50 + Math.random() * 100;
var cy : Number = H / 2 - 50 + Math.random() * 100;
var r : Number = Math.sqrt((exx - cx) * (exx - cx) + (exy - cy) * (exy - cy));
var v : Number = 4 + Math.random() * 3;
var evx : Number = (cx - exx) / r * v;
var evy : Number = (cy - exy) / r * v;
_enemies.push(new Enemy(exx, exy, evx, evy, 10));
}
judge();
move();
draw();
algo();
_tf.text = "" + _nHit + "/" + _nAll;
}
/**
* 移動
*/
private function move() : void
{
_self.move(_dir);
for each(var enemy : Enemy in _enemies) enemy.move();
for each(var bullet : Bullet in _bullets) bullet.move();
}
/**
* 当たり判定
*/
private function judge() : void
{
var i : int, j : int;
outer:
for (i = _enemies.length - 1;i >= 0; i--) {
var enemy : Enemy = _enemies[i];
// 弾命中
for (j = _bullets.length - 1; j >= 0; j--) {
var bullet : Bullet = _bullets[j];
if (
(enemy._xx - bullet._xx) * (enemy._xx - bullet._xx) +
(enemy._xy - bullet._xy) * (enemy._xy - bullet._xy) <=
(enemy._r + bullet._r) * (enemy._r + bullet._r)
) {
removeBullet(i, _enemies);
removeBullet(j, _bullets);
_nHit++;
_nAll++;
continue outer;
}
}
// 境界外へ
if (!_boundary.contains(enemy._xx, enemy._xy)) {
removeBullet(i, _enemies);
_nAll++;
}
}
}
/**
* 弾・敵機消去
* @param i
* @param bullets
*/
private static function removeBullet(i : int, bullets : Array) : void
{
if (i == bullets.length - 1) {
bullets.pop();
}else {
bullets[i] = bullets.pop();
}
}
/**
* 描画
*/
private function draw() : void
{
_field.lock();
_field.fillRect(_field.rect, 0xffffff);
// 自機
_field.copyPixels(_bmdSelf, _bmdSelf.rect, new Point(_self._xx - _self._r, _self._xy - _self._r));
// 敵機
for each(var enemy : Enemy in _enemies) {
_field.copyPixels(_bmdEnemy, _bmdEnemy.rect, new Point(enemy._xx - enemy._r, enemy._xy - enemy._r));
}
// 弾
for each(var bullet : Bullet in _bullets) {
_field.copyPixels(_bmdBullet, _bmdBullet.rect, new Point(bullet._xx - bullet._r, bullet._xy - bullet._r));
}
_field.unlock();
}
/**
* 命中アルゴリズム
*/
private function algo() : void
{
// 最短発射時間を実現する方向に移動
var mint : int = 100;
var optDir : int = 0;
var optE : Enemy = null;
for each (var enemy : Enemy in _enemies) {
if (enemy._locked) continue;
for (var dir : int = -1; dir <= 1;dir++){
var t : int = Hitter.calcFastestShootTime(_self, enemy, dir, mint);
if (mint > t) {
mint = t;
optDir = dir;
optE = enemy;
}
}
}
// trace(mint, optDir);
if (mint == 0) {
// 今発射しなければいけないとき発射する
_bullets.push(_self.shoot());
if (optE != null) optE._locked = true; // ろっくおん
}
_dir = optDir;
}
private function makeCircleShape(r : Number, c : uint) : Shape
{
var ret : Shape = new Shape();
var g : Graphics = ret.graphics;
g.beginFill(c);
g.drawCircle(r, r, r);
g.endFill();
return ret;
}
}
}
class Self
{
public var _phi : Number; // 1ステップあたりの回転量
public var _step : Number; // 1ステップあたりの速度
public var _rRotate : Number; // 回転半径
public var _vBullet : Number; // 弾速
public var _xx : Number; // 位置ベクトル
public var _xy : Number;
public var _theta : Number; // 砲頭ベクトル
public var _rBullet : Number; // 弾径
public var _r : Number;
public function Self(xx : Number, xy : Number, theta : Number, r : Number, phi : Number, step : Number, vBullet : Number, rBullet : Number)
{
_xx = xx; _xy = xy;
_theta = theta;
_r = r;
_phi = phi;
_step = step;
_rRotate = step / 2 * Math.sin(phi);
_vBullet = vBullet;
_rBullet = rBullet;
}
/**
* 方向dirに1ステップだけ動かす
* @param dir
*/
public function move(dir : int) : void
{
_theta += _phi * dir;
_xx += _step * Math.cos(_theta);
_xy += _step * Math.sin(_theta);
}
/**
* 方向dirで動き続けた場合のtステップ後の位置
* @param t
* @param dir
* @return
*/
public function pos(t : Number, dir : int) : Array
{
if (dir == 0) {
return [
_xx + _step * Math.cos(_theta) * t,
_xy + _step * Math.sin(_theta) * t
];
}else {
var Q : Array = [
_xx - _rRotate * Math.sin(_theta) * dir,
_xy + _rRotate * Math.cos(_theta) * dir
];
return [
Q[0] + _rRotate * Math.sin(_theta + t * 2 * _phi) * dir,
Q[1] - _rRotate * Math.cos(_theta + t * 2 * _phi) * dir
];
}
}
public function shoot() : Bullet
{
return new Bullet(
_xx, _xy,
_vBullet * Math.cos(_theta),
_vBullet * Math.sin(_theta),
_rBullet
);
}
}
class Hitter
{
/**
* selfの中央から移動方向に弾がでる時の、
* selfがenemyを打ち落とす弾発射までの最短時間
* @param self
* @param enemy
* @param dir
* @param lim
* @return
*/
public static function calcFastestShootTime(self : Self, enemy : Enemy, dir : int, lim : uint = 100) : int
{
for (var t : uint = 0; t < lim; t++) {
var spos : Array = self.pos(t, dir);
var w : Array = [
Math.cos(self._theta + self._phi * t * dir),
Math.sin(self._theta + self._phi * t * dir)
];
var cct : Array = calcCollisionTime([
spos[0] - enemy._xx - enemy._vx * t,
spos[1] - enemy._xy - enemy._vy * t
], [
w[0] * self._vBullet - enemy._vx,
w[1] * self._vBullet - enemy._vy
]);
// trace(t, cct);
if (cct == null) continue;
if (cct[1] <= self._rBullet + enemy._r) {
if (t == 0) {
trace(cct[0], cct[1]);
}
return t;
}
}
return lim;
}
/**
* A+Btの最小値を求める。
* @param A
* @param B
* @return [collisionTime, collisionDistance]
*/
private static function calcCollisionTime(A : Array, B : Array) : Array
{
var n : uint = A.length;
if (n != B.length) return null;
var i : uint;
var na : Number = 0;
var nb : Number = 0;
var ip : Number = 0;
for (i = 0; i < n; i++) {
na += A[i] * A[i];
nb += B[i] * B[i];
ip += A[i] * B[i];
}
if (nb == 0) {
return [0, Math.sqrt(na)];
}
if (ip <= 0) {
// |A+BT|^2=|A|^2+2TA・B+|B|^2・T^2
// =|A|^2-2(A・B)^2/|B|^2+(A・B)^2/|B|^2
// =|A|^2-(A・B)^2/|B|^2
// 実数の場合
// return [-ip / nb, Math.sqrt(na - ip * ip / nb)];
// 整数の場合
var T : int = Math.round(-ip / nb);
return [T, Math.sqrt(na + 2 * T * ip + nb * T * T)];
}else {
return [0, Math.sqrt(na)];
}
}
}
class Bullet
{
public var _xx : Number;
public var _xy : Number;
public var _vx : Number;
public var _vy : Number;
public var _r : Number; // 弾径
public function Bullet(xx : Number, xy : Number, vx : Number, vy : Number, r : Number)
{
_xx = xx; _xy = xy;
_vx = vx; _vy = vy;
_r = r;
}
public function move() : void
{
_xx += _vx;
_xy += _vy;
}
}
class Enemy extends Bullet
{
public var _locked : Boolean; // すでにロックオンされたかどうか
public function Enemy(xx : Number, xy : Number, vx : Number, vy : Number, r : Number)
{
super(xx, xy, vx, vy, r);
_locked = false;
}
}