2018年12月17日

プチコンゲームプログラミング講座~第二回「ヘビゲームを作って、スプライトやBGも使ってみよう」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 前回は書いた通り二本紹介予定でしたが、容量ミスで一本になってしまいました。今回はどうしようか、三回書いてこの内容であまり進んでいないのは気が引けたんですが、学習内容としても好みとしても外せないと考えて、二本目を載せます。ただ載せるだけでなく、当初はやはりテキストキャラのみでしたが、スプライトやBGなど多少考えた命令も入れて、自己解析してもらう事で、もう一段レベルの高い学習ができるようにしました。
教材プログラム 2 「ヘビゲーム」(ファイル名「KOZA02」)
KOZ02_S1.jpg

★ ゲームの概要 ★
 商業作品でも「バリケード」「トロン」などの類似作が出ていました。昔から初心者学習用ゲームの模範とされています。その理由は
1.動き続けるキャラがマイキャラしかいないので、プログラムが短く簡単。
2.高級言語の実行速度が遅かった時代にも、動かすキャラが少ないので、ゲームがじゅうぶん高速で楽しめた。
3.動いた後の体が障害物になるので、動いた距離に比例して難易度が高くなる。
4.エサや障害物も配置が単純なので、ゲームバランス調整や、改良してオリジナルゲームを作ることも簡単。
 ゲームの内容は…タイトル画面を見ればわかるので、パス!w

★ プログラムの説明 ★
 前回までは行番号順でしたが、プログラムの行数が長くなり、それだと文章の量も増えてしまうので、今回から処理内容ごとにも説明しています。また前回の説明と同じ部分のプログラム技術は略しています。

★ 初期設定 ★
@プログラムを起動した時、一度だけ設定に使う命令
 1行:GPRIOという命令が出ています。これは文字・グラフィック・スプライト・BGを使う時の、レイヤー(どの表示がどれから優先的に上から出てくるか)の命令で、プチコンの起動時のままでは、グラフィックが下に隠れてでなくなってしまうため、本来より上の位置である"0"に出しています。レイヤーについては後の連載でまた書きます。
 2行:3つの変数に数字を入れています。それぞれ「マイキャラの頭」「マイキャラの胴体としっぽ」「宝箱」のBG番号です。こうしたキャラを変える場合、キャラの番号に関する命令に、そのまま数字(例えば胴体なら「BGPUT…372」や「IF BGGET(…)==372」など)を入れておくと、後からキャラを変える時面倒なので、数字でなく変数にしてあります。
 順番がずれますがBGFILLは、指定した四角い範囲をBGで埋める(埋める=FILL。グラフィックのGFILLも同じ)命令です。パラメータは「BGFILL レイヤー番号,X座標1,Y座標1,X座標2,Y座標2,BG番号」です。これは背景が黒いのではつまらないので、目にやさしい緑の芝(103)で塗ってみましたが、一番下のスコア表示部も同じにすると見にくいので、BGのY座標で言う14行目を、茶色の床(573)で塗ってみました。
 SPSETはスプライトでは必ず最初に使う命令で、「スプライト管理番号の0番を使う。キャラクター番号はとりあえず0番にするよ」という意味。スプライトは必ず、SPSETを宣言しないと使えません。今回は頭に使っているだけで、当たり判定などを使うアクションゲームなどでの使い方は、後日解説しましょう。
@INIT1 ゲームを開始するたびに行う初期設定
 最初の変数はゲームスタート時のデータで、LVは1つまり1面から、BGMはBGM番号で0から始まります。
 今回からいきなりゲームが始まるのでなく、オープニングタイトルを入れてみました。ここではゲームの説明文を6行書いていますが、PRINTでは字が小さいので、ここでの表示専用のDEF SYMBOLを作りました。詳しくはDEF SYMBOLを見て下さい。
 また同時に使っている新命令として、READが登場しています。これは変数に入れたい中身を、DATAの後に書いておけば、「READ 変数」と書くことで、その中身が変数に入るという命令です。文章などのように、不規則なデータの並べ方をコンパクトに収めるのに便利です。読まれるDATAの位置は、プログラムの先頭から順番(この場合「FOR Y=0 TO 5」と繰り返しているので6回)に進みます。しかし一度読むと読み込みの位置は戻らず、READがあってもDATAが無いとエラーになるので、@INIT1を実行するたび、「RESTORE @INIT2」で、読み込み位置を@INIT2の先頭に戻しています。

