Flashゲーム講座 & アクションスクリプトサンプル集



点とインスタンスとで当たり判定を取る

 

サンプルをダウンロード
 



 
■点とインスタンスとで当たり判定を取る
 
hitTest() を使用すると、点(座標)とインスタンスとの当たり判定を簡単にとる事ができます。

 
■Hittest() メソッド

ムービークリップ.hitTest( x座標 , y座標, true か false );

先頭にMCインスタンスのターゲットを指定します。

()の中の1番目の引数にX座標を、2番目の引数にY座標を指定します。
3番目の引数に、インスタンスと座標(点)との当たり判定を、インスタンスのイラストの形状通りに正確にとるか、あいまいなインスタンスの外枠の四角形でとるかを true か false で指定します。


true false
 
インスタンスと座標(点)が重なっていれば true が、重なっていなければ false が返ってきます。
 
例1) ムービークリップ a と座標(100,200)との当たり判定。

	if (_root.a.hitTest(100, 200, true)) {

	}

 
例2) ムービークリップ a と、マウスポインタとの当たり判定

	if (_root.a.hitTest(_root._xmouse, _root._ymouse, true)) {

	}

 
 
ちなみに、どんなに複雑な図形でも、巨大なムービークリップを使っても当たり判定の処理の重さは変わりません。
 
 




インスタンス同士で当たり判定を取る

 

サンプルをダウンロード
 


 
■インスタンス同士で当たり判定を取る
 
先ほどの hitTest() 関数を使用して、今度はインスタンスとインスタンスとの当たり判定をとります。
 
■Hittestメソッド

ムービークリップ.hitTest( ムービークリップ );

先頭にMCインスタンスのターゲットを指定します。
()の中に2番目のMCインスタンスのターゲットを指定します。
 
インスタンスとインスタンスが重なっていれば true が、重なっていなければ false が返ってきます。
 
■注意点

インスタンス同士の当たり判定は、イラストの形状通りの正確な当たり判定を取ることは出来ません。インスタンスが四角形として扱われ、四角形同士の当たり判定が取られます。
 
 
例1) MCインスタンス a と、MCインスタンス b との当たり判定。

	if (_root.a.hitTest(_root.b)) {

	}

 
例1) MCインスタンス a の中にあるMCインスタンス b と、MCインスタンス c の中にあるMCインスタンス d との当たり判定。

	if (_root.a.b.hitTest(_root.c.d)) {

	}


 
 




四角形(矩形)同士で当たり判定を取る

 

サンプルをダウンロード
 


 
■四角形(矩形)同士で当たり判定を取る


hitTest() 関数を使わずに座標値だけで、四角形同士の当たり判定を取ります。
 
■必要なパラメータ


矩形でよく使われている x 座標 , y 座標 , 幅 , 高さ という4つのパラメータを使用することにします。


 
当たり判定に使うパラメータ。

    /* 矩形A */
    ax =  50;        // x 座標
    ay = 100;        // y 座標
    aw =  50;        // 幅
    ah =  80;        // 高さ

    /* 矩形B */
    bx = 150;        // x 座標
    by = 200;        // y 座標
    bw =  30;        // 幅
    bh = 100;        // 高さ
 
この時点で x 最小値 , y 最小値 , x 最小値 , y 最大値 というパラメータにしてもいいですが、座標が常に変動したりその後の衝突の計算を考慮すると、幅と高さのデータになっている方が何かと便利です。
 
■考え方


まず、矩形の x 最小値, y 最小値, y 最大値 ,y 最大値 を計算しておきます。


矩形データから最小座標、最大座標を計算する

    /* 矩形A */
    a_x_min = ax;            // x 座標最小
    a_y_min = ay;            // y 座標最小
    a_x_max = a_x_min + aw;  // x 座標最大
    a_y_max = a_y_min + ah;  // y 座標最大

    /* 矩形B */
    b_x_min = bx;            // x 座標最小
    b_y_min = by;            // y 座標最小
    b_x_max = b_x_min + aw;  // x 座標最大
    b_y_max = b_y_min + ah;  // y 座標最大
 
この数値を使って4回比較していきます。
この4回の比較をすべて満たさなかった場合、重なっていることがわかります。
 


1回目


矩形Aの x 最大値と矩形Bの x 最小値を比較します。






2回目


矩形Bの x 最大値と矩形Aの x 最小値を比較します。






3回目


矩形Aの y 最大値と矩形Bの y 最小値を比較します。






4回目


矩形Bの y 最大値と矩形Aの y 最小値を比較します。






念のために書きますが、この4回の比較をすべて満たさなかった場合、重なっていることがわかります。計算例は下の通りです。
 
