2019年02月18日

プチコンゲームプログラミング講座~第四回「シューティングゲームを作ろう」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 やっとそれらしい中規模ゲームまで行きましたが、アクションゲームの王道であるシューティングゲーム(以下STG)が出ていませんでしたね。STGと言っても固定画面、縦スクロール、横スクロール、全方向などいろいろあります。縦STGはプチコンのサンプルゲームでGAME4がありますが、今回はGAME4と違う、縦スクロールSTGを作りましょう!

教材プログラム 4 「クセヴィオウス」(ファイル名「KOZA04」)

koz04_s1.jpg

★ ゲームの内容と説明 ★
 ゲーム名は、いい名前が思いつかなかったもので…wあと本来こうしたゲームは沢山の敵・地上物・爆弾・ボスキャラがいますが、今回もプログラムや説明文が肥大化するので見合わせました。と言っても改造・追加しやすいよう作ってあるので、今後これにさらに追加したサンプルゲームを発表できるかも知れません。
 今回から動かすキャラが非常に増えたため、これまでの連載で少しだけ使ったスプライトや配列変数を、沢山使っています。スプライトをゲームで使うためには、ただ表示させたりキャラを変えるだけでなく、「現在どのスプライトキャラがどこでどんな状況になっているか」常時管理・判定しなければいけません。それにはSPVAR関数やOUTオプションの方法もあるんですが、ここではスプライトと同じ条件で配列変数を設定する方法です。
 一般にスプライト管理番号はゼロから始まり、1,2…と増えていく。配列の添字もゼロから1,2…そう!スプライトと配列で、番号を一致させてしまえばいいのです。これが初心者には簡単でいいと思います。ここではこれを「S番号」と書きますが、連載での便宜上のもので、プチコンユーザやプログラマーで広く使われている訳ではありません。
 ヘビゲームでは動きが簡単なので、キャラを16ドットずつ動かしましたが、今回は微妙なズレが生死を分けるSTGなので、1ドット単位の移動や当たり判定です。

koz04_p1.jpg

★ プログラムの説明1:初期設定とメインループ ★
 まずS番号は、何番から何番までどのキャラに使うか決める事が重要です。全部の配置を実数にすると修正が面倒なので、各キャラの数を自機(変数A_MAX)・自弾(同じくB)・敵(C)・敵弾(D)で設定して、各キャラのS番号の最初(●_s)と最後(●_E)を求めています。ちと面倒に見えるが、紙に書いて計算して行くなどして、それぞれのS番号を復習してみましょう。その下のFORループを使った8~10行は、キャラの種類ごとの初期設定です。9行目のSPCOLの「8,16」は、当たり判定の範囲を横8ドットx縦16ドットに変えるという意味です。
 S番号で使う配列は以下の通り。あまり使っていない配列もありますが、将来プログラムを拡張しやすくするためです。
・SS[i]→キャラの状態(ステータスのS)
・SX[i],SY[i]→縦横の座標
・SVX[i],SVY[i]→移動方向(ベクトルのV)
@INIT1 リプレイ時の初期設定
@INIT2 ミスした後の初期設定
 自機のデザインは、現代型縦シューにふさわしいデザインが三種類あったので、22行でRND関数により、三種類をランダムに出してみました。S番号[0]は前述通り自機の初期設定です。OPは当時のナムコゲーを真似てみました。24行で字を出した後、変数OPをゼロにして、短時間で演奏終了するBGMPLAY 4を流しています。次の@LOOPでOPがゼロ(まだ1になってない)かつBGMCHKがゼロ(BGMが終わって聞こえなくなった)ら、OPのフラグを1にして先の字を消し、エンドレスのBGMPLAY 25を演奏します。
@LOOP メインループ
 28行目は本来ゲームに必要ありませんが、学習用に入れてみました。自機がスタートする毎に、左上に出る変数CNは、WAITが1進むたびに蓄積するカウントが表示されます。また以前ちょっと書いた、OUTという予約語が登場しています。これは通常の命令が「命令語 パラメータ1,パラメータ2,パラメータ3…」と書く所を、「命令語 パラメータ1 OUT パラメータ2,パラメータ3…」と書くと、パラメータ1(この場合はBG0番)のパラメータ2やパラメータ3の今の値が読み込める…というものです。使える所しか使えないオプションですが、うまく使えば便利です。ここで読み込んだ値はBG0のX座標とY座標で、これを右に表示しています。これでスタート時からどこまで進んだかが判るでしょう。「どこまで進んだら、どこでどんなザコ敵やボスキャラが出てくるか」は、この数値で判定すればいい訳です。自機の当たり判定は@ATARIの結果、SS[0]が1なら@DEADに★行きます。