KOZ02_P1.jpg

@INIT2 面クリアのたびに行う初期設定
・22行:DEF JIでスタートメッセージを出し、36行のDEF JIで消しています。36行で「" "*25」と言う書き方がありますが、これはその文字列をその回数という事で、ここではスペースを25字分、「"ABC"*3」なら「ABCABCABC」という文字列が使えます。
・23行:LV一回分につき5個ぶんの障害物をBG1番に置いていますが、同じBGだとつまらないので、プチコンに入っている、チェスの駒6種類を置いています。次の24行もよく似た記述ですが、こちらはLVと同じ数だけ、TKRつまり宝箱を置いています。それぞれの行で、何が同じで何が違うか、よく考えてみましょう。
・25行:同じ方法で頭を置いていますが、スタートしてすぐ障害物にぶつかると困るので、ちょっと変えています。まずRND関数による座標(変数はAXとAY)の配置を23・24行より小さくして、外周に絶対頭を置かない。次に置こうとする場所をBGGET関数で調べて、既に何か置かれている(BGコードがゼロでない)なら、UNTILでもどしてやり直す。この二点をクリアしたら、SPCOLORで色、SPCHRでスプライトのキャラ番号(ATM)、DEF ATAMAで座標AX,AYの位置に頭を出します。
・27~31行:@LOOPによく似ていますが、RND関数で0から3の乱数を出し、それに応じて、最初に進む方向(変数はVXとVY。Vはベクターつまり方向の意味)を決めています。
・33行:SPANIM命令で、頭のスプライトを規則的に変化させ、チカチカ点滅するようにしています。SPANIM命令はパラメーターがちょっと難しいので、後日また解説します。35行はボタンを押すまでゲームを開始せずに待っています。これはいきなり頭が動き始めると、プレイヤーが慌てている内に、ぶつかってゲームオーバーになるのをふせぐためです。最後に36行で、表示関係を準備中からゲーム中に変え、BGMを鳴らします。

★ メイン処理 ★
@LOOP 基本処理
・39~43行:おなじみ押ボタン処理です。44行では頭のある位置にBGで尻尾を描き、新たな移動先の座標NXとNYを求めて、45行で頭の表示と歩行音を出しています。
・46行:少し長いIF文は、NXとNYがフィールドの外に出たかを判定し、出たらもう@GAMEOVERに飛ばしています。普通ヘビゲームでは、周りに壁を描く事が多いのですが、BGでは25x15マスとヘビゲームにはやや小さく、しかも一番下はスコア表示に使います。このため壁を描かないようにして、少しでもフィールドを大きくしています。もちろん壁があったとしても、ヘビゲームでは大体ゲームオーバーにする事が多いですね。
・47~49行:変数Cに移動先のBGコードを入れ、それが変数TKRだったら@GETへ、ゼロでも32(どちらも、何も描かれていない空白表示で、BGで表示する空白は32を使っています。DEF JIも参照してください)でもなかったら、障害物なので@GAMEOVERへジャンプしています。そのどちらでもなかったら、移動先のNXとNYの座標を、頭の座標AXとAYに代入し、WAIT 16つまり4分の1秒ぐらい待って(ここを変えると、ゲームの速度が変わります)、@LOOPへ戻ります。

KOZ02_S2.jpg

@GET 宝箱の取得、および面クリアしたかの判定
 BEEPの7番は「マリオ」でコインを取ったような音がするので、金や価値のあるものを取った時は、この音色が定番でしょう。58行はレベルの10倍に応じて得点を加え、@SCOREを呼び出しています。32行で代入したLV2は、画面に残っている宝箱の数で、取るたびに1つ引いています。引いた後もまだ1以上あるなら、GOTO @GET_RETで@LOOPの中に戻ります。LV2がゼロなら一面クリア。DEF JIでクリアメッセージを出し、2秒間待って、LVとBGM番号をどちらも1つ足しています。なおBGM番号のうち4~6番はブリッジ(半永久演奏でなく、短く演奏すると終わる)なので、42番以上は存在しないのでエラーになってしまうため、それぞれIF命令で7番と0番に変えています。そして@INIT2にジャンプすれば、面クリア後に必要な処理は、全て@INIT2でやります。