計算例


 
例) 矩形Aと矩形Bが当たっているか調べる

    /* 矩形A */
    ax =  50;        // x 座標
    ay = 100;        // y 座標
    aw =  50;        // 幅
    ah =  80;        // 高さ

    /* 矩形B */
    bx = 150;        // x 座標
    by = 200;        // y 座標
    bw =  30;        // 幅
    bh = 100;        // 高さ
    
    /* 矩形A */
    a_x_min = ax;            // x 座標最小
    a_y_min = ay;            // y 座標最小
    a_x_max = a_x_min + aw;  // x 座標最大
    a_y_max = a_y_min + ah;  // y 座標最大

    /* 矩形B */
    b_x_min = bx;            // x 座標最小
    b_y_min = by;            // y 座標最小
    b_x_max = b_x_min + bw;  // x 座標最大
    b_y_max = b_y_min + bh;  // y 座標最大
    
    if( a_x_max < b_x_min){
    }else if( b_x_max < a_x_min){
    }else if( a_y_max < b_y_min){
    }else if( b_y_max < a_y_min){
    }else{
        // 当たりあり
    }

 
 
矩形の当たり判定の高速化


当たり判定の順番を変えるだけで処理速度が大きく改善する事があります。

当たっているかをチェックしようと考えるのではなくて、いかに当たっていないかをチェックしてさっさと当たり判定処理を抜けるように逆の発想で考えてみましょう。

 
例えば、シューティングゲームなど当たり判定を調べたいとします。

横シューティングの場合、プレイヤーが左側にいて、弾が右にいるパターンがはるかに多いです。 プレイヤーの矩形x最大値と弾の矩形x最小値を比較してプレイヤーのほうが小さかったら、if分の判定を 1 回だけした時点で当たっていない事がわかり、判定処理を抜ける事ができます。
 
縦シューティングの場合、プレイヤーが下側にいて、弾が上にいるパターンがはるかに多いです。 プレイヤーの y 最小値と弾の y 最大値を比較してプレイヤーのほうが大きかったら、if分の判定を 1 回だけした時点で当たっていない事がわかり、判定処理を抜ける事ができます。
 
まとめると、よく発生するパターンを if 文の 1 番目に持ってきて、あまり発生しないパターンを if文の最後に持ってきます。すると、 ほとんどが if文一回だけで「自機と弾が当たっていない」ということを調べることが出来ます。 これでサイクルが減り、処理速度が改善します。
 
 
hitTest() と矩形当たり判定の使い分け


ムービークリップの形状が常に変化している場合、それに合わせて矩形データを変えていくのは大変です。 自前で回転や階層を考慮して矩形のサイズをはじき出すよりは素直に、hitTest() を使ったほうが早いでしょう。

矩形が頻繁に変形していなくて、さらに衝突後の押し合いなどの処理をしたい場合は、矩形の当たり判定を使ったほうが幅と高さのデータがあるため、後の計算がわかりやすくなると思います。
 
Flashは内部で描画範囲用の矩形データを持っていて、変形や階層に応じて矩形を再計算しているため常に最適な矩形データを持っている事になります。という事は、自前で矩形データをいちいち計算するのは無駄な処理と言えるでしょう。
 
衝突について

矩形同士の当たり判定を使った地形衝突の処理をこちらにまとめています。
 
 
 




円同士で当たり判定を取る

 

サンプルをダウンロード
 


 
円同士の当たり判定
 
円と円同士の当たり判定を調べます。
 
安定した負荷で当たり判定が取れますが、矩形同士の判定と比べると速度的に落ちます。
 
考え方

円を構成するパラメータとして、中心の x 座標, y 座標と半径の 3 つの変数を用意します。


 
当たり判定に使うパラメータ。

    // 円A
    ax = 66;        // 中心 x 座標
    ay = 153;       // 中心 y 座標
    ar = 30;        // 半径

    // 円B
    bx = 210;       // 中心 x 座標
    by = 72;        // 中心 y 座標
    br = 60;        // 半径
 
下の図を見てください。

 
まず a に当たる部分の長さを求めます。三平方の定理から下の式で求まります。

Math.sqrt((円Aの x 座標 - 円Bの x 座標) * (円Aの x 座標 - 円Bの x 座標) + (円Aの y 座標 - 円Bの y 座標) * (円Aの y 座標 - 円Bの y 座標))

上の図の a にあたる部分の長さを求める

  var length = Math.sqrt((ax - bx)*(ax - bx) + (ay - by)*(ay - by));

 