★ プログラムの説明2:定義済み処理(メインキャラ) ★
 STGやアクションゲームのキャラの処理は、一般に4つか5つ。「自機」「自機の攻撃手段(ここでは弾)」「敵」「敵の攻撃手段」「特殊なキャラ(ボスなど)」です。今回特殊なキャラはいないので4つとなります。
@LOOPでの自機移動のタイミング
 @JIKIから@TEKITAMAまで4種類のキャラを、順番にGOSUBで呼び出します。S番号が複数ある時の処理は前述したが、「GOSUBで呼ぶ前の@LOOPにFORを書く」「GOSUBで呼んだ後の各処理にFORを書く」のうち、今回は前者を使っています。
@JIKI 自機
 自機は1キャラだけなのでFORは使いません。基本はヘビゲームと同じで、十字ボタンの方向に動くだけですが、違う点はBUTTON関数の取得です。以前の「IF BUTTONの値==値」だと「他のボタンを押さず、この値のボタンだけ押した」ですが、今回の「IF (BUTTONの値 AND 値)」だと「他のボタンを押しているにかかわらず、この値のボタンも押した」になります。これだと右と上どちらも押したと判定すれば、右上に進めます。この辺は次回説明します。
 STGではここから下、自機以外は動かし方の基本が同じです。
1.画面に出ていない(このプログラムではSS[i]が-1)なら、条件によっては画面に出す。
2.現在画面に出ていて、敵や味方に当たったり、画面の端に入っていたら、必要に応じて消えた処理をする。
3.でなければ座標を進め、新たな座標のキャラを表示する。
@JIKITAMA 自弾
 49行から新規出現処理。SS[i]が-1かつBUTTON()関数でAボタンが押されていると、自機の座標変数などを自弾に移し、発射音を出します。変数S_Tは、一発撃つとしばらくカウントをとって時間待ちをする連射間隔用です(値の変え方は後述)。58行から出現ずみの処理。59行では自弾が画面上まで行ったら、@S_LOSTで消しています。60行では1ドット上に進めて、SPOFSで表示し直しています。
自弾が敵に当たった時の処理
 ここから当たり判定の説明です。文字(CHRCHK)・グラフィック(GSPOIT)・BG(BGGET)は「その座標に何が描かれているか」だけでいいが、スプライトは各自がバラバラに動くので、似た命令が使えません。当たり判定は「SPHITSP式」「座標式」の二種類のうち、ここでは前者を使っています。
 SPCOLはこの処理に必ず必要な命令で、当たり判定を出したい双方のスプライトに、同じマスク(16進数で同じ場所のビットに1を入れる)を設定します。今回は「自弾が敵に当たった」の判定用に、一番右の"00000001"だけ使っています。この辺は複雑なので次回説明します。マスクを0に戻している処理があるのは、爆発中もマスクに当たり判定があると、余計に当たってしまうバグの防止です。
 63~68行が当たり判定のメイン処理で、SPHITSP(当てられたか判定したいS番号,当たってきたS番号の最初,当たってきたS番号の最後)関数の結果をSHに入れます。これが当たってないと-1、当たったらそのS番号を返すので、64行から当たった時の処理をします。65~66行でスプライトを爆発のアニメーションに変え、SS[SH]に-1より小さな値を入れて、爆風表示中はこのS番号で新規出現しないようにしています。
@TEKI 敵
 74行から新規出現処理。SS[i]が-1で、かつ64分の1の確率で出しています。出現位置(SX,SY)は上、移動は左右(SVX)がどっちか斜めで、下(SVY)は2ドット。79~80行は前述の爆発中の時間待ち処理で、SS[i]が-1になるまで+1し続けています。現在出現中のフラグは現在、SS[i]を0にしているが、1や2でも動く様になっています。これは何種類も敵を出した時、出現条件や移動方法の違いを「ELSEIF SS[i]==…」で区別するためです。82行は@JIKITAMAと同じで画面外に行った時の消滅処理、 85~86行で移動と当たり判定をしています。
@TEKITAMA 敵弾
 敵弾は「敵の処理中に出す」「敵弾の処理中に出す」の二種類あり、今回は後者つまりここでなく@TEKIの87~88行でやっています。S2で敵弾のS変数をランダムに出し「OPが1(OPが終わっている)」「SS[S2]が-1」「発射元の敵が画面内の一定の座標か」「もう一度乱数で確率を出す」の条件に会えば、敵Sの位置から敵弾S2を出現させます。出現していたら下に3ドットずつ移動。@S_LOSTや@ATARIは後述します。

