HSP3 ゲームのプログラミング
前へ 目次へ 

(5) キャラクタ同士の衝突判定

 画像に赤色のバーを追加して、キャラクタとバーの衝突について考える。

ダウンロード move3.bmp (58.6KB)

 図のように、それぞれの画像は指定した座標に画像の左上が来るように描画される。
 図では、キャラクタは座標(x0,y0)に描画され、バーは座標(x1,y1)に描画される。この座標(x0,y0)と(x1,y1)を比較して、一致すれば衝突した、一致しなければしていないと判定できる。判定は、一度にできないので、条件式は「x0 = x1 & y0 = y1」と記述する。
 しかし、1点だけで衝突判定すると実際にはなかなか衝突にはならない。ある程度、範囲を持たせて判定することになる。例えば、「x0 - 10 <= x1 & x0 + 10 >= x1 & y0 = y1」と記述するとx座標が左右10ドットの範囲で衝突したと判定される。範囲は内容によって変更が必要である。
 

 まず、x方向について考える。実際には図のように、2つの画像から見た目で衝突している範囲を考えて、衝突の範囲を決定する。図では、±16ドットの範囲で衝突したと判定するのが適当とした。
 判定の条件式は、次のようになる。

   x0 - 16 <= x1  &  x0 + 16 >=  x1  … 条件式@

 また、x0 - x1の計算結果が -16 〜 16の範囲内であれば衝突したとも判定できる。条件式は次のようになる。

   abs(x0 - x1) <= 16  … 条件式A
 


 y方向についても、図のように4ドットの範囲(y0+28≦y1≦y0+32)で衝突とさせる。条件式は次のようになる。

   y0 + 28 <= y1  &  y0 + 32 >= y1  … 条件式@’

 y0+28<=y1はy0-y1<=-28、y0+32>=y1はy0-y1>=-32と変形される。y0-y1の計算結果が-28〜-32の範囲内であれば衝突したと判定できるが、-n 〜 +n のような範囲ではないので、このままではabsを使った式にできない。しかし、条件式の両辺に30を加算するとそれぞれ次のように-n 〜 +nの範囲になる。
   y0-y1<=-28 → y0-y1+30<=-28+30 → y0 - y1 + 30 <= 2
   y0-y1>=-32 → y0-y1+30>=-32+30 → y0 - y1 + 30 >= -2
 y0 - y1 + 30の計算結果は、-2 〜 2の範囲になる。したがって、条件式は次のようになる。

   abs(y0 - y1 + 30) <= 2  … 条件式A’
 


 以上から、衝突判定の条件式は次のようになる。

   abs(x0 - x1) <= 16  &  abs(y0 - y1 + 30) <= 2

 図のように、x方向のサイズが違う場合も、y方向で示したような式の変形が必要になる。
 図のように衝突の判定をするとき、x0 <= x1 かつ x0 + 16 >= x1 となる。
   x0<=x1   → x0-x1<=0   → x0-x1+8<=0 + 8  → x0 - x1 + 8 <= 8
   x0+16>=x1 → x0-x1>=-16 → x0-x1+8>=-16+8 → x0 - x1 + 8 >= -8
 x0 - x1 + 8の計算結果は、-8 〜 8の範囲になる。したがって、条件式は次のようになる。

   abs(x0 - x1 + 8) <= 8  … 条件式A’’
 

サンプル(move51.hsp)
 キャラクタをバーで打ち返す。

    buffer 1                    ;ウィンドウID 1番
    picload "move3.bmp"         ;画像ファイル読み込み
    screen 0                    ;ウィンドウID 0番
    gmode 2, 32, 32       ;コピーモード設定
    x0 = 100 : y0 = 100   ;キャラクタの座標
    dx0 = 8 : dy0 = 8     ;キャラクタの移動量
    x1 = 100 : y1 = 450   ;バーの座標
    dx1 = 16 : dy1 = 0    ;バーの移動量
    repeat
        redraw 0            ;仮描画
        color 255, 255, 255    ;白色。背景と同じ色。
        boxf            ;ウィンドウと同じサイズの四角形を塗りつぶしで描画する。
        color 0, 0, 0        ;黒色
 
        pos x0, y0               ;カレントポジション設定
        gcopy 1, 0, 0            ;画像コピー
        pos x1, y1               ;カレントポジション設定
        gcopy 1, 64, 0, 32, 8    ;画像コピー
 キャラクタとバーの
 描画処理
        if abs(x0 - x1) <= 16 & abs(y0 - y1 + 30) <= 2 : dy0 = -dy0        ;衝突判定  衝突判定
        x0 = x0 + dx0        ;x座標加算
        y0 = y0 + dy0        ;y座標加算
        if x0 >= ginfo_winx - 32 : dx0 = -dx0  ;右壁に衝突。
        if x0 <= 0 : dx0 = -dx0                ;左壁に衝突。
        if y0 >= ginfo_winy - 32 : dy0 = -dy0  ;下壁に衝突。
        if y0 <= 0 : dy0 = -dy0                ;上壁に衝突。
 キャラクタの
 座標計算 と
 判定
        stick key, 15    ;キー入力
        if key & 1 : x1 = x1 - dx1    ;左
        if key & 4 : x1 = x1 + dx1    ;右
        if key & 128 : end            ;[Esc]
        if x1 <= 0 : x1 = 0    ;左壁に衝突
        if x1 > ginfo_winx - 32 : x1 = ginfo_winx - 32    ;右壁に衝突
 キー入力とバーの
 座標計算 と

 判定
        redraw 1        ;実描画
        await 100       ;無限ループの時のお約束
    loop
    stop
 

