自機、敵機、ショット(弾)とシューティングゲームに必要な最低限のオブジェクトが出揃いました。あとは、それらオブジェクトの相互関係、即ち、「弾が敵機に当たったか!?」「自機に敵機がぶつかったか!?」を判断するための当たり判定のコードを作ります。
今回の章で、シューティング理論の基礎部分すべてが完了することになります。
前回のVol20. 【2Dシューティング】敵機を出現させようで作成したコードに手を加える形で作業を進めます。赤色で示すコードが前回からの変更点または追加点です。
パラメータpos1、size1及びpos2、size2には、2つの物体(自機と敵機、または弾と敵機など…)の位置と大きさが引き渡されます。その2つの物体が2D空間上で重なった場合に、HitTest関数は1を返します。重ならなかった場合は0を返します。
この関数は、fight.sbpの先頭コードの下にでも挿入しておきましょう(※fight.sbp内のグローバル領域であればどこでも大丈夫です)。
'------------------
' 衝突判定用の関数
'------------------
Function HitTest(
ByRef pos1 As POINTAPI,
ByRef size1 As SIZE,
ByRef pos2 As POINTAPI,
ByRef size2 As SIZE) As Long
If pos1.x<pos2.x+size2.cx and pos1.x+size1.cx>pos2.x and _
pos1.y<pos2.y+size2.cy and pos1.y+size1.cy>pos2.y Then
HitTest=1
Else
HitTest=0
End If
End Function
当たり判定の際に、自機の位置と大きさを取得するための関数を定義します。
尚、自機の大きさ(width、height)は32*32というようになっていますが、この大きさを当たり判定用として返してしまうと、敵機をちょっとかすめただけでも当たったと判断されるような、少し不自然な動きをしてしまいます。それを回避するために、実際の大きさの70%を当たり判定用の領域として返しようにします。
例えば、左の画像で説明しますと、赤で囲まれた領域が当たり判定に使われます。かすめただけでは当たりと判定されないように、小さめにしておくのがコツです。
'---------------
' 自機クラス
'---------------
Class CPlane
'位置・高さ・幅
x As Long
y As Long
width As Long
height As Long
'移動スピード
speed As Long
'傾き
RudderSituation As Single
'自機が画面からはみ出さないようにチェックする関数
Sub EreaOutCheck()
If x-width/2<0 Then
x=width/2
End If
If x+width/2>SCREEN_WIDTH Then
x=SCREEN_WIDTH-width/2
End If
If y-height/2<0 Then
y=height/2
End If
If y+height/2>SCREEN_HEIGHT Then
y=SCREEN_HEIGHT-height/2
End If
End Sub
Public
'コンストラクタ
Sub CPlane()
width=32
height=32
'初期位置
x=SCREEN_WIDTH/2
y=SCREEN_HEIGHT*0.8
'初期スピード
speed=5
End Sub
'デストラクタ
Sub ~CPlane()
End Sub
'位置と大きさを取得する関数
Sub GetHitPositionAndSize(ByRef pos As POINTAPI, ByRef size As SIZE)
'機体のヒットポジションは小さめにする
Dim TempWidth As Long, TempHeight As Long
TempWidth=width*0.7
TempHeight=height*0.7
pos.x=x-TempWidth/2
pos.y=y-TempHeight/2
size.cx=TempWidth
size.cy=TempHeight
End Sub
'描画
Sub Draw()
Dim TextureX As Long, TextureY As Long
TextureX=32*(Fix(RudderSituation)+2)
TextureY=0
pImage_CharaMap1->DrawStretch(
x-width/2 +SCREENBASE_X,
y-height/2 +SCREENBASE_Y,
width,
height,
TextureX,
TextureY,
32,
32)
End Sub
'自機を移動させる関数
Sub MoveUp()
y=y-speed
EreaOutCheck()
End Sub
Sub MoveDown()
y=y+speed
EreaOutCheck()
End Sub
Sub MoveLeft()
x=x-speed
EreaOutCheck()
If RudderSituation>-2 Then
'左方向に移動中は自機の傾きをマイナス値にする
RudderSituation=RudderSituation-1/speed
End If
End Sub
Sub MoveRight()
x=x+speed
EreaOutCheck()
If RudderSituation<2 Then
'右方向に移動中は自機の傾きをプラス値にする
RudderSituation=RudderSituation+1/speed
End If
End Sub
Sub NoMove()
'移動していないときは自機の傾きを0に近づける
If RudderSituation<0 Then
RudderSituation=RudderSituation+1/speed
ElseIf RudderSituation>0 Then
RudderSituation=RudderSituation-1/speed
End If
End Sub
Sub Shot()
'弾を撃つ
Dim pShot As *CShot
pShot=New CShot(x,y-width/2)
End Sub
End Class
当たり判定の際に、ショット(弾)の位置と大きさを取得するための関数を定義します。弾については、余裕領域を考えなくても良いので、そのままの大きさを返すようにします。
'--------------
' ショット(弾)クラス
'--------------
Class CShot
x As Long
y As Long
width As Long
height As Long
speed As Long
Sub EreaOutCheck()
'はみ出しチェック
If y-height/2<0 Then
'画面をはみ出したので、自爆させる
Delete VarPtr(This)
End If
End Sub
Public
Sub CShot(sx As Long, sy As Long)
width=4
height=16
speed=15
'初期位置
x=sx
y=sy
AddShot(VarPtr(This))
End Sub
Sub ~CShot()
DeleteShot(VarPtr(This))
End Sub
'位置と大きさを取得する関数
Sub GetHitPositionAndSize(ByRef pos As POINTAPI, ByRef size As SIZE)
pos.x=x-width/2
pos.y=y-height/2
size.cx=width
size.cy=height
End Sub
'描画
Sub Draw()
pImage_CharaMap2->DrawStretch(
x-width/2 +SCREENBASE_X,
y-height/2 +SCREENBASE_Y,
width,
height,
6,
48,
width,
height)
y=y-speed
EreaOutCheck()
End Sub
End Class
当たり判定の際に、敵機の位置と大きさを取得するための関数を定義します。自機(CPlane)のときと同様、ヒットポジションを小さめにセットしておきます。
'--------------
' 敵機クラス
'--------------
Class CEnemie
'位置
x As Long
y As Long
width As Long
height As Long
'スピード
speed As Long
Sub EreaOutCheck()
'はみ出しチェック
If y>SCREEN_HEIGHT Then
'画面をはみ出したので、自爆させる
Delete VarPtr(This)
End If
End Sub
Public
Sub CEnemie()
width=24
height=24
speed=1
'初期位置
x=Rnd()*SCREEN_WIDTH
y=0
AddEnemie(VarPtr(This))
End Sub
Sub ~CEnemie()
DeleteEnemie(VarPtr(This))
End Sub
'位置と大きさを取得する関数
Sub GetHitPositionAndSize(ByRef pos As POINTAPI, ByRef size As SIZE)
'機体のヒットポジションは小さめにする
Dim TempWidth As Long, TempHeight As Long
TempWidth=width*0.7
TempHeight=height*0.7
pos.x=x-TempWidth/2
pos.y=y-TempHeight/2
size.cx=TempWidth
size.cy=TempHeight
End Sub
'描画
Sub Draw()
pImage_CharaMap1->DrawStretch(
x-width/2 +SCREENBASE_X,
y-height/2 +SCREENBASE_Y,
width,
height,
0,
160,
width,
height)
y=y+speed
EreaOutCheck()
End Sub
End Class
ゲームループから毎秒60回(60FPSの場合)呼び出されるInputActionProc関数では、常に当たり判定を行う状況を作り出します。このプログラムでは、自機・敵機・弾の動きを常に監視し、互いに衝突したかどうかを調べています。
敵機に弾が当たったときは、弾と敵機を消滅させます。
自機と敵機が衝突してしまったときは、敵機を消滅させ、ビープ音を鳴らします。通常のゲームであれば、自機を破壊してしまって、ゲームオーバーにしてしまっても良いでしょう。
Sub InputActionProc()
' TODO: この位置に入力に関するコードを記述してください。
' (キーボード、マウス、ジョイパッド、ジョイスティックなどによる入力)
' メモ - キャラクタの移動など、状況進行(アクション)を意味するコードを
' 記述することもできます。
Dim KeyState[255] As Byte
pInKey->GetState(KeyState)
'ESCキーが押されたときは終了する
If KeyState[DIK_ESCAPE] and &H80 Then
PostQuitMessage(0)
End If
If KeyState[DIK_UP] and &H80 Then
'方向キー(上)が押されているとき
pMyPlane->MoveUp()
ElseIf KeyState[DIK_DOWN] and &H80 Then
'方向キー(下)が押されているとき
pMyPlane->MoveDown()
End If
If KeyState[DIK_LEFT] and &H80 Then
'方向キー(左)
pMyPlane->MoveLeft()
ElseIf KeyState[DIK_RIGHT] and &H80 Then
'方向キー(右)
pMyPlane->MoveRight()
Else
'左右キーが押されていないとき
pMyPlane->NoMove()
End If
ShotTime=ShotTime+1
If KeyState[DIK_SPACE] and &H80 Then
'スペースキーが押された、弾を発射
If ShotTime>TempTime_Shot+3 Then
pMyPlane->Shot()
TempTime_Shot=ShotTime
End If
End If
'一定間隔で敵機を生成
TempTime_Enemie=TempTime_Enemie+1
If (TempTime_Enemie mod 20)=0 Then
Dim pEnemie As *CEnemie
pEnemie=New CEnemie
End If
'-----------------------
' 弾と敵機の当たり判定
'-----------------------
Dim i As Long, i2 As Long
Dim pos1 As POINTAPI, size1 As SIZE
Dim pos2 As POINTAPI, size2 As SIZE
For i=0 To ELM(MAX_SHOTS)
If pShotsArray[i] Then
For i2=0 To ELM(MAX_ENEMIES)
If pEnemiesArray[i2] Then
pShotsArray[i]->GetHitPositionAndSize(pos1,size1)
pEnemiesArray[i2]->GetHitPositionAndSize(pos2,size2)
If HitTest(pos1,size1,pos2,size2) Then
'ヒットしたときは弾、敵機ともに破棄
Delete pShotsArray[i]
Delete pEnemiesArray[i2]
Exit For
End If
End If
Next
End If
Next
'-------------------------
' 敵機と自機の当たり判定
'-------------------------
For i=0 To ELM(MAX_SHOTS)
If pEnemiesArray[i] Then
pMyPlane->GetHitPositionAndSize(pos1,size1)
pEnemiesArray[i]->GetHitPositionAndSize(pos2,size2)
If HitTest(pos1,size1,pos2,size2) Then
'ヒットしたときは敵機を破棄(同時にビープ音を鳴らす)
Delete pEnemiesArray[i]
MessageBeep(0)
Exit For
End If
End If
Next
End Sub
敵機に向かって弾を発射してみてください。敵機を倒すことができるようになったはずです。シューティング理論の基本はここでおしまい。あとは、このシューティングテンプレートを拡張してオリジナルのゲームを作るだけです。
次回からは、このテンプレートをどのように拡張すればよいのか、効果音や爆発アニメーションを追加しながら解説していきます☆
講座インデックスへ戻る | ©2005 Discoversoft |