2019年08月19日

プチコンゲームプログラミング講座~第10回「サイモンゲームを作ろう」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 今回は訳あって(理由は文章の最後で!)アクションゲームでも思考ゲームでも自由に作れたのですが、第六回のように、以前と同じジャンルを再度作りたくないという考えがありました。で色々作りかけて、あれはプログラムが長い、これは締切まで完成できるか…と考えた結果、このゲームを思いついて、予想通り短時間で完成できました。

教材プログラム 10 「サイモン」(ファイル名「KOZA10」)
koz10_s1.jpg

★ ゲームの内容と説明 ★
 何かボタンを押すとゲーム開始で、4つの色のボタンが(以下「四色」と略す)、順番(以下「現回数」)に音を出してポーズをとります。左下に表示されている回数(以下「最大回数」)を全部終えると「今の順番を繰り返せ!」とメッセージが出るので、左の十字ボタンでも右の丸ボタンでも、さっきの順番と同じボタンを押してください。間違えず押せたらクリアです。最大回数は当初1回ですが、クリアするたび増えて、覚えるのがどんどん大変になります。一度でも間違えたらゲームオーバー。

Simon_game.jpg

 「サイモン」は、1978年にラルフ・ベアという発明家が作った携帯ゲームで(光線銃ゲームや家庭用ゲームも発明しています!)、いわゆるミニゲームですが、結構奥が深い上にプログラムが簡素にまとまる特徴があります(毎回文章の容量を規定内に削るのに苦労していましたが、今回は全文書いても少なめでした!)この連載では第二回で4方向移動ゲームをやりましたが、今回は四色の要素を配列変数に入れて、コンパクトにするなどの技術を勉強しましょう。

★ プログラムの事前説明 ★
 第五回で説明したテキスト以外の色コードについて、また短くする工夫を使っています。白を出すにはRGB関数か#WHITEだが、今回は3号のあるユーザが発見した「-1を指定すると#WHITEと同じ」を使っています。プチコンでは-1の二進数は、第五回の説明通り「10000000000000000000000000000001」となります。色指定の場合はこの左端1ビットをマイナス符号でなく、非常に大きな数と捉えるので、RGB(255,255,255)より大きいつまり#WHITEと認識します。前回の「!」同様、プログラム中で目立ちやすく、かつ短くて便利です。

koz10_p1.jpg

★ 初期設定 ★
 2行目のDIM OTO[99]は最大回数です。これも第五回で書いた通り、コンピュータと配列はゼロから始める(異世界生活ではないw)事が多いですが、人間からの見やすさを考え、1から始める(つまり[0]はダミーで使わない)事もあるので、今回はそうしています。[99]という事は最大添字が98なので、最大回数が98を超えるとエラーでゲームが止まりますが、そこまで行く超人は稀にしかいないと思い、あえてそのままです。なお配列の最大数を増やすにはPUSH、減らすにはPOPを使うので、興味ある方は調べてみて下さい。
 添字が[4]のDIMは全て、四色・四方向の定義です。オリジナルはアブストラクト(単なる記号)ですが、ここでは前回に続き、RPG向けの赤勇者・青魔女・緑僧侶・黄盗賊を使ってみました。サイモンでは画面を動くキャラこそ無いものの、4方向に配置したボタンがゲームの基本です。4方向の順番はプチコンの各要素でバラバラで、例えば十字ボタン=上下左右、丸ボタン=右下左上、文字キャラの人や車・スプライトの112~444=上右下左、464~1119=右下左上などとなっています(スプライトは一部違うものもあり)そこでここではFORとREADを使い、[0]~[3]に時計回りで上右下左の順番を入れています。これで四方向に関わる処理は、これら配列変数の添字を[0]~[3]で指定すれば、容易に使えます。順番が元々上右下左であるデータは、配列を使わずに掛け算で出しています。
 その後13~16行のGIで、左上のタイトルと中央のボタン操作を表示しています。

★ メイン処理 ★
@INIT リプレイ時の初期設定
 19行のGIは、リプレイ時の回数表示を消しています(@KAISUを参照)。20行目のDEF TENMETUは、四色全部を基本形のキャラに戻していますが、これは四色ともゲームオーバー時に通常と違う姿にしているためです(@OVER参照)21~22行はいつものスタートボタン待ちです。23行からはその後のスタート処理で、24行のDEF GI2でスタートメッセージの表示。21~22行で四色を同時に1秒だけ、ボタンを押した姿にしています(この処理については、DEF TENMETUも参照して下さい)

@LOOP 最大回数を一回増やしてプレイの繰り返し
 こうした処理は前回書いた通り、本当はGOTOを使わない構造化プログラミングが一番よいのですが、今回のゲームで構造化を使うと、むしろフラグ変数などを使って複雑になってしまうので、スパゲッティにならない程度のGOTOを使い、簡素にまとめてみました。
 変数K_ALLは最大回数で、19行でゼロにした後、@LOOPで32行を通るたびにINCで1ずつ増えていき、新たなOTO[K_ALL]に0~3の乱数を入れていきます。なおこの乱数が妙に偏ることが結構ありますが、これはプチコンやこのプログラムのバグでなく、プチコンの疑似乱数が0~3だと、たまたま偏りが出やすくなる様です。
 変数の再設定が出来たら繰り返しの開始です。33行のGI2で「じゅんばんを おぼえろ!」とメッセージを出し、34行のFOR…TO K_ALLで、最大回数K_ALL分だけTENNMETUを呼び出し、プレイヤーはこれを覚えます。36行はプレイヤーの繰り返し入力用の準備で、現回数を格納しておくK_NOWを1にします。

@LOOP2 ポタンが押されるまで待つループ
 39行で@KAISUを呼び出して、現回数と最大回数を表示します。実はこの位置に書くと、ボタンが押されるまで小刻みに@KAISUを実行するので、わずかながら無駄な負荷をかける非効率処理なのですが、文字表示のチラつきや処理落ちは起きず、これも処理が簡単という事で、そのままにりしてあります。
 40行のBL(BUTTON・LEFT)は左のボタンの1(上)+2(下)+4(左)+8(右)=15で、BR(BUTTON・RIGHT)は右のボタンの16(A)+32(B)+64(X)+128(Y)=240で、BUTTON関数とANDをとっています(こう書いている処理の理由も、第五回を読み返して解読してみて下さい)それを元に、左と右どちらかで四方向の一方向だけボタンを押したかが41~43行で該当すれば、-1だった変数V(40行で代入)に0~3の値を入れます。この処理は[0]から[3]まで4行並べてもいいのですが、今回は参考例として、FORループで3行にまとめた記述としました。
 Vが-1でない場合はOTO[K_NOW]と比較し、同じつまり正解なら44行で@ICCHIに、違うつまりミスなら45行で@OVERに飛びます。
 Vが-1のままならボタンを押さなかった事。WAITを60分の1秒だけ待って、Wにカウントを1加えます。47行で60で割った余りがゼロ、つまりWAIT 60こと一秒たったなら、プレイヤーに時間経過を知らせるため、早押しクイズのように一秒ごとに、BEEP 31で「タッ」と音を出して急かします。48行でWが600を超えたなら10秒オーバーなので、GIでタイムアウトを表示し、やはり@OVERに飛びます。それにも該当しなければ、49行で@LOOP2に戻って、入力待ちとカウントを繰り返します。

★ 定義済み処理 ★
@ICCHI 正しいボタンを押した時の処理
 TENMETUで正しい四色だけ白にします。この後WHILEとBUTTONでループをとっていますが、これは正解の後もボタンを押し続けた時、次の現回数が違う四方向だと、自動的にミスとしてゲームオーバーになってしまうので、指をボタンから離すまでわざと待つためです。また押したときのキャラの姿が、当初は中央のボタンを指差していましたが、つられて反対方向のボタンを押してしまうのでw外側に向けました。このキャラ番号も向きの関係で不連続になったので、計算でなく配列変数SS[i]に入れて、使うキャラ番号を求めています。
 53行で現回数と最大回数を比較し、最大回数が多かったらまだ残りがあるので、現回数を1増やしてWAIT 10で少し待ち、@LOOP2に飛ばして次の入力待ちです。

koz10_s3.jpg

 でなければ最大回数まで行ったのでノルマクリア。54行で「よくやった!」のメッセージを出し、55行でBGMPLAYによりファンファーレ。56~60行は四方向全部を躍らせるためです。第七回でやったSPANIMの記号の区別は覚えていますか? Cが色、XYが座標、Iがキャラ番号ですね。55行のA=96は四方向が踊っている時間で(つまり約1秒半)、Aから変数D・B・Cを求め、それをSPANIMで使っています。どの変数がどのSPANIMでどう使われているのか、一つ一つ計算してみましょう。61行のBEEP 71で「やったね!」の声を出して、また@LOOPに飛んでで最大回数1からやり直しです。