実行キャラクタをバーで打ち返すとキャラクタがはね返る。打ち返しに失敗しても下の壁ではね返る。

 キー入力とバーの座標計算にある判定は、左右のウィンドウ枠を超えないようにするものである。この判定の2行は次のようにlimit命令(関数)で書き換えることができる。

   x1 = limit(x1, 0, ginfo_winx - 32)

試してみようサンプルプログラムの衝突判定には「条件式A’」を使っている。これを「条件式A」に変えて実行せよ。

   if abs(x0 - x1) <= 16  &  abs(y0 - y1 + 30) <= 2 : dy0 = -dy0 
      ↓
   if abs(x0 - x1) <= 16  &  y0 + 32 = y1 : dy0 = -dy0

実行バーで打ち返すことができない。
 原因は、バーのy1(y座標)は450で、キャラクタのy0(y座標)は100から8(dy0)ずつ増える。しかし、y0が450になることはないので衝突したと判定されない。

y0+32の変化 132 140 148 156 164 172 180 188 196 204 212 220 396 404 412 420 428 436 444 452

 対策としては、次のようにキャラクタとバーの座標を移動量から計算する。

dx0 = 8 : dy0 = 8    ;キャラクタの移動量
x0 = 10 * dx0 : y0 = 10 * dy0    ;キャラクタの座標
x1 = 10 * dx0 : y1 = 54 * dy0    ;バーの座標
dx1 = 16 : dy1 = 8    ;バーの移動量

 もう一つの対策としては、HSP標準のウィンドウサイズは640×480であるが、図のように8ドット単位のグラフ用紙だと考えて、座標はグラフの交点だけを使うようにするとよい。


サンプル(move52.hsp)
 キャラクタをバーで打ち返す。ただし、画面を8ドット単位のグラフ用紙として考える。

    buffer 1                ;ウィンドウID 1番
    picload "move3.bmp"     ;画像ファイル読み込み
    screen 0                ;ウィンドウID 0番
    gmode 2, 32, 32    ;コピーモード設定
    mm = 8             ;間隔
    x0 = 12 : y0 = 12           ;キャラクタの座標
    dx0 = 1 : dy0 = 1           ;キャラクタの移動量
    x1 = 12 : y1 = 55           ;バーの座標
    dx1 = 2 : dy1 = 0           ;バーの移動量
    repeat
        redraw 0            ;仮描画
        color 255, 255, 255     ;白色。背景と同じ色。
        boxf            ;ウィンドウと同じサイズの四角形を塗りつぶしで描画する。
        color 0, 0, 0       ;黒色
        pos x0 * mm, y0 * mm    ;カレントポジション設定
        gcopy 1, 0, 0           ;画像コピー
        pos x1 * mm, y1 * mm    ;カレントポジション設定
        gcopy 1, 64, 0, 32, 8   ;画像コピー
        if abs(x0 - x1) <= 2 & y0 + 3 = y1 : dy0 = -dy0         ;衝突判定
        x0 = x0 + dx0       ;x座標加算
        y0 = y0 + dy0       ;y座標加算
        if x0 >= (ginfo_winx - 32) / mm : dx0 = -dx0  ;右壁に衝突。
        if x0 <= 0 : dx0 = -dx0                      ;左壁に衝突。
        if y0 >= (ginfo_winy - 32) / mm : dy0 = -dy0  ;下壁に衝突。
        if y0 <= 0 : dy0 = -dy0                      ;上壁に衝突。
        stick key, 15           ;キー入力
        if key & 1 : x1 = x1 - dx1          ;左
        if key & 4 : x1 = x1 + dx1          ;右
        if key & 128 : end                  ;[Esc]
        x1 = limit(x1, 0, (ginfo_winx - 32) / mm)
        redraw 1        ;実描画
        await 100       ;無限ループの時のお約束
    loop
    stop

 



課題(move97.hsp)
 下壁に当たったら、はね返らずにメッセージ(ダイアログ)を表示して、再開または終了できるようにする。


(リスト)新しいウィンドウで開く


前へ 目次へ 
2007 © Hiroshi Masuda

 

 

inserted by FC2 system