KOZ02_S3.jpg

KOZ02_S4.jpg

@GAMEOVER ゲームオーバー
 前回のスクロールスキーゲームと基本的に同じです。文字表示はPRINTでなく、DEF JIで行っています。67行ではSPCHRで頭のしかめっ面にしています。次のFOR I=1 TO 60…WAITは、60分1秒待ちながらを60回繰り返す、つまり「1秒間に60回処理をする」という事で、その間にRGB関数とRND関数で色のRGBをランダムにしている、つまり顔の色がチカチカと変わって、やられた演出を表現しています。71行では変数SC(今回のスコア)と変数HS(ハイスコア)を比較し、SCの方が大きかったら、HSにSCの内容を代入し、@SCOREを呼び出してスコア表示部を書き換えています。

KOZ02_P3.jpg

★ 定義済み処理 ★
 定義済み処理とは、共通した処理をどこからでも呼び出せる便利な処理で、大別して二種類あります。「GOSUB~RETURN」は古い時代から存在し、プチコンでも初代はこれだけでした。呼び出し元から定義済み処理へジャンプ時、変数を渡したり返したりする機能がなく、個別に「変数=値」を書く必要があります。新しい時代から登場した「DEF~END」は、プチコンでは3号から採用。変数を渡す事ができます。返すにはRETURNとOUTの二種類があるが、今回は渡しだけ使っています。
@SCORE 「DEF JI」を使って、スコアやハイスコアを表示
 注意して欲しいのは、JIでの文字表示用引数は文字列しか使えないので、数値変数はSTR$関数で文字列に変えている事です。例えばA$=STR$(255)と書くと、A$に文字列"255"が入ります。
@CHR12 ゲームに必要な画面を一端クリア
 CLSで文字、GCLSでグラフィック、BGCLRで4つあるBGの1番と3番を消し、DEF ATAMAも実行しています。
DEF SYMBOL L$ GPUTCHRを使って文字列を表示
 オープニング画面で、ゲームの説明を出す時、JIではアルファベットしか出せず、PRINTでは小さいので、GPUTCHRという命令で、文字をグラフィックで出し、また縦横とも2倍の大きさにしています。またどの色でもイマイチ見えにくいので、#WHITEで書く前に、座標を少しずらして#BLACKで書いて、影をつけています。
DEF JI LX,LY,L$ BGを使って文字列を表示
 実はスプライトもBGも、定義番号の32~95番は、文字表示用のアスキーコードの32~95番と同じになっています。このためアルファベットや数字なら、BGで大きい字を出せます。BGレイヤー番号は、一番手前の3番に出しています。またLXに-1を入れておくと、自動的に位置が中央に来るという親切設計にしています。
DEF ATAMA LX,LY スプライトを使って頭を表示
 座標が、グラフィックとスプライトは文字の8倍で400x240、BGは2分の1で25x15です。ここではスプライトによる頭を表示する時、ゲームで使っているBGにあわせた座標を、ススプライト用にあわせるため、引数のLXとLYを16倍して、SPOFSでスプライトを表示しています。頭を隠す時はLXかLYどっちかの座標を、-1とか画面の外に指定すればいい訳です。

KOZ02_P4.jpg

★ 最後に ★
 一つ課題?を。実は何度もゲームをやっていると、宝箱を全部取ったのに、面クリアしないバグが時々あります。どうしてそうなるのか? 改良方法は? 私は気づきましたが、わざと改良しませんでした(直せなかったんぢゃないよ!)これを読んでいて、ある程度プログラミングに自信のある方は、そこを見つけて、改良してみましょう!
(第三回につづく 次回は反射神経を使わず頭脳だけを使う、コマンド入力ゲームの予定です)

★今回の教材プログラムがうまく打ち込めなかった方のために、同じ教材プログラムを、サーバにアップしてあります。必要な方はサーバからダウンロードして、どこが間違っているかなどを、見比べてみて下さい。
 ご多聞にもれず、どんどんリストが長くなっているので、リスト全体を載せる事は、今回が最後になると思います。次回からは別ジャンルのミニゲームを何回か載せていく予定ですが、公開キーで一発ダウンロードのみになると思います。
公開キー  :ED743WAE(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
※次の更新は、12月20日。URAURA! GAME REVIEWの予定です。お楽しみに!