DEF TENMETU LS,LA 四色の1つの表示を切り替える
 まず「IF LA THEN」なら(この記述の意味は前回参照)、四色の中からLSだけ、-1で明るく表示して、@ICCHIで説明したSS[LS]で指差しポーズを出します。

koz10_s2.jpg

 ここで音を出しますが、BEEPよりBGMPLAYと文字列で自作メロディを出す、いわゆるMML(プチコンではmkIIから実装されています)の方が良い音になったので、そちらを採用しました。この講座ではMMLは扱ってきませんでしたが、実は現代式のMMLをよく知らないもので(^^;)、ここでは最低限の文法だけで作っています。「@+数字」はBEEP同様に音色番号で(これは国際規格です)、今回使っていませんが、演奏速度や音符の長さも指定できます。ドレミファのメロディは文字順でなく「CDEFGAB…」となるので、CHR$(67+LS)を使って四色の[0]を"C"/ド、[1]を"D"/レ、[2]を"E"/ミ、[3]を"G"/ファとしました。音が出終わるまで、WHILEとBGMCHKでループをかけて待っています。
 その後LAが1でも0でも67行を実行し、四色の暗さとキャラを元に戻しています。
DEF GI LX,LY,L$,LX2,LY2,LC1,LC2 GPUTCHRで背景に色が付いた文字を出す
 第九回と同じです。
DEF GI2 L$,LX2,LC1,LC2 LX=30・LY=26・LY2=2に固定してDEF GIを実行
 メッセージ表示のうち、右下はメインとして非常によく出すので、処理をまとめました。
DEF GI0 LX=30・LY=26・LY2=2に固定して文字を消す
 同じ場所で文字を消す処理です。GOSUBでもいいが、整合性を出すためDEFにしました。

@KAISU 左下に回数を表示
 DEF GIで現在に回数と最大回数を表示します。本来のサイモンには無いのですが、こうした表示は、数字がやや多めに出てくる処理ではよく見られるので、入れてみました。同時に「最大何回の繰り返しで何回目まで行ったか」というスコア表示の役目にもなっています。
 84行ではSTR$関数を使って、K_NOWとK_ALLを変数A$に入れており、これを86行のDEF GIで表示しています。ただし最大回数が増えると、現回数が小さい時に表示文字数の増減が大きくなり、以前表示したGIが右に残ってしまうので(こういうのをゴミと言います)、85行でそれより少し長い分スペースを表示して、必ず消す様にしています。

koz10_p2.jpg

@OVER ゲームオーバー処理
 90行で四色全部を驚いた顔にし、次の91行ではタイムオーバーでなければ、本来押す予定だった正しい色を、コケた姿にしています(前回のバトルゲームで倒れた時と同じ)92~92行でミスの表示をし、BEEP 122のサウンドが終わったら、ゲームオーバー表示以後はこれまでと同じです。

koz10_s4.jpg

★ お別れのご挨拶 ★
 さて、第0回も含めれば11回も続いたた当連載、実は今回で終了となります。
 同じ事をやっている松原拓也さんの「日経ソフトウェア」のプチコン講座を目指して頑張りましたが、同じゲームはやりにくいし、作ろうと思ってもなかなか上手く作れなかったり、こんな難しいゲームと解説でいいのか、もっと簡単にしようか…思い返すと、サンプルゲームのジャンルや内容があまりに偏り過ぎて、それはひどいものでした(三回やった予習・復習は「逃げ」でなく、必要なものとしてやりましたが…)三次元迷路とかトランプゲームとか、松原さんの連載とダブっても、私のプログラムでやるべきでしたね(^_^;)
 「プチコン4が出たら、連載形式で毎回仕様を実装していく、大きめのゲームが作れたらいいね」と、B-Magaさんと話していたのですが、私がプチコンを触れる時間が減ってしまったため、残念ながら終了させて頂く事にしました。決してB-Magaさんとの関係が悪化したのでなくwちゃんと円満終了です。
 皆さんに対してどれ程役立ったか判りませんが、今後ゲームを作る際、少しでも参考になれば幸いです。
 では、さようなら!

★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。
公開キー  :7P33E334
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
次の更新は、『URAURA! GAME REVIEW』の予定です。お楽しみに! 

2019年07月15日

プチコンゲームプログラミング講座~第九回「予習・復習3:条件判断とジャンプ命令」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 今回もサンプルゲームをお休みして、プログラム記述の話です。今回は、条件判断とジャンプ命令について説明していこうと思います。
 さてさて、プログラム処理の基本は一番簡単な回答なら

1.転送:値を別の所に動かし、出力装置に動かせば字や音を出す
2.比較:2つの数を比べてどっちが大きいか結果を出す
3.ジャンプ:プログラムの実行場所を他の場所に移す

 の三種類です。比較の結果ジャンプする事もあり、それがBASICで言うIF・THEN・GOTOです。使いやすくするため、FORやGOSUBなど微妙に改良を加えた命令も作られました。今回は内容の関係上、プチコン以外も一部紹介しています。

★ IFとTHENの基礎1:演算子 ★
 比較演算子について、昔の記述とプチコンの記述を並べてみましょう。

1907-ex1.jpg

 違いがあるのは、初代プチコンを出す時そのままでなく、後世に出た優れたプログラム技術も組み込んだためで、「==」「!=」はC言語の記述です。なぜ「両者が同じ」が「==」かというと、「A=B」では代入か比較か区別がつきにくいからです。
 また以内や以北など「以」が入るのは、以の直前の対象も含む(英語だと"equal"=イコール)」という意味です。「未満」「超える」は以が入らないので直前の対象は含まない、つまり英語だと"equal"の無いlessやmoreだけです。この辺をしっかり覚えておくと、プログラム以外でも厳密な区切りを書く時に役立ちます。
 それと演算子の無い「IF 変数 THEN…」もよく見ますね。これは「ゼロでない」つまり「IF 変数!=0 THEN…」と同じです。これまで時々出てきた「UNTIL BUTTON()」は「終了条件は、BUTTON()がゼロでない」つまりボタンが押されるまでという事です。

★ IFとTHENの基礎2:ELSEの登場 ★
 一番簡単かつ基礎となる条件判断文は「IF 比較内容 THEN 比較内容がYesだった時の処理…」で、BASICが生まれた時もこれだけでした。これではNoだった時の処理が書きにくいので、日本のホビーパソコンではPC-8001から「IF 比較内容 THEN Yesの処理… ELSE Noの処理…」が実装されました。これならYesとNoどちらも短い処理なら一行でまとまります。初代プチコンもELSEが計画されていましたが、命令を削減する事になり、LEFT$やRIGHT$と共に消えました。

★ IFとTHENの基礎3:ジャンプ命令  ★
 ELSEが出た所でジャンプ命令のおさらいです。普通にジャンプする時は「GOTO ジャンプ先」。では以下の違いは何だか答えられますか?
A.IF 条件判断 THEN GOTO ジャンプ先
B.IF 条件判断 THEN ジャンプ先
C.IF 条件判断 GOTO ジャンプ先
 答えは「どれも同じ」。元々Aだけでしたが、書きやすくするためBやCも認められました(三種類で処理速度に僅かな違いが出る言語もあります)どれを使うんだ?と迷う人もいますが、ここで目安を一つ紹介します。もし例1のような記述を書いたら、例2の方がスッキリしませんか?

1907-ex2.jpg

 スッキリした記述は見ただけでなく、修正しやすく、デバッグしやすく、書いた本人以外でも解析しやすい長所があります。こうした事を心掛けていると、後々で便利です。なお「ELSE GOTO ジャンプ先」に対して「ELSE ジャンプ先」も出来ますが、「IF…GOSUB ジャンプ先」は出来ません。ご注意!

★ IFとTHENの基礎4:ENDIFの登場 ★
 とは言え上記でも、THENの後に大量の内容が書けません。この問題の解決のため、「構造化プログラミング」が考えられました。これはFOR~NEXTやREPEAT~UNTILなど、二つの予約語で挟める命令をうまく配置し、スパゲッティ(GOTOであっちこっちに飛んでわかりにくくなるプログラムの事。中にはたった一行の処理で元に戻るものもある)を無くすというもので、IFに対し「ENDIF」が登場しました(言語によっては「END-IF」「END」「if … {…}」もあり)プチコンのENDIF追加はmkIIで検討されましたが、mkIIの処理では組み込めない事が判り、3号から登場しました。IFとENDIFで挟むとどれだけスッキリするかはここで書くより、皆さんがENDIFの例を探して、それを見る方がいいでしょう。

★ 多重分岐命令1:ON GOTO ★
 ある程度プログラムが組めると、どんな時にどんなIF命令を使ったらいいかが判るので、多少種類の多い判定をするとIF命令が増えてきます。IFが判ってきた初心者のリストを見ると
