第7回 逆引き:プログラムを分解解説 「5.衝突判定と燃料補給」
|
|
7−5 衝突判定と燃料補給
次は、隕石との衝突判定を説明します。 NS Basicのコマンドでは、「そこに隕石を描く」事はできるのですが、「そこに隕石があるか」ということが分かりません。 通常は、その座標に何か描かれているか?というような関数があり、例えば、VBではPoint(X,Y)という関数で、座標(X,Y)の位置に表示されている色コードを返します。 しかし、NS Basicの標準の関数ではそのような機能を持ったものがありませんので、多少の工夫が必要です。 さて、隕石画像は、先ほどもありました通り、
となっています。 実は、お気づきの方もあるかもしれませんが、隕石も自機の移動量と同じ15ピクセル毎、の8段に描き分けられています。 ![]() 赤い線は15ピクセル毎の線ですが、このように、隕石の位置も「段」の概念を取り入れて作りました。 この尺度で考えると、自機の位置を表す変数Yiが0〜7でしたから、画像番号から1007を引いた値が、このYiと同じ尺度になります。 また、この画像番号は、スクロール用の配列変数Scrn()に入っていましたが、このうち、自機が表示されているのは、Scrn(1)の位置になりますので、
If Scrn(1)-1007=Yi Then
とすれば、この条件が成立する時に、衝突したことになります。ただし、ブランク画像や燃料画像もありますので、
If Scrn(1)<1014 Then
If Scrn(1)-1007=Yi Then
Call Crush
Redraw
End If
End If
と、Scrn(1)<1014の条件を先にチェックしておけば、隕石の時だけ判定することが可能です。さて、この中にCall Crushという部分があります。 Callコマンドで別のサブルーチンを呼ぶ方法は初登場ではありませんが、今回のCrush()というサブルーチンは、共通モジュールではなく、FormのAfter部分に記述されています。 NS Basicでは、共通モジュールを用意しなくても、同一のルーチン中(コード編集画面で表示されている範囲内)でいくつでもサブルーチンや関数を作ることが可能です。 ただし、そこで定義したサブルーチンは、同一のルーチン内からしか利用することが出来ません。 ![]() 例えば、今回の衝突処理を行うCrush()というサブルーチンは、Form1014のAfter内からしか使いませんので、この場所に定義しています。 一方、初期設定を行うInit_Screen()はスタートアップでも利用しますから、共通モジュールとして定義しているわけです。 さて、隕石に衝突したら、Callコマンドによって、Crushサブルーチンを呼び出して衝突処理をしています。 Crushサブルーチンの最初の、
For i=0 to 10
DrawBitmap 1018,5,Yi*15+25
Delay 0.1
DrawBitmap 1019,5,Yi*15+25
Delay 0.1
Next
この部分は、爆発の表示です。1018と1019の2つのビットマップを交互に表示して、爆発の雰囲気(?)を出しています。次のFillRectangleコマンドは、自機の残数処理ですが、この部分を説明する前に、自機の残りを表示している部分を説明しましょう。 自機の残数表示は、10x10ピクセルのビットマップ1020を使って、次のように表示しています。
For i=1 to MyShip
DrawBitmap 1020,90+i*11,148
Next
10ピクセル四方の画像の座標を、i*11と、11倍していますので、お互いの画像に1ピクセルの隙間をおいて並んでいる訳です。そして、これは、[After]のDo〜Loop内で、ReStartという共通変数が 1 の時だけ処理される位置に入っています。(ゲーム中では「Ready!」の表示をしている部分です)
If ReStart=1 Then
DrawChars "Ready!",65,75
For i=1 to MyShip
DrawBitmap 1020,90+i*11,148
Next
DrawLine 5,147,155,147
Delay 1.5
ReStart=0
End If
この部分では、Ready!、の表示をするとともに、自機の残りを表示し、エネルギー残量の初期値を表示し、Delayコマンドで、1.5秒待ったあと、ReStart変数の値を 0 にして処理から抜けます。Do〜Loopの外に置いてもよいのですが、一度、自機や背景が描画された後に位置していますので、画面表示のルーチンをあえて作らなくても済み、プログラム的に楽なため、ここに入れてみました。 さて、Crush()サブルーチンに戻りましょう。 表示ルーチンでも分かるように、自機の残りと表示の関係は、([残り数]×11+90,148) になります。 したがって、衝突した時は、自機の残り表示の一番右端、つまり、残り数MyShipの位置に、空白の四角描いて、自機を消しています。
FillRectangle 90+MyShip*11,148,10,10,0,nsbInverted
MyShip=MyShip-1
表示したら1機減らせばい良いので、その後にMyShip変数から 1 を引いています。そして、自機の残りがある間は「Ready!」に戻りたいですし、自機がなくなれば「ゲームオーバー」ですね。
If MyShip<0 Then
DrawChars "GAME OVER",55,75
Flg3=1
Else
Delay 1.5
Call Init_Screen
End If
この部分で、MyShip変数が 0より小さくなったら「GAME OVER」表示をしてゲームオーバー用の共通変数 Flg3 に 1 を代入しています。Flg3変数が 1 になると、ゲームオーバーです。したがって、もう、Do〜Loopを行う必要がありませんので、Do〜Loopの条件にFlg3の条件を追加します。
Do Until (SysEventAvailable()=1 And Flg=1) Or Flg3=1
これで、ゲーム中にキーを押したか、ゲームオーバー時だけDo〜Loopから抜けることが出来ます。一方、まだ、残機がある場合、Callコマンドで画面の初期化ルーチン Init_Screen()を呼び出しています。 Init_Screen()は、
そして、Callコマンドの後に Redrawコマンドがあるので、ちゃんと[After]の始めから処理されるのですが、そもそも[After]は画面の描画が完了した場合に発生するイベントでしたね? 画面の描画が終わった処理をしている最中に、再度、Redrawで再描画するとは、なんだか、プログラムは休む暇なしですね。 また、自機が壁に衝突した場合、これは、変数Yiが1〜6以外の値になった時(ま、0か7にしかならない筈ですが)ですね。 ついでに、燃料が 0 になった時も衝突扱いとすれば、隕石との衝突の場合と同様に、
If Yi<1 Or Yi>6 Or FUEL=0 Then
Call Crush
Redraw
End If
とすれば処理できますね。燃料は、FUELという変数を使っていますが、これはDo〜Loopが1回実行される毎に1ずつ減っていきます。
FUEL=FUEL-1
初期値は、150で、Init_Screen()ルーチン内で初期化しています。実際の表示は、1本の線ですね。 初期表示は、
DrawLine 5,147,155,147
で、消していくのは、自機の残りを表示するのと同じ手法を用いて、
DrawLine 5+FUEL,147,155,147,nsbInverted
としています。そして、その逆の燃料が増える方、つまり、燃料補給は衝突処理とまったく同じになります。 自機の位置が Yi=6 で、背景画像が1016の時、燃料画像と重なっていますから、次のように処理します。
If Scrn(1)=1016 And Yi=6 Then
FUEL=FUEL+20
If FUEL>150 Then
FUEL=150
End If
DrawLine 5,147,5+FUEL,147
End If
一度燃料を補給すると+20されますが、最大は150までとして、If〜Thenを使って調整しています。そして、増えた分の描画していますが、これら補給に関する処理は、衝突などで一度お目にかかった手法を使っていますので、それほど難しくはないでしょう。 「スクロール」「移動」と続いて「衝突・補給」と3つの処理を説明しましたが、いかがでしょうか? だんだん難しくなりましたが、ここまでの流れが理解できれば、ほぼゲームの骨格は出来ていますね。わかんなくなったら、もう一度、読み直しましょう。 また、説明がわかりにくいようでしたら、苦情のメールをお送り下さい(TT)
|
||
第7回 逆引き:プログラムを分解解説 「5.衝突判定と燃料補給」
|