Beautifl - Flash Gallery

Thumbnail : 命中
命中
uwi 2010-01-15 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

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;
    }
    
  }