koz04_s2.jpg

★ プログラムの説明3:定義済み処理(その他) ★
@ATARI 座標式の当たり判定
 座標式は「インベーダー」の砲台やUFOのように、キャラの数や存在位置が限られていると、処理が簡単という長所があります。ここでは自機の[0]とGOSUBで呼び出す前のS番号で、縦座標と横座標の差を求め、どちらも15未満だったら自機に当たったので、SS[0]に「死亡フラグ」を立てています。ABS関数は本来「絶対値」を出すもので、数にマイナスがあると無条件で除去しますが、このように座標による当たり判定には最適です。
@S_LOST 特定のスプライトを画面から消す
 変数S2に入ったS番号をSPOFSで画面外左上にやり、SS[i]を-1にして、画面上も変数上も消します。
@RESET ゲームに必要な画面を一端クリア
 110行はヘビゲームと同じ。111行はS番号全て@S_LOSTで消しています。BGOFSはBYから15を引いた、つまり背景の下から15行目までBGをずらす処理。BGANIMは順に「0=BG0番の」「"XY"=座標を」「-600=600fps(10秒間)を一気になく徐々に」「0,0=BG座標0,0まで変えて」「0=無限に繰り変えす」という処理です。SPANIMやBGANIMは、こうして無限に同じことを繰り返す処理が可能で、同じ働きをする別の命令(ここではBGOFS)を使うと、ピタリと止まります。
@SCR 「DEF JI」を使って、スコアやハイスコアを表示
DEF JI LX,LY,L$ BGを使って文字列を表示
@DEAD ミスおよびゲームオーバー
 127~128行は@JIKITAMAと割と同じです。129行ではまたOUTを使ってBG座標を取得し、すぐOUT無しのBGOFSで実行して、BGANIMを止めています。アクションゲームは三回死ぬとゲームオーバーが基本なので、@INIT1でZAN=3と設定し、130行でマイナス1して、ZANがまだゼロでなければ@INIT2に戻り、でなければゲームオーバーです。

koz04_p2.jpg.jpg

@MAP 背景のDATAを読み込んでBGの0番に描く
 一度しか使わないが行数が多く、全部前に置くとリストが見にくい場合は、このようにサブルーチンにして後ろに置きます。
 変数BYは地形に使うBGの縦長さで、BGSCREEN命令で0番BGのサイズを変えています。140行は第二回で説明した、各画面表示要素の高さ設定で、これがそのままだったり設定を間違えると、例えば地上物の下を飛行機がくぐったりしますwそこでFORループでBGの0(背景)を16、1を12、2を8、3(文字表示)を4の高さに設定しています。(ただし1と2は現時点で未使用)。GPRIOも第二回にあった通り。
 141行はBG0をまず全部森(103番)で埋めていますが、ゼビウスのように最初と最後の15行ずつは森にしているので、森の一行下である15を変数Yに入れ、一行描くたびに155行でYを増やしています。
 142~157行はいよいよ、BGに不規則なBGキャラを入れる方法です。プチコンのエディタを使い、BGSAVEで保存する方法もありますが、今回は「ドラクエ1」の街などのようにあまり広くないので、DATAで書いたものを読むという、簡単な方法です。
 READでA$を読み込み、このA$に一番最後の""(文字列長さゼロ)が入るまで(ここでは20行分)REPEATを続けます。FOR X=0 TO 24とMID$関数で、A$の0番目から24番目までの文字列を一つずつ求めてB$に代入します。そして147~152行で、このB$が何という文字列であるかで、BG番号をCに代入し、153行のBGPUTで書き込んでいます。実は146~153行は、配列を使うと短くなるので、できる人は考えてみて下さい。本当はゼビウスに似せたかったのですが、時間が間に合わず、HSPのサンプル用フリーゲームである「シューティングゲーム」の地形画像ファイルを参考にしました(^^;

★ プログラムの修正方法 ★
 処理が簡単なSTGは、パラメータ修正で難易度を変えやすい点も特徴です。最大数やスピードなど修正できる所を並べてみました。いろいろいぢって、一喜一憂してみて下さい。なお数は1以上でないと、エラーになるものがあります。

koz04_e1.jpg

(第五回につづく なお次回はサンプルゲームを一回お休みして、コンピュータには欠かせない二進数・10進数・16進数と、プログラミングへの応用についてお話します)
★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。
公開キー  :D8EA4CD(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
※次の更新は、2月21日、『レトロゲーレビュー』の予定です。お楽しみに!