IF 変数==0 THEN…
IF 変数==1 THEN…
IF 変数==2 THEN…
 と並んで見にくいですね(^^;この原因はプログラムを書く人の能力だけでなく、言語自体にもあるため、一つの命令で複数の比較と実行先を並べる「多重分岐命令」が生まれました。プチコンでも初代から存在する「ON GOTO」「ON GOSUB」は、BASIC最古の多重分岐命令です。
 実際の使い方ですが、「ON A GOTO @ラベルA,@ラベルB,@ラベルC…」と書くと「変数Aが0なら@ラベルAに飛ぶ、1なら@ラベルBに飛ぶ、2なら@ラベルC…」という処理をするので、ツールやコマンド入力ゲームの様な、順に並んだ数字ごとに何をする?というプログラムに向いています。
 これだと変数が必ず0,1,2…と順になる場合しか使えませんが、「ON A+1 GOTO @ラベルA,@ラベルB…」と書けば「1なら@ラベルA、2なら@ラベルB、3なら…」、「ON A*2 GOTO @ラベルA,@ラベルB…」と書けば「0なら@ラベルA、2なら@ラベルB、4なら…」に変えられます。が「一つの変数が規則的に変わる時しか使えない」「記述が長すぎ」といった欠点はそのままです。

★ 多重分岐命令2:ラベルに文字列を使おう ★
 mkIIから追加された新機能で今回関係あるものに「ラベルに文字列が使える」があります。それのどこが便利か?「GOTO "@J"+STR$(A)」としたら、どうなると思いますか?「変数Aが0なら@J0に飛ぶ、1なら@J1に、2なら@J2…」となります。しかも記述がスッキリな上、GOTOやGOSUBのみならず、RESTOREやBGMSETDなどラベルを指定する全命令に使えます!
 しかし「命令分としては非常に小さくなるため、再度見つけにくく、バグが出るとデバッグしにくい」「ラベルにはアルファベットや数字が使えるが、符号が使えないので、変数から作ったラベルがマイナス符号になるとエラーになる」という欠点があります。またONもラベル文字列も、変数が大きくなりすぎて対応するラベルを作っていないと、エラーになります。

★ 多重分岐命令3:ELSEIFの応用 ★
 3号の途中からは、ELSEの変形バリエーションとして「ELSEIF」が追加されました。昔のプログラムは「とにかく短く詰めて、容量も速度も縮めろ」が美徳でしたので、「IF 条件1 THEN Yesの処理 ELSE IF 条件2 THEN Yesの処理」も「IF条件1THENYesの処理ELSEIF条件2THENYesの処理」と詰められ、ELSEとIFが合体してELSEIFとなったのです。ところがこれは以下のようにも使えます。既にこの連載で早くから出しており、以下は前回のプログラムです。

1907-k08.jpg

 最初の条件判断でYesならそれを処理し、Noなら次の条件判断でYesならそれを処理し、さらにNoならまた次の条件判断で…と、見た目も処理も上手いプログラムが組めます。最初のIFと条件の間のスペースを、1つでなく5つ開けると、下のELSEIIFと文字が並び、これまたスッキリしますね。こういう局面があったら、どんどん使いましょう!

★ 多重分岐命令4:多重分岐命令の完成 ★
 それでもまだ便利な多重分岐命令が足りず、一つの条件判断から複数処理を分岐させる命令が、いろんな言語で作られました。COBOLはEVALUATE、C言語はswitch(ゲーム機じゃないぞ)、VisualBASIC(VB)はSelect Caseと方言(機種やプログラム言語毎の違い)が大きいが、これはこの命令が比較的後世に作られたためです。プチコン4では新命令として「CASE~ENDCASE」が追加されました。例はプチコン4に載っているのですが、ここでも書いておくほか、よく似ているVBのSelect Caseも書いておきましょう。

1907-ex3.jpg

★ 評価関数 ★
 これは比較についての中級者テクニックです。関数というからにはRND()やDEF()と同じで、値が帰ってきます。どんな入出力か? 「変数==値」などの評価は今回多数書いてきましたが、ちょっと画面に「A=1:? (A==1)」と書いて、ENTERを押してみてください。1が出ましたね?では「A=2:? (A<3)」や「A=3:? (A==0)」はどうだったでしょう? それぞれ1と0が出たと思います。

1907-k09_0.jpg

 つまり条件判断の両側をカッコでくくると、条件がYesなら1(古い機種は-1もあり)・Noなら0を返します。これを上手く使うと、IFが無くても処理を変えることができます。「1しか出ないんじゃ、もっと大きな数はどうするのか」って? 次のプログラム「KOZA09-1」を実行してみましょう。数をFORで変化させて、5の時だけ大きな数を加えています。左の表示はIF、右は評価関数と掛け算を使っており、右はこんなにスッキリします。前回の数値バトルゲームの「S=(A<B)」は、これで先攻と後攻を決めています。

1907-k09_1.jpg

 もう一つの応用がプログラム「KOZA09-2」です。左の十字ボタンで赤いキャラが、右の丸ボタンで青いキャラが動きます(ボタンを複数同時に押すと動きません!本当はそこもクリア出来るが、記述が面倒になるのでこうしました)右は第五回で紹介したプログラム「K05_SPC」と同じですね。これを使うと第四回や第六回のようなアクションゲームは、動きの処理がよりスッキリします。

1907-k09_2.jpg

1907-k05spc.jpg

 ただこの裏技、KOZA09-2のようなキャラ移動はそうでもありませんが、KOZA09-1のような一部の数値を変える処理に使うと、前述の「ラベル+文字列」のようにリストに埋もれてしまい、目立たなくなるので、バグが見つけにくくなります。それに気を付けて使いましょう。評価関数まで普通に使える様になったら、上級者に手が届いた証拠です。

★ 条件が来るまで抜けない!1:WHILEとREPEAT ★
 次は3号以降の新命令。日本のホビーパソコンでは、PC-8001の後続機であるPC-8801から採用されました。どちらもFORのように一定回数繰り返すのでなく、条件を満たすまで繰り替えします。両者の違いは以下の通り。

・WHILE~WEND=繰り返し処理の最初で「条件がYesなら繰り返す」判定をする。最初に繰り返さない条件(No)が来ると、繰り返し処理を一度もせず抜ける。
・REPEAT~UNTIL=繰り返し処理の最後で「条件がNoなら繰り返す」判定をする。最初に繰り返さない条件(Yes)が来ると、繰り返し処理を最低一度して抜ける。

 この違いを並べてみたのが、サンプルプログラムKOZA09-3です。数の変化と条件判断だけなので、プログラムの説明は無し(え?)なぜこの4つの処理でこう違いが出るのか、上の説明をよく読んで考えてみましょう。

1907-k09_31.jpg

1907-k09_32.jpg

 何か複雑そうですが、実際の処理はREPEATがFORとよく似ているので、最初はREPEATだけ使い、WHILEの方がいいと把握できる様になったら、WHILEを使いましょう。
 前述のVBには、よく似たDo~Loop命令があります。終了条件の記述方法がWHILEやREPEATより判りやすいので、これも後学のために図でお見せします。

1907-ex4.jpg

★ 条件が来るまで抜けない!2:LOOP ★
 プチコン4では「LOOP~ENDLOOP」も追加されました。ではこの命令はどうやって抜け出すかというと…WHILEやUNTILに相当する抜け出し命令がありません!代わりに3号から「BREAK」命令が追加されており、これはFOR,WHILE,REPAETなどのループを強制的に抜ける命令なので、LOOP~ENDLOOPもこのBREAKで抜けます。

★ 条件が来るまで抜けない!3:FORを使う ★
 実は初期のBASIC命令を使った裏技で同じ事が出来ます。FOR~NEXTは規則的に数が変化していきますが、「FOR I=0 TO 1 STEP 0」とすると、STEPがゼロなのでずっと終わらず無限ループになります。この中でIFや評価関数を使いIを1にすると、「TO 1」の条件を満たすのでループを抜けます。条件つき終了命令の代用として、一画面プログラムなどで使われました。例としてhonoPさんが初代プチコン公式サイトに投稿した「Petit Ski」を見てみましょう→ https://smileboom.com/special/petitcom/pochette-petitski.html

★ その他の条件判断1:MZ-80の時代 ★
 他の機種から、上記のどれにも入らない例を少し紹介。PC-8001の名を何度か出しましたが、共にホビーマイコンの代表格だったMZ-80K/Cの初期は、条件判断をつなぐANDやORがありません!こんな書き方をしていました。
・ANDの例:「IF(A=1)*(B=2)THEN…」
・OR の例:「IF(A=1)+(B=2)THEN…」
 カッコがあるから評価関数ですね。上は*が掛け算つまり両方の評価関数が1の時だけ1を返し、下は+が足し算つまり片方でも1になれば1以上を返します。「IF 数値 THEN」は「IF 数値!=0 THEN」と説明した通りで、これによって変数AとBそれぞれがYesかによって、ANDやORと同じ処理が出来るのです。コンピュータの奥深くでは、こうした方法で計算しています。

★ その他の条件判断2:三項演算子 ★ 
 最後にCからまた一つ。CにはBASICにはない、短く便利な命令が沢山あり、X1やプチコン3号など、Cの処理を部分的に採用したBASICも存在します。三項演算子はIFと同じ判定をそのまま関数で使えるもので

a = (条件 ? Yesの値 : Noの値)

と書くと、条件によってYesかNoの値が左端の変数aに入ります。これもプチコンに欲しかったなぁ…

(第10回につづく 次回はまたサンプルゲーム。内容は未定です)

★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。
公開キー  :2KL37KAE(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
※次の更新は、フリーゲームレビューの予定です。お楽しみに!

2019年06月17日

プチコンゲームプログラミング講座~第八回「数値バトルゲームを作ろう」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 今回のプログラム作成中にプチコン4が出ました。今回はまだ3号だけですが、もう少しお付き合い下さいw
 今回はコマンド入力ゲームです。メニューやメッセージは第三回でやったので、今回はRPGのキモである、素早さや力などの能力値を組み合わせた、戦闘部分を作りましょう。思い出したのが「週刊少年ジャンプ」の読者コーナーでやった「JBSクエスト」です。これは100の数値を早さ・力・身の守り・体力(HP)に分配したキャラを読者が投稿し、編集部が作ったプログラムで戦わせるもので、その方法を頂きました。実際の配分は人による操作と自動どちらでも可能にしました。
教材プログラム 8 「数値バトルゲーム」(ファイル名「KOZA08」)

koz08_s1.jpg

★ ゲームの内容と説明 ★
1.まず左の青いキャラクター(以下「左」)の名前を下画面で聞いてくるので、入力して「決定」を押して下さい。
2.次にキャラの能力値の決め方を聞いてきます。自分で決めるなら「はい」を押して下さい。自動で決める「いいえ」だと5番まで飛びます。
3.外見を聞いてくるので、十字ボタンの左右で選び、Aボタンで決定して下さい。
4.次に能力値の振り分けです。十字ボタンの上下で4つの能力値を選び(3で選んだキャラがカーソル替わり)、左右で数を±してAボタンで決定して下さい。残りがゼロでなく残っていても、Aボタンが作動します。
5.右の赤いキャラクター(以下「右」)も、2~4を繰り返します。
6.左右のキャラクターが現れ、一方が勝つまでオートバトルを続けます。

★ プログラムの事前説明 ★
 今回の様に二者が交互に攻めるゲームは、「ドラクエ1」「ポケモン」の他、オセロや野球もそうですね。初心者だとどうしても、自分側と敵側でそれぞれ処理を作ってしまいますが、それでは似た処理を二つ書くので無駄が増えます。今回は以下の方法でコンパクトにまとめました。
 まず名前や能力値を配列変数で定義し、左を添字[0]、右を[1]にします。攻撃する側(「攻撃側」と呼ぶ)の配列添字(今回もSを使っています)が例えば0だとすると、攻撃を受ける側(「防御側」と呼びます)あるいは次に攻撃側となる相手は、「S=1-S」とすれば、0と1が逆転します(実際に脳内で暗算してみて下さい)実はこれ「!S」と書けばいいのです!(「!」は数学的には否定の意味があります。AとBが同じでないは「A!=B」でしたね)これなら見えやすいので、相手のS番号をいちいち別の変数に代入しなくても、そのまま[!S]と書いて平気ですね。
 キャラの姿は第三回でも使った、30種類のRPG調スプライトです。配置方法は、簡単なゲームだとGAME2みたいに正面姿だけですが、折角RPGも作りやすいぐらいポーズの多い3号なので、「FF」等でおなじみ左右お見合いにしました。

★ 初期設定 ★
 1行のCHR$(10)は制御文字と言って、文字を出すのでなく、文字以外の他の操作をする文字コードで(例えば画面クリアはプチコンではCLSですが、昔のマイコンはCHR$(12)を使っていました)、19行で使います。2行では左右に使う配列を定義。
・S_NAME$[2]=名前
・S_CHR[2]=キャラの姿
・SK_ILL[2,5]=左右の能力値
・SK_ILL$[5]=能力値名
 能力値名の最後「のこり」は、厳密には能力値でなく、後述の振り分けに使いますが、一緒にした方が便利なのでこうしました。4行で左右のスプライト、5行でグラフィックとBG背景を定義しています。

@INIT ゲームのタイトル画面
 これまで大きな文字を表示する時は、BGかGPUTCHRを使いましたが、今回は汎用性のあるDEF GIを作りました(機能は後述)。11~12行のゲーム開始ボタン処理はこれまでと同じですが、上記の通りBG文字を使わないので、代わりにBG背景をフラッシュさせています。
 13~72行にFOR~NEXTという処理がありますが、これでS番号の0と1つまり左右を並べて処理しています。この記述は他の場所でも出てきます。

koz08_p1.jpg

★14~18行 名前の入力 ★
 入力は古い高級言語だとINPUT命令がありますが、規定に合わない入力(数を入力するのに文字を入れたなど)をするとレイアウトが乱れるなどの欠点があります。今回は時間がなかったのでDIALOGを使いました。DIALOGはVisualBASICのMsgBoxによく似た命令で、3DSの下画面(BIGではTV画面)に、プチコンの文字より綺麗な字とレイアウトでメッセージを出します。さらに命令でなく関数として使うと文字列入力が出来、字数制限も簡単です(今回はリストの通り8文字まで。なぜ8文字なのかは第五回参照)ところが文字を入力せず右の「戻る」を押すと、文字数ゼロで確定してしまうので、その時は17行で「ちゃんと入れろ!」とエラーを出して、18行のUNTILで再入力させています。
 またDIALOGはアルファベットと数字しか入力できないのが欠点で(DIALOGによる入力文字を、ファイル名に使う為だと思われます)、これが不満だと思う方は、INPUTで作り直してみて下さい。
 次に19行でまたDIALOGを使い、各種設定をプレイヤーが決めるか、CPUが自動で決めるかを選びます。「はい」を選ぶと、DIALOGが返す値は-1ではないので、29行にジャンプします。

koz08_s2.jpg

★20~27行 CPUによる使用キャラ&能力値決定 ★
 まずSK_ILL[S,I]の4項目全てにゼロが出ない様、1を入れておきます。残りの96は変数Aに入れておきます。先のSK_ILL[S,4]は、ここでは使っていません。
 能力値の割り振りは、4項目全部一つずつ乱数で入れると平均化しそうなので、23行のRND(10)+1で変数Bに1~10を代入し、Bを4項目のどれかに入れています。これを25行のUNTILによって「残った変数Aが10未満になるまで」続けますが、ここで数の大小が矛盾を起こさない様、23行では「UNTIL A>B」を入れています。
 27行は前述のスプライトキャラの先頭番号をS_CHR[S]に代入しています。演出によって姿を変える時は、このS_CHR[S]に一定の数を加えます。

★29~44行 プレイヤー操作による使用キャラ決定 ★
 ここからはプレイヤーが設定する処理です。29行のDEF COL8は、左右で青と赤それぞれの色を先に代入しておきます。
 34行で最初に496番の姿を出して、SPOFSで表示しています。37~42行は第三回のメニュー選択部分と似ていて、十字ボタンの左右を操作するたび、管理番号を±20ずらして次の姿を出し、番号がその範囲を超えたら、反対側の番号に戻しています。格ゲーなど最近のゲームでキャラを選ぶシステムと同じですね。Aボタンを押すと、42~43行でそのキャラの姿に決定しますが、WAIT 15をかけているので、パッと押しただけでは反応しないかも知れません。少し長めに押して下さい。

koz08_s3.jpg

★46~70行 プレイヤー操作による能力値決定 ★
 47~49行で操作表示を書いています。50行から、SK_ILL[S,4]だけ96を入れて残りは1から始まるのは、20行と同じです。54行の変数Yは、カーソルと能力の位置。カーソルは前述通りマイキャラを使うが、6倍では大きいのでSPSCALEで2倍にしています。
 55~68行が十字ボタンを使った割り振り。コンシューマーゲームでは「ウィザードリィ」のキャラ作成で有名ですね。57~60行が上下ボタンによるカーソル移動で、カーソルは動きの有無に関わらず68行で表示。57~60行が左右ボタンによる増減です。ANDの右は「それ以上そっちの方向に動かすと限界なので、もう動かせない様にするか」の判定です。66行では現在割り振った能力値を出しているが、@STATUSと同じレイアウトが使えないので、ここだけ違う配置で表示しています。これを68行でAボタンが押されるまで行います。ELSEIFは次回説明します。

★74~82行 バトル開始前準備 ★
 スプライトを、75行でS_CHR[S]を基準に左右それぞれ相手を向く方向にし、76行で4段階の足踏みをさせ、77行で画面の外から中に動かしています。78行のGIは上部の名前表示です。
 左右の表示を終えたら、80行のWAKUでメッセージ枠を出し、81行で左右の早さSK_ILL[S,0]で乱数を作り、高い値が出た方から82行で「先手を取った」と表示します。81行の「S=(A<B)」という処理は次回説明します。

★84~90行 攻撃開始して当たったか外れたか ★
 左右の能力値を掛け合わせて、攻撃が成功か失敗か出すわけですが、掛け合わせ方にも何種類かあります。「能力値でサイコロを振る計算をする(能力値が16ならRND(8)x2回とか」は処理が複雑なので使わず、今回は攻撃側の攻撃力だけ「能力値をそのまま使う」、他は「能力値で乱数を作る」としました。
 SPANIM命令を使う時、S番号に対して「S-.5」という計算が何か所かで出てきます(プログラム言語では少数点の左のゼロを略していいので、「.5」は「0.5」の意味)。この計算をすると左がS番号0なので「-0.5」、右が「+0.5」と、ゼロに対し左右対称な値となります。攻撃や防御の時、これと「SPANIM…"XY+"」(この記述は前回勉強しましたね)を使って、左右どちらも向かい合ったり遠ざかったりの動きに使っています。
 84~86行は攻撃判定の準備です。86行のSPANIMで攻撃側のスプライトが動きますが、その前に85行のDEF MESでWAIT 64がかかっているので(後述のDEF MES参照)、約1秒たってからスプライトが動きます。
 87~91行で命中判定です。攻撃側と防御側の早さ(SK_ILL[S,0])で乱数を作り、防御側が大きかったら、89~90行で「かわした動き」を行います。89行は前述の「S-.5」で、攻撃側が迫ると同時に防御側をバックさせています。

koz08_s4.jpg

★92~105行 命中したのでダメージが出たか ★
 攻撃側の力(SK_ILL[S,1])から、防御側の身の守り(SK_ILL[!S,1])で乱数を作ったものを引き、代入先の変数Aがダメージです。ゼロ以下だったら敵が硬くてノーダメージですが、これだと双方の力と身の守りが微妙な場合、いつまでも決着がつきません。そこでこの場合94行でA=RND(2)すなわち0か1にし、「ドラクエ」の様に時々ダメージ1を出しています。こうすると最悪でも相手の体力をジワジワ消耗する上、体力が一桁という微妙な状況で死闘も発生します。
 ダメージ1以上なら95~101行でダメージ有りの処理。95行で防御側の体力(SK_ILL[!S,3])からAを引きます。また体力表示がマイナスだとおかしいので、96行でSK_ILL[!S,3]がゼロ未満になったら、ゼロに変えています。GOSUB @STATUSで能力値を再表示し、SPANIMは前回も説明した「"C"で透明と#WHITEを交互表示」、つまり初期のドラクエのように防御側が点滅します。
 ダメージ1未満なら103~104行でダメージ無しの処理。防御側はSPANIMで軽く上にジャンプさせました。最後に107行で「S=!S」として、攻守を入れ替えています。もし攻撃側が二回攻められるルールがあるなら、この処理をしなければ、同じ側が繰り返し攻撃側となります。

koz08_p2.jpg

★109~115行 バトル終了 ★
 82行で変数WNに2を入れ、83行のREPEATからバトルを開始します。終了条件を入れる位置は「バトル中に防御側が負けた」か「最後のUNTIL」どちらかですが、今回は終了条件が前者しか無いので、101行で防御側の体力が1未満になったら、WNに攻撃側のS番号を入れ、108行のUNTILで「WNが0か1になった」らバトル終了と判断しています。
 109~113行が決着の演出です。109行のBGMPLAYは4と5番、110行のBEEPは88と89番で音の雰囲気が似ていたので、左右どちらが勝ったかで変数WNつまり0か1をプラスして、左右でサウンドを変えています。113行のSPCHRでも同じことをしていますが、これはS番号が!WNつまり負けた側です。S番号がWNが勝った側ですね。もう一度プレイするかの選択画面も、前述のDIALOGを使っています。

koz08_s5.jpg

★ 定義済み処理 ★
@STATUS 両者の能力値を表示
 S番号の変数がSでなくS2なのは、@STATUSを呼ぶ前にSを使っているので、ここで同じ変数Sを使うと、処理がおかしくなる論理バグを起こすからです。FORMAT$関数については第五回でも書きましたが、書式が複雑なのでここでは略します。
DEF WAKU LX1,LY1,LX2,LY2 枠線を描く
 第三回の流用ですが、バトル開始前の一回しか使っていません。第三回では青でしたが、左の色とまぎらわしいので紺色にしました。

@MCLS 枠線内の消去
 枠線内の4行を全部消し、新たな表示に備えます。この4行はファミコン時代のRPGなどを多数調べて決めました。

DEF MES L$,LB,LW 枠線内に文章を表示
 WAKUで描いた枠線に、L$のメッセージを出します。短い文章で右端にはみ出さない事が判ったので、長さチェックはせず、また表示文章の行数高さを扱う変数WYに+1して、次に表示する文章の位置処理の無駄を省きました。この処理と同時に、サウンドやウェイトを使う事が多いので、同時に処理できるようにしました。LBは0以上だとその音色のBEEPを出します。LWは1以上だとそのFPSのWAITで待ちます。

DEF COL8(LC) 代入された0~8に応じたRGB関数の値を返す
 第五回の流用。これで左のS番号0はCOL8(1)つまり青、右のS番号1はCOL8(2)つまり赤を出します。

DEF GI LX,LY,L$,LX2,LY2,LC1,LC2 GPUTCHRで最後に色が付いた文字を出す
 テキストとBG以外で大きな字を手っ取り早く出すにはGPUTCHRですが、座標が小さなグラフィック単位だったり、目立たせるために背後に色を付けておくのが面倒なので、これを作りました。
・LX,LY - 字を表示する座標の左上ですが、値はグラフィック単位でなくテキスト単位つまり横50x縦30です。後述の文字拡大を使っても、ここの座標は1倍角基準です。
・L$ - 表示する字
・LX2,LY2 - GPUTCHRで拡大する大きさ
・LC1 - 文字自体の色
・LC2 - 文字の背景色

@ALLCLS ゲームに必要な画面を一端クリア
 これも以前と同じだが、毎回ゲームの内容に応じて、リセットする要素や方法を変えています。
(第九回につづく 次回はまたまた「予習・復習3:条件判断とジャンプ命令」です)

★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。
公開キー  :BP54Q33J(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
※次の更新は、レトロゲーレビューの予定です。お楽しみに!

2019年05月16日

プチコンゲームプログラミング講座~第七回「予習・復習2:スプライトとその周辺」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 今回もサンプルゲームを休んで、命令文の説明のため、プログラムは小さなものだけです。条件判断とジャンプ命令を先に書きたかったのですが、スプライト関連の解説が増えてきたので、こちらを先にやります。またスプライトと似て非なる存在にBGがあるが、この機会に同時に覚えられる所は同時に覚え、覚える負担を軽減しましょう。

★ グラフィックページ(GRP)の基本 ★
 スプライトやBGの設定はmkIIと大幅に変わり、GRPを使う方法になりました。スプライトを基礎から時系列で解説するには、GRPの説明が欠かせません。GPPはGRP0~GRP5とGRPFの7つがあります。大きさや編集の具合はどれも同じだが、一部のGRPはどこで表示するか、何に使うか、プチコンを起動したりACLSを実行した時点(初期化ですね。「プリセット」と呼ぶ)で決まっています。ここで説明しておく命令はGPAGE命令ですが、通常は「GPAGE 0,今編集しているGRP番号」でいいでしょう。
 プリセット表示はやはりGRP0です。GRP1~3は未使用・未表示だが、XSCREEN命令で上下を別画面として使うと(パラメータは2と3)、下画面はキーボードでなくGRP1が表示されるので、控えのGPRデータを入れる時はGPR2や3を使いましょう。GRP4がスプライト、GRP5番がBGに割り当てられています(割り当て変更はSPPAGEとBGPAGE命令)これはお絵かきツール(入力画面から「SMILE」→右上の「お絵かき」)でも「G0 G1 SP BG」と書かれているので判るでしょう。GRPFは文字用の隠しGRPで、表示文字を変えている凝ったゲームはGRPFを使っています。なお複数GRPの重ね表示は、残念ながら出来ません!

★ 自作の絵をスプライトにしよう1:ペイントツール ★
 プチコン初心者を見ると、自分で描いた絵をゲームで動かしたい希望が強く、方法についての質問や、実際に使ってみたゲームもよく見かけます。これまで全然書いてこなかったたが、このタイミングなので、ここで方法を記します。まず前述のお絵かきツールを起動します。操作方法は動かせば判ると思うので、他の注意点を並べます。
1.黒と透明の区別(第五回も参照)がつかない事に注意。間違えると描きあがったキャラをスプライトで出した時、変な所が黒くなっていたりします。下で説明しているPAINT(塗りつぶし)をうまく使いましょう。
2.大きさは第五回の通り8の倍数ドットにすると、プリセットのキャラと合わせ易くなります。
3.PAINTの後、ペンモードに戻さずにタッチすると、余計な所まで塗っちゃいます。画面下部の「UNDO」(アンドゥ)をタッチすれば大丈夫ですが、PAINT使用後はすぐPAINT以外のモードに変える癖をつけましょう。
4.いきなり描く前に、緑や水色などわかりやすい色で、描くスペースだけ四角で囲っておき、最後に透明色でPAINTすると便利です。

k07_1s2.jpg

 完成したら右下の「SAVE」をタッチして、ファイル名を付けて保存しましょう。なおここでは3号プリセットのツールで説明していますが、ユーザの皆さんが使いやすいペイントツールを発表しており、ツールが違っても保存するGRPデータはほとんど同じなので、もっと使いやすいツールで描いて大丈夫です。またGRPの保存範囲は画面全体つまり512x512で、一部範囲で保存できません。このため一部のユーザが公開している「サーバ保存を使わなくても通信機能だけでファイルの送受信ができる」ツールでGRPを送る事は難しいので、注意しましょう。プログラムとGRPは原則として、同じプロジェクトに入っていないと読み込みにくいので注意! 違っていたら、トップメニューで作業用のプロジェクトを変更するか、コピー機能で同じプロジェクトに入れましょう。

★ 自作の絵をスプライトにしよう2:SPDEF命令 ★
 で講座のために私もツールを使って、キャラを描いてみました。これもファイル名「GRP:KOZA7」で、今回のプロジェクトに入っています。普通にプログラムを読むのはLOADですが、GRPを読む場合は「LOAD "GRP【番号】:ファイル名」です。試しにキーボードから「LOAD "GRP:KOZA7」と打ち込むと、確認ボタンを押す様求められた後、画面に出ますね。確認ボタンが面倒な人は、オプションパラメータに確認ボタンを出さない0を付けた「LOAD…KOZA7",0」でやってみましょう。
k07_1s3.jpg

k07_1s4.jpg

 では読み込んだ後、「SMILE」→画面下の「SPDEF」で、SPDEF情報を見てみましょう。恐らく皆さんはGRPの左上に描いたと思いますが、番号が小さいスプライトは、皆さんの描いた絵が分割されてグチャグチャになっていると思います。これは別に読込が失敗したのでなく成功しているが、SPDEF情報がプリセットで設定されているからです。自分が使いたいサイズや向きに再設定すれば大丈夫です。SPDEFの記述も何種類かありますが、ここでは基本的なものを書きましょう。OUTの使い方は、これまでの連載を見れば判ると思うのでパス。

SPDEF スプライト定義番号,X座標1,Y座標1[,X座標2,Y座標2[,X座標3,Y座標3]][,アトリビュート]

 ここで角カッコ『[]』を説明しておきましょう。数式では通常のカッコ『[()』の中と外に使いますが、ここでの使用はプログラム文法そのものでなく、プログラムを説明する時の選択種別です。『[]』は「この中は書く必要は無かったら書かなくていい。書かないと一定の処理になる」。これを英語のDefaultを日本語風にちぢめて「デフォルト」「デフォ」なんて言います。また大カッコ『{}』というのもあり、「ここで説明している内のどれか一つ」の意味です。スプライト定義番号はもう判りますね。 四角形の範囲を指定するため、座標を四つ書く命令の場合、
A.四角形の四隅のうち二ヶ所を指定(●●FILLなど)
B.前二つの座標が左上位置、後二つの座標が横幅と縦高さ(SPCOLなど)
があるが、SPDEFはBです。X座標2とY座標2はデフォルトつまり略すと16x16になります。X座標とY座標3は配置・拡大・回転などの基本位置で、初心者はまだ覚えなくて大丈夫です。アトリピュートは定義するGRPの情報を、SPCOLのようにビット単位で指定します。デフォルトだとそのままの向きだが、うまく設定すると回転や逆方向が作れるので(六回目に少し書きましたね)ゲームで4方向に動くキャラを作る時も、いちいち全部作る必要がありません。という事でサンプルプログラムです。保存しておいたGRPファイルをLOAD→SPDEF→SPSETと処理しています。ゲームでの動かし方などは沢山やってきたので、ここでは表示だけして終わりです。

k07_1p.jpg

k07_1s5.jpg
(ファイル名「KOZA07-1」)

★ GPR5とBGの設定 ★
 SPDEFと関係ないが、順番の関係上BGもここに書きます。スプライトと違ってBGは全て同じ大きさなので、BGDEFのような命令はなく、ただGRPに描くだけで、何番のBGがどういう模様になるか自動的に決まりますし、位置とBG番号の関係も掛け算と割り算で出せます。ただし左上つまりBGキャラの0番は、何を描いても必ず空白「 」になるので注意! BGSCREEN命令を使うと、大きさを8ドットや32ドットに変えられます。

★ SPANIM命令とBGANIM命令 ★
 スプライトをじわじわ動かす「補間移動」のパラメータはmkIIにもあったが、SPOFSやSPANGLE(3号のSPROT)など一部の命令に存在していました。が3号では「SPANIMとBGANIMは指定した状態を連続して変える、その他の命令はその瞬間一回しか変えない」で統一されました。これは前述通り共通性があるので、ちょっと並べてみました。丸暗記の必要は無いが、覚えておくと便利な部分が多いので、可能な部分でも覚えてみましょう。「VAR」は聞きなれない機能だが、スプライトやBGも番号別に値を保存しておける機能です。ただし普通に変数を設定する方が短く書けるので、覚えなくても大丈夫です。またここでの全命令はOUTが使えるので、それも頭にとどめておくと楽です。

ex1.jpg

 単独変化命令を実行すると、変化が実行中だろうが無限に続いていようが、指定した変化は強制的に単独変化で止まります。例えば「SPANIM "I"…0:SPANIM "C"…0」を実行後、SPCOLORだけ実行すると、色の変化は止まるが、キャラは変化し続けます。
 ではパラメータの説明です。SPANIMは便利になった反面文法も難しくなり、私も3号を買った当初は判りませんでした。ヘルプを見ないで書けたらもう中級者でしょう。パラメータは4つに大別され、「1→2→3[→場合によっては2→3を何度か繰り返し]→最後に4」という順番は変わりません。

1.変更内容 - 数字と文字両方あるが、文字の方がわかり易いでしょう。そのままだと絶対指定だが、数字は「8」を、文字は最後に「+」を加えると相対指定になります。
2.fps - 時間ですね。
3.変更値 - 2の時間と共に変更する値。「座標とVARは二つ書く」「その他は一つ書く」と覚えましょう。
4.繰り返し回数 - 一番最後に来ます。詳細は三種類。
・0→無限に繰り返す。ゲーム中ずっと動き続けるキャラに使用。
・1またはデフォルト→一回だけ変化する。登場直後ややられた時に使用。
・2以上→書いた回数だけ変化する。1と同じだが同じ表示を決まった回数見せる時。
 パラメータを配列やDATAに書いて実行する事も可能です。ここでは説明しないが文法は同じなので、覚えたい人はまとめて覚えましょう。

★ サンプルプログラム1 絶対指定と相対指定 ★
 絶対と相対は、文字より見た目で判る典型的なケースなので、ここでサンプルプログラムに行きましょう。実行するとAとBは、表示位置以外は当初同じ様に思えますが、変数Wで指定した時間がたつと、プログラムではどちらも同じ数字を書いているようなのに、表示がずれます。Aの絶対指定は「変えるべき値は、現在の状況がどうだろうがゼロ基準からまた変える」、つまり最初に「座標100,50で角度45度」の後、「200,50」「90」の指定は「一から仕切り直して座標200,50で角度90度」です。Bの相対指定は「変えるべき値は、現在の状況から加えて変える」、つまり最初に「座標100,100で角度45度」の後、「200,100」「90」の指定は「それに加えて座標300,150で角度135度」です。
k07_2p.jpg

k07_2s2.jpg

k07_2s3.jpg
(ファイル名「KOZA07-2」)

 ではこの変数Wを変えてみましょう。数が小さいと短く、大きいと長い間隔に変わります。では「-120」とかマイナス符号に変えたらどうなると思います? いきなりでなく、ジワジワ動きましたね。第五回では「3号の数は32ビット保存で、左1ビットを±の符号、右31ビットを実際の数として扱っている」と説明しましたが、●●ANIMもそれと同じで、プラス(左1ビットが0)ならその時間待っていきなり移動、マイナス(1)なら補間移動となります。
 それと二つ注意点。相対指定と前述した繰り返しパラメータを組み合わせれば、そのままずーっと変わり続ける("XY"なら画面端に見えなくなるまで移動)ようだが、繰り返しは●●ANIMに書かれた最初のパラメータから繰り返します。つまり千本ノックやキャッチボールやみたいになるので悪しからず。
 またこのサンプルプログラムでは、SPANIMを判り易く見せるため、最初の設定をSPOFSやSPROTで行っているが、これも凄く短時間にやってしまえば、SPANIMだけで大丈夫です。ただし時間指定にゼロは使えないので、例えば「SPOFS…座標1:SPANIM…"XY",時間,座標2」を略す時、「SPANIM…"XY",0,座標1,時間,座標2」だとエラーになります。1fps入れて「SPANIM…"XY",1,座標1,時間,座標2」にしましょう。1fpsは素早いので全く見えませんw

★ サンプルプログラム2 文字の点滅 ★
 もう一つサンプルプログラムをやりましょう。ゲームではなくいろいろな所で見ますが、文字や絵が点滅しているのは、見えやすくて便利ですね。そこでSPANIMで字が点滅するプログラムを作りました。違いが判りますか? KOZA07-2と同様に、変数Wを変えてみましょう。この講座では実際に動画を貼らないので、わざと補間変化中に修正した画像を出していますが、それぞれの点滅方法の違いが判ると思います。

k07_3p.jpg

k07_3s2.jpg

k07_3s3.jpg
(ファイル名「KOZA07-3」)

・A=座標:今の座標と、左上の画面外の位置を繰り返し
・B=文字:今の文字と、32番のスペース「 」を繰り返し
・C=倍率:今の等倍と、縮小倍率ゼロを繰り返し
・D=色1:今の白(RGB(255,255,255))と、黒(RGB(0,0,0))を繰り返し
・E=色2:今の白(RGB(255,255,255,255))、透明(RGB(0,n,n,n))を繰り返し
 これだけでも各機能がどういう変化をするか、判ったと思います。点滅は他にも、キャラや文字の上に背景と同じ色をかぶせて、消す・出すを繰り返すなど、様々な方法があります。

★ その他のSP●●●命令とBG●●●命令 ★
 繰り返しますがスプライトとBGは似た命令が多数あり、一緒に覚えれば習得が楽です。反面一方にしか無い命令もあるので、バージョン3.6.3の時点で、それぞれに該当する命令を書き出してみました。
・スプライトのみ - CHR,DEF,SET,USED
・スプライトとBG両方にあってANIMと関連あり - COLOR,OFS,ROT,SCALE,VAR
・スプライトとBG両方にあってANIMと関連なし - CLIP,CLR,FUNC,HOME,PAGE
・BGのみ - COORD,COPY,FILL,SCREEN
 また「●●を始める/有る状態にする」「●●を止める/無い状態にする」などでコンビとなる命令もあります。機能説明は略すが、これも並べて覚えちゃいましょう。
・ANIM/CHK,COL/COLVEC,STOP/START,HIDE/SHOW,SPUNLINK/SPLINK,SPHITINFO/SPHITRC/SPHITSP,BGGET/BGPUT,BGLOAD/BGSAVE
(第八回につづく 次回はまたサンプルゲームで、コマンド入力ゲームの予定です)
★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。

公開キー  :4DC3DK3V(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
次の更新は、『PCでスマホゲーが!』の予定です。お楽しみに!

2019年04月15日

プチコンゲームプログラミング講座~第六回「潜水艦ゲームを作ろう」

文 プチ太郎 氏
=-=-=-=-=-=-=-=-=-=-
 冒頭から少しお詫び?です。今回は三次元視点シューティングゲームの予定でしたが、プログラムのメイン部分がうまく実装出来ず、頭を抱えていました(実は「スターウォーズ」「スペースハリアー」みたいなゲームを作った事がなかったw)今回もサンプルゲーム無しの講座にしようか、落とそうとかといろいろ考えていた所、たまたま今回のゲームを思い出したので、今回は(も?)急ごしらえですがこれにしました。キャラが多い割に、行数も150行強と短めです。
教材プログラム 6 「潜水艦ゲーム」(ファイル名「KOZA06」)

koz06_s1.jpg

★ ゲームの内容と説明 ★
 「ディプスチャージ」(英語で爆雷の意味)が元祖だが、79年にセガがアレンジして出した「ディープスキャン」で有名になりました。スピードに頼らず独特の味わいがあるので、その後も多くの類似作が作られています。
 初心者にとっては「第四回のシューティングゲーム『クセヴィオウス』(以下、第四回と略す)と全然違うな」と思った方もいると思います。実は第四回と自分・自弾・敵・敵弾の発生位置と動く方向が違うだけで、処理内容は似ており、今回も第四回からあちこちの処理をコピペして持ってきています。また第四回より一世代古いゲームなので、動きの処理ももっと単純です。今回もスプライトに関する命令が沢山出てきますが、それは次回でもまとめて解説するので、今回だけで判らない場合は、次回までお待ちください。

★ プログラムの説明1:初期設定とメインループ ★
 まず初めに、まとめて説明しておくべき「爆風とSPANIMの処理」から書きます。今回もゲーム中のキャラや爆発した時の爆風を、第四回同様にSPANIMで連続して見せています。今回やっているキャラは爆雷(これだけずっとそのままなので、9行目で定義)・潜水艦爆発・機雷上昇・機雷爆発の4種類です。SPANIMで使っている変数は全てAが表示間隔・変数Bがキャラです。どれも記述が長くなり、GOSUBかDEFでもっとコンパクトに出来ないか考えたが、それぞれ微妙に設定が違うので出来ませんでした。何かいい方法は無いでしょうか…。
 また水中という事で、スプライト表示を透明または半透明にしている部分があります。SPANIMに「"C"」「マイナス符号つきのfps時間」「0(カラー数値で言う透明)」を使っている所は大体これです。ただし使用後にSPCOLOR #WHITEをしないと、キャラが見えずに動くという、判りにくいバグが出るので注意!
 またS番号でSB[S]を新設しています。これは通常(SS[S]=O)と違う動作をしている間、どれだけ待つかというインターバルのBです(正しくはIntervalですがw)。爆破処理でSS[S]=1とした後(今回使っているのはこの爆破だけですが)、SB[S]に待たせたいカウントを代入し(今回は58行や113行で使用)、SS[S]が1ならSB[S]から1ずつ減算、0になるまで余計な事はしないという処理です。この処理がいい加減だと、やられた演出を表示中のキャラに何か命中した時、さらにやられた演出が繰り返されるなどのバグが出ます。第四回の爆破処理ではSS[S]に、-1より小さい数字で入れて使っていましたが、処理が複雑になったら、そうした変数共用は出来るだけ避け、今回の様に特定の変数を作る方が、バグが出にくくなります。

koz06_p1.jpg

@INIT1前の初期設定
 4~9行のS番号初期設定は、第四回を少し書き換えただけです。8~12行は、プログラム起動時に一度やっておけば変更が無いスプライトの設定です。背景は第三回「アドベンチャーゲーム」ではBGを使ったが、今回は13行目でBACKCOLOR命令を使いました。3号ではレイヤーが奥の1024から手前-256まであるが、これは一番奥に色をつける命令です。逆に一番手前はFADE(フェードインのフェード)が使えるが、単語と位置以外は同じ働きです。fps指定でSPANIMのようにジワジワ変える事も出来ます。
 14~16行はBG0番に海水のアニメーションを描いており、第四回の地面と似ているが、もっと簡単です。またこれも以前解説した事ですが、沢山のキャラを出すとどれが上や下になるか調整が大変です。今回は簡単な処理だからか、奥行き処理を一切しなくてほぼ大丈夫でした。強いて言うとゲームオーバー時のBG文字が、スプライトより後に出る程度です。
@INIT1 リプレイ時の初期設定
@INIT2 ミスした後の初期設定
 26行目のSPCOLORとSPROTは、配列変数S*[0]と共に、自機が沈んだ状態を元に戻しています(@MISS参照)。BKZは@JIKITAMA、DKZは@TEKITAMAを参照。
@LOOP メインループ
 キャラを細かく動かす場合、WAIT命令は1fpsずつ動かすのが基本です。しかし今回それをやったら、元のゲームがゲームなので微妙に速くなってしまったため、オリジナルと似た雰囲気の「WAIT 2」としました。このため前述のSB[S]などに、いつもなら待ちたいfpsと同じ数(一秒なら60)を代入すればいいが、今回は2fps待つのでその半分(一秒なら30)となるので、解析する方は注意して下さい。
★ プログラムの説明2:定義済み処理(メインキャラ) ★
@JIKI 自機(駆逐艦)
 近代戦風の軍艦キャラが無いので、10行目でSPDEF番号365番「帆船」を仮使用しました。小さいのでSPSCALEで横に拡大した所、イージス艦みたいにも見えたので(え?)見苦しくないかとそのままにしてあります。もしこれで不満だと思う方は、前回のミニプログラム「K05T16」とSPDEFを使って、灰色の軍艦を自作してみて下さい。
 マイキャラの移動は左右だけなので、第一回と同じくらい簡単です。BUTTON関数は、@JIKITAMAの爆雷ボタンと同時に押す必要があるので、前回と前々回説明した「ANDを使って、そのボタンのビットが立っているか」の判定で動かしています。
 さて以下の3処理の前に、書いておくべき特徴です。海中は自弾・敵機・敵弾の三種類が入り乱れて複雑そうですが、実際に影響するキャラ同士は非常に少なく「海中で自弾から敵機に」「海上で敵弾から自機に」の二つだけです(アーケード「ザ・ディープ」では撃ち消し合う)第四回で解説した二種類の当たり判定処理については、今回座標判定式だけです。当たり判定の数や場所が限られる事も理由ですが、もう一つの理由は、拡大・縮小して使っているスプライトが多く、これにSPCOLを使うと、上下左右にドット単位で当たり判定の誤差が出るためです(割り算で割り切れない余りが出るためではと思われます。スプライトやBGの表示も、砂粒のように乱れが出ますが、プチコンBIGでは表示の乱れは出ません)
@JIKITAMA 自弾(爆雷)
 こちらのBUTTON関数は、48行目で#Aと#BつまりAボタンとBボタンだけ取得し、49~50行でどっちかだけ押されたかにより、左右の投下位置を決めて「GOSUB @JT_NEW」で処理しています。変数BKZ(KZ=数)は第四回のS_Tと同じで、連射防止用のカウントです。
 爆雷が画面に出ていたら、108行目でグラフィック縦座標SY[S]を調べ、66行目で240に来ていたら底に着いたので、@S_LOSTで戻しています。ここから潜水艦との当たり判定です。ドット単位で動かしているので「縦と横どちらも一定の範囲に入ったか」でも考えたのですが、潜水艦は一段に一隻しか出ないので、縦座標(毎回SY[S]と書くのが面倒なため、53行目で一時的に変数Yに代入)は54行目で、前回説明したMODを使った「Y MOD 24==0」、つまり24ドット毎に当たりの可能性ありとし、55行目で当たった可能性のあるS番号をYから相対計算して変数SH(=Sprite Hit)に代入、56行目でSX[S]とSX[SH]の横の差が-8~32なら当たりと判定しています。得点は深い方が大きいので、これも63行目でよく似た処理により、スコアに代入しています。
 当たったら爆雷はすぐ@S_LOSTで消し、S番号がSHに入っている潜水艦の爆発処理に移ります。57行目のBEEP 108は3号ですぐに覚える音色「大爆発」ですが、ここで新パラメータ「BEEP 音色番号,周波数」を使っています。これは周波数の値に応じて音の高さを変えるものです。本来のパラメータは±32768だが、プチコンBIGでは±4000を過ぎると音が変わらなくなるので、±4000以内にした方がいいでしょう。58行目のSB[SH]は前述した通り。最後に69行目で上記のどちらの処理にも該当しなかったら、1ドット上に進めて表示し直しています。
@TEKI 敵(潜水艦)
 先に今回使う潜水艦のSPDEF情報の話をします。今回も3号のプリセットを使っていますが。潜水艦は上下左右4方向設定されていると思いきや、1419番と1420番のSPDEF定義が、どっちも右向きになっていました。これはSPCHRで表示する時にもアトリビュート設定(前回説明したビット単位でのパラメータ)で修正できるんですが、今回は19~20行においてSPDEF OUTで一度1419番のSPDEF情報を取得し、隣の1420番に、定数リテラル#SPREVH(Sprite Reverce Horizontal=スプライト横逆転)を使って再定義しています。SPDEFは次回詳しく解説します。
 肝心の処理ですが、「出す」「動かす」「自身が爆沈中に待つ」の三つしか無いので、ここも記述は少なめです。まず80行目で出現していなければ64分の1の確率で出現させています。RND関数で変数Aに0か1を入れ、これで左から出して右に動かすか、右から出して左に動かすかの変数を計算しています。83~84行目は、爆風の時にもスプライト表示情報を変えているので、それを戻すため。オリジナルの潜水艦キャラも高さが大きくて不づり合いなので、SPSCALEも実行しています。 87~88行は画面の反対側まで行ったら消滅し、でなければ90行目で移動。3号では文字以外は画面外にも表示する事が可能だが、そうすると機雷発生の処理が少し面倒なので、画面端にしました。93行目はSB[S]処理。

koz06_p2.jpg

@TEKITAMA 敵弾(機雷)
 「ディープスキャン」では難易度の表現として、潜水艦を沈めるたびにレベル表示が高くなっていき、機雷の発射頻度も増えます。これを再現するため、変数DKZをゲーム開始時には28行目で0とし、潜水艦が沈むたびに64行目で+1して行きます。100行目のFORループではD_SからD_Eでなく「D_S+DKZ」としているので、潜水艦が沈むたびに機雷が増えます。ただし注意点として、100行目ではこの時の値がD_Eを超えない様にしています。超えるとS番号が未定義なのでエラーが出てしまいます。
 第四回でも説明した「敵弾を敵キャラから出すかの判定は、敵キャラ内の処理で行うか、敵弾内の処理で行うか」ですが、今回は後者の101~107行で行っています。機雷は雪の結晶みたいなSPDEF1203~1204番を使いましたが、サイズが大きいのでSPSCALEで半分にしています。機雷が画面に出ていたら108行目で縦座標を判定し、海面上である28なら109~112行でスプライトを爆発に書き換え、@JIKITAMA同様に機雷と駆逐艦の横の差が-12~52ドット以内なら、SS[0]=-1で駆逐艦の死亡フラグを立てます。でなければ116行目で1ドット上に進めます。119行目はSB[S]処理。

★ プログラムの説明3:定義済み処理(その他) ★
@S_LOST 特定のスプライトを画面から消す
 第四回では消すS番号と他のS番号が干渉するため、この処理での変数をS2にして、Sと区別していましたが、今回は処理が単純なので、ここでもSのままです。
@ALLCLS ゲームに必要な画面を一端クリア
 今回は全スプライトと、文字表示用のBG3番を消すだけで、この時@SCRも実行しています。
@SCR スコアやハイスコアを表示
 以前と全く同じではつまらないので、「特定の点数で一機追加」を新規実装しました。これは@INIT1でフラグ変数EXを0にしておき、得点が入ってスコア表示のたび、「EXがまだ0 AND スコアが一機追加の基準(今回は2000点)」まで行ったら、ファンファーレのBEEP 113を鳴らして一機追加というものです。なおここでEXを1にしておかないと、その後は得点表示するたびに一機追加が続く…というバグになってしまいますwここをうまく工夫すると、さらに得点を積むと一機だけでなく二機目も追加とか、「ゼビウス」のようにウン万点取るたび(そう、ここもMODで計算すればいいですね)に毎回追加…という実装も可能です。
DEF JI LX,LY,L$ BGを使って文字列を表示
@MISS ミスおよびゲームオーバー
 34行目でSS[0]が-1になるとここに飛びます。145~146行は駆逐艦が傾いて轟沈する姿を、SPANIM命令で容易に表現しています。これは「ディープスキャン」のプチコン亜流のうち、葛城コニミルさんの「D.P.S.」を参考にしました。
 ところで「ディプスチャージ」では残機制でなく時間制で、自機は時間内に何度やられても復活します。時間制の部分は簡単に実装出来たんですが(本当は三次元シューティングで使う予定でした)、ゲームが進みながら自機がやられてまた出てくるのは、プログラムが複雑になるので見合わせました。またこの当時のゲームのBGMは、ミスしてやられる瞬間にBGMSTOPが多いが、今回は23行目からずっとBGMPLAY 34が続きます。これも時間制にしようとした名残ですが、雰囲気を考えてこのままとしました。

koz06_s2.jpg

★ このゲームの未実装・改良できる部分 ★
 3号の性能で「ディープスキャン」よりも綺麗な画面となりましたが、未実装もかなりあります。見やすくするプログラムとして入れられなかった理由が大きいのですが、ある程度件数があるので並べておきます。スキルのある方は実装に挑戦してみて下さい。
・未発射の爆雷のストックが、画面上部に表示されていない。
・潜水艦の速度が、オリジナルでは上にいるほど速い。
・潜水艦に沈めた時の点数が表示されていない。
・「ディープスキャン」では、高得点を狙える「X艦」が最深部を通る。
・潜水艦の接近を知らせるレーダーが無い。
(第七回につづく シューティングが二回続いてバランスが悪くなってしまいましたね。七回目はまたミニプログラムによる基本の復習、八回目は反射神経でない数値思考ゲームの予定です)
★今回の教材プログラムは、サーバにアップしてありますので、サーバからダウンロードしてご覧下さい。
公開キー  :5DBEVWKE(連載が進んで新しい教材プログラムが追加されると、公開キーも変わります。この公開キーでダウンロード出来ない場合は、連載の一番新しい回の公開キーをご利用下さい)
プロジェクト:KOZA
ファイル名 :上の教材プログラムの見出しをご覧下さい。
=-=-=-=-=-=-=-=-=-=-
次の更新は、『自爆して点を稼げ!PSPにもなったよ。EveryExtend』の予定です。お楽しみに!