あとは円Aと円Bの半径を足したものと a の長さを比較すれば2つの円が重なっているかわかります。
 
当たりなし (円Aの半径 + 円Bの半径) < a の長さ


当たりあり (円Aの半径 + 円Bの半径) > a の長さ
 
計算例
 
 
例) 円Aと、円Bが当たっているか調べる

  /* 円A */
  var ax = 66;        // 中心 x 座標
  var ay = 153;       // 中心 y 座標
  var ar = 30;        // 半径

  /* 円B */
  var bx = 210;       // 中心 x 座標
  var by = 72;        // 中心 y 座標
  var br = 60;        // 半径

  var sx = bx - ax;
  var sy = by - ay;

  var length = Math.sqrt(sx * sx + sy * sy);
  
  if(length < (ar + br)){

    // 当たりあり

  }else{

    // 当たりなし

  }

 
衝突について

円同士の当たり判定を使った衝突の処理をこちらにまとめています。
 

 




点が線を境にどちら側にあるか判定を取る

 

サンプルをダウンロード
 


 
内積を使って、点が線のこちら側にあるか反対側にあるか当たり判定を調べます。
 
内積とは?
 
内積は2つの単位ベクトルの平行の度合いを表しています。
単位ベクトル=長さが1のベクトル
 


特徴としては、
2つのベクトルの角度が90度以下のときは正、
90度より大きいときは負の値になります。
 
内積は以下の式で求まります。
ベクトルA(ax ,ay)とベクトルB(bx ,by) の内積
 

    var doc = (ax * bx) + (ay * by);
 
 
計算する
 
座標を(x,y) で表現する事にします。
 

    x = 10;
    x = 20;

 
線AB を 2 点( ax , ay) (bx , by) で表現する事にします。
 

    ax = 75;    ay = 195;
    bx = 330;   by = 90;

 
線AB のベクトルを求めます。
 

    v1_x = bx - ax;
    v1_y = by - ay;

 
ABベクトルを垂直にしたベクトルを求めます。
 

    tmp = v1_x;
    v1_x = -v1_y;
    v1_y = tmp;

 
座標 A から 座標(x , y) までのベクトルを求めます。
 

    v2_x = x - ax;
    v2_y = y - ay;

 
2つのベクトルの内積を求めます。
 

    doc = v1_x * v2_x + v1_y * v2_y;

 
内積が正なら表側、負なら裏側にあることがわかります。
 

    if(doc > 0){
        // 表側
    }else{
        // 裏側
    }

 

 




二次元配列を使った当たり判定を取る

 

サンプルをダウンロード
 


 
主にパズルゲームや背景の当たり判定に使います。

 
考え方
 
二次元配列をマップと見立てます。0 は何も無い状態、 1 は壁があるとします。
 

    HitTable = [
        [ 1,1,1,0,1 ],
        [ 1,1,0,0,1 ],
        [ 1,0,0,0,1 ],
        [ 0,0,0,0,1 ],
        [ 0,0,0,1,1 ]
    ];
 
自分の位置を[2][2]とします。x = 2, y = 2
 

    HitTable = [
        [ 1,1,1,0,1 ],
        [ 1,1,0,0,1 ],
        [ 1,0,0,0,1 ],
        [ 0,0,0,0,1 ],
        [ 0,0,0,1,1 ]
    ];
 
ここから上に1つ移動したいと思います。
1つ上の配列は[1][2] なので配列の中身をチェックします。0 なので移動できます。(x = 2 , y = 1)
 

    HitTable = [
        [ 1,1,1,0,1 ],
        [ 1,1,0,0,1 ],
        [ 1,0,0,0,1 ],
        [ 0,0,0,0,1 ],
        [ 0,0,0,1,1 ]
    ];
 
さらに左に1つ移動したいと思います。
一つ左の配列は[1][1] なので配列の中身をチェックします。1 なので移動できません。(x = 2 , y = 1)
 

    HitTable = [
        [ 1,1,1,0,1 ],
        [ 1,1,0,0,1 ],
        [ 1,0,0,0,1 ],
        [ 0,0,0,0,1 ],
        [ 0,0,0,1,1 ]
    ];
 
こんな感じでマスを自分の座標と考えて次に移動したいマスの配列にデータがあるか見ながら移動します。
 
この当たり判定を使うメリットは高速で当たり判定を取る事ができる点です。
どんなに広大なマップでも、処理の負荷は同じです。現在の位置に当たる配列の中身を 1 回見るだけでわかるため非常に高速な処理が出来ます。

そのかわりデメリットとしてマップ用の二次元配列を用意しないといけないためメモリを喰う事になります。