ふろく

ホーム > ふろく > BASIC講座 > パソコンミニPC-8001でゲームを作ろう!
THE RETURN OF BASIC COURSE
BASIC講座

パソコンミニPC-8001でゲームを作ろう!
THE RETURN OF BASIC COURSE

帰ってきたBASIC講座

みなさん、おひさしぶりですm(_ _)m
さて、BASIC講座で一度は完成させた「ブロックくずし」ですが、
さらなる改良を加えたものを作っちゃいましたw


まずは基本レベル


ブロック配置が?!


ブロックの形状が?!

ということで、その改良について、ここでどどーんと公開します!

今回大きく変わった仕様は3つ。
 ・面構成を自由にレイアウトできるようになった。
 ・ブロックの種類が増えた。
 ・移動するおじゃまキャラが増えた。
です。

やっぱり、ずっと同じ面が繰り返されるのは、ゲームとしての刺激が足りませんよね?

面が進んだらブロックのレイアウトは変わって欲しいし、ブロックも1種類じゃ物足りないし、
さらに、おじゃまキャラも止まったままじゃ、画面に動きが無くて寂しい。

そんなこんなで、プログラムの改良に着手しました!

今回は、すでに用意してある以下のファイルをダウンロードして使ってくださいね。
BLOCK6.cmt

(いつものお約束)使い方説明
ファイルをダウンロードして、パソコンミニPC-8001のシステムが入っている
microSDの /boot/PCM の下にコピーしてください。
システムを起動して F9->MEDIA にて、 BLOCK6.cmt を SET し、BASIC上で
CLOAD “BLOCK6
で、ロードできます。

レベルの基本構成

今回追加する仕様を満たすため、各レベルごとにレベル構成データというのを、
指定できるようにする必要がありました。

つまり、
 ・おじゃまキャラの配置位置と種類の指定
 ・ブロックの配置位置や形状、そして耐久度の指定
です。

これらは、4000~4570行までのプログラム部と、
5000行~、5500行~、6000行~、6500行~、7000行~、7500行~と、
今回用意した6レベル分のデータ部で設定されます。

さらにデータ部は、おじゃまキャラ設定部と、ブロック設定部の2種類からなりたっています。

まずは4000行からプログラムを追っていきますね。

4000行のMLV=6というのは、今回用意したレベルデータ数です。
ご自身でレベルデータを増やした場合は、ここを変更してください。

4010行ではレベルを設定するのに必要な変数の初期化を行っています。

4020~4038行までで外枠を描画しています。

4040~4050行で、各レベルごとの設定プログラムを呼び出しています。

4100行から先は、各レベルで指定されたデータを読み込みながらレベル設定を行うプログラムです。
そして、この部分が今回の重要な要素です!
ですので、より詳しい解説が必要なので章を分けて説明しますね。

さて次章に進もうと思うのですが、その先で新しい命令が使われていますので、その解説をして
本章を終わりにしようと思います。

 READ 変数名1(,変数名2,変数名3,・・・)
 DATA データ1(,データ2,データ3,・・・)
 RESTORE 行番号

READ命令とDATA命令はセットで、READを行うと一番最初にあるDATAに記述されている値を読みだして、
READに指定されている変数に値が格納されます。

以下のサンプルプログラムで説明します。

10行でREAD Aとしていますね?
ではどこから値を読み込むかというと、プログラムの先頭から進んでいって、一番最初に
見つかったDATA文がある60行から値を読み込みます。
60行にはDATA 1とありますので、READ Aで指定した変数Aに、1が格納されます。

次の20行ではREAD B,Cとしてあります。
このように変数をカンマで区切って同時にデータを読み込むことが可能です。
ここで読み込まれるデータは70行からのDATA 2,3です。
(こちらもカンマで区切ってデータを連続して並べることができます)
そうです、READするとDATA文を次々と読み進んでいくのです。
結果は、変数Bには2が、変数Cには3が格納されます。

さて30行にRESTORE 60とあります。
これは巻き戻しというか、データの読み出し位置を60行に再設定することができます。
よってREAD Dによって変数Dには、60行にある1が格納されます。

なおREADに使う変数には文字列変数、DATAには文字列を使用することができますよ。

おじゃまキャラ設定の巻

まずは、おじゃまキャラの設定を、レベル1のデータを使って説明します。

レベルが1の場合、4050行のON GOSUBで5000行が呼び出されて、RESTORE 5100が実行されるので、
データの読み込み先が5100行からに設定されます。

おじゃまキャラの設定プログラムは4100~4190行で、レベル1のおじゃまキャラ設定データは
5100~5120行になります。
(それ以降はブロック設定データなので、次章で説明します)

4110行では5つセットでデータを読み込んでいます。
1番目の値が種別を表し、種別ごとに2~5番目の値を使って各種設定を行っています。

1番目が0の場合:停止タイプ
その場にとどまるタイプのおじゃまキャラで、「#」で表示されます。
データは5100行がそれにあたります。
2番目が出現するスタートY座標で、3番目が出現するエンドY座標です。
つまり2番目と3番目のY座標の間に出現するということですね。
(X座標はランダムです)
そして4番目の値が最大出現数です。
時間が経つにつれどんどん出現していくのですが、その最大数をここで指定します。
なお、停止タイプの指定は1面ごとに1セットまでです。
2つ以上指定すると4160行のエラーチェックに引っかかって停止します。
5番目の値ですが、のちほど説明します。

1番目が1の場合:移動タイプ
今回新しく増えたおじゃまキャラで、「@」で表示されます。
データは5110行がそれにあたります。
左右に行ったり来たり移動して、障害物があると反転します。
最大3つ、配置が可能です。
(配置を4つ以上にもできますが、ゲームが遅くなりすぎてお勧めできません…)
2番目と3番目の値を使って、最初の出現座標を指定します。
4番目は移動方向です。右に行く場合は1で、左に行く場合は-1を指定します。

1番目が9の場合:データの終了
データの終了を表し、おじゃまキャラの設定を終了します。
データは5120行がそれにあたります。
2番目から5番目の値は使われないダミーデータです。

さて説明を先送りにしていた5番目の値について解説します。
5番目のデータは初期タイマー値です。
おじゃまキャラはそれぞれタイマーカウントを行い、カウントが0になった時に出現や移動を行います。
カウントの幅は共通ですが、カウント0になるタイミングをずらしています。
これはなぜか?というと、同時に出現や移動の処理を行うと、いっぺんにその処理が行われて、
その時だけ画面全体の動作が非常に遅くなってしまい、遊びづらくなってしまうのを回避するためなんです。
ちょっとしたプログラムテクニックですね。

ブロック設定の巻

こちらはレベル3のデータを使って説明しますね。
まず、レベル3のブロック設定データは、6200~6499行にあります。

また、ブロックを設定するプログラムは4200~4570行にあります。

まず4200行でRXへデータを1つ取得します。
この値が 999だとデータの終了を表し、このレベルのブロック設定を終了し、ゲーム開始へと戻ります。
4210行がその判断を行います。

値が999ではなく、100未満だったら4500行に飛んで、さらにRYにデータを取得しています。
変数名からわかるように、RXとRYはブロック一個分の画面上の座標なんですね。
(なお4510~4520行は座標の範囲外チェックをしています)
座標は4530行で配列変数B3に登録しています。
さらに4540行で、いま登録したブロックのB3への位置を配列変数B4に設定し、4550行で登録した
個数を1つ増やしています。
4560行はブロックの登録先を更新しています。
その後、4200行に戻り、再度RXへデータを読み込みはじめます。
ここまでがブロック1個分のデータを取得するループです。

とりあえずプログラムを読み進めて行きますね。
6211行のデータをみると、5,7,5,6,5,5,6,5,7,5と、100未満の値が並んでいますので、
X座標とY座標で2つずつの組でブロックが5個分登録されます。

で、次です。
RXに100が読み込まれるので、4240行の107以下の条件に当てはまり、4300行へ飛んでいます。
(4230行はあとで説明します)
4300行を見てみましょう。なにやら計算をしています。
HPにRXから99を引いた値を入れて、同時に配列変数B4へHPを登録しています。
これは変数名から推察できる通り、ブロックの耐久値を登録しているんです。

4310行はブロックの番号に10を足して当たり判定番号をHTに生成し、今回のブロックの個数をNに代入しています。

4320~4360行は、今回のブロックを画面に表示しながら、変数MPの仮想当たり判定マップに
当たり判定番号を登録しています。

4370行で変数BCのブロックグループ数を更新し、そして変数B2の登録ブロックグループ位置も更新しています。

…うん、なんのこっちゃですよね(^^;

ということで図に表して説明しますね。

配列変数の内容

仮想当たり判定マップ(一部切り出し)

つまりブロック単体の位置を登録し、複数ブロックを1つのグループとして扱って「どこ」に「何個」で
「耐久値」はいくつかを、記録していっているのです。
同時にブロックの表示と仮想当たり判定マップへの登録も行っています。
なおグループのブロックは、登録箇所から個数分、マイナス方向へ取得していけば、良いことになります。

この二重の登録方法の良いところは、「ブロックの塊がどんな形状でも個数でも効率よく登録できる」という点です。
また仮想当たり判定マップに登録されている番号から、ブロックグループをすぐに特定できることも
可能となっています。

もしこの方式ではなく、例えば単純に1ブロックグループが最大16個まで必要だとしたらどうなるでしょうか?
そうです、すべての1ブロックグループに16個分のブロックの座標を覚える領域が
必要となってしまうのです。
そうなると1ブロックグループに1個しかブロックが無いようなレベルだったら、
残りの15個分の領域は非常に無駄ですよね?

このようにデータの間接参照手法を覚えておくと、プログラミングの幅が広がりますよ。

4360行についてもう少し詳しく解説しましょう。
新しい命令が使用されています。
 CHR$(文字番号)
この命令は文字番号から文字を取得する命令です。
(文字番号は、F11キーを押してクイックコマンドリファレンスの17ページにある
キャラクタコード表を参照してください)
4360行では&H88からHPを引いていますよね?
HPの幅は、DATA文に指定できるのが100~107なので、99が引かれた値の1~8までがHPに指定され、
表示されるキャラクタは&H80~&H87ということになります。
つまりHPが高ければ高いほど縦幅が薄いブロックが表示されるようになっています。

では残りのプログラムも見てみましょう。
4230行では、800が指定されていると4400行に飛んでいます。
4400~4450行は無敵ブロックの登録になります。
無敵ブロックは外枠のカベと同じく、破壊不可能なブロックです。
一応位置の登録はしてはいますが、ブロックグループの個数であるBCのカウントはしていません。
(破壊不可能なのにブロックが全部破壊されたことを感知できなくなるので)
4440行にあるGOSUB 200で表示と仮想当たり判定マップへの登録を行っています。

エリミネートぉ~

レベルごとの設定パートは終了しましたが、ゲーム本編側のプログラムもそれに合わせて修正しないとなりません。

ですが基本的な部分は修正する必要が無く、大きく変わるのは抹消処理とおじゃまキャラのコントロール処理の2つです。
(それ以外では高速化などのために多少修正していますが、実行している内容にあまり差異はありません)

この章では抹消処理に関して説明します。

抹消処理は600~644行で行っています。
細かく処理ごとにブロックを分けると以下のようになります。
・600~604行が分岐処理
・610行が停止型おじゃまキャラの抹消処理
・620~622行が移動型おじゃまキャラの抹消処理
・630~644行がブロックの抹消処理

ではブロックごとに解説します。

まず停止型おじゃまキャラですが、当たり番号が3なので、603行で分岐して、610行で抹>消処理を行っています。
以前と処理は変わらず対応する行番号が変わっています。

次に移動型のおじゃまキャラですが、当たり番号は4以上で、602行で分岐して、620~622行で抹消処理を行っています。
停止型と違うところは、タイマーを再セットしているところです。

最後にブロックの抹消処理です。

当たり番号は10以上を割り振っていますので、600行で630行に分岐しています。

630行では当たり番号からブロックグループの番号に変換し、かつ自身の耐久値を減らしています。
また632行は登録先の番号と個数を取得しています。
634行から642行でグループに属しているブロックを、座標を取得しながらひとつずつ変更しています。
なお、640行は耐久値が0になったら抹消して、耐久値が残っていたらブロックの形状を更新しています。

644行は耐久値が0になったらブロックグループの個数を減らしています。
これはレベルクリアの判定用の処理ですね。

646行は得点の加算で、ブロックが消えたらではなく、耐久値が1減ったら+10点になっています。

650行で得点の表示を行っていますが、実はこの行は初期画面を描画する時にも呼び出されます。
わざわざGOSUBで呼び出すのもムダなので、そのまま処理が続く様にプログラムを配置しています。

ということで特に難しいところはないですよね?
え?640行?POKE命令?
あぁ、すいません。解説を忘れてました。
 POKE メモリアドレス,書込み値
となっており、メモリに直接、値を書き込む命令です。
(とうとう機種依存の命令を使い始めてしまいました…)

PC-8001は、キャラクタVRAMという領域に値を書き込むことで、画面に文字が表示されます。
LOCATE命令やPRINT命令は、人間が分かりやすい形でキャラクタVRAMに値を書き込む命令なんですね。
ただ各命令の内部で色々と処理を行うので、速度の点から不利なため、今回は直接値を書き込むようにしました。

なお、VRAMのアドレスマップは、F11キーを押してクイックリファレンスの24ページに
載っていますので参考にしてください。

補足:
POKE命令に使われているアドレスですが、&HF302となっています。
これはカラー表示にすると1列目は使えないために最初から値を加算してあるのです。
(覚えていますか?第二回のHello World!の章で説明しました。)
また最下位の桁が1でなく2なのは、40桁モードだと偶数のアドレスの文字だけが表示されるからです。

なおキャラクタだけしか書き込んでいないので、カラーのアトリビュートは変更されません。
ですので、カラー指定が必要な箇所はLOCATE/PRINT命令を使って表示を行っています。

おじゃま、行きまーす!

とうとう最後の大規模修正点の解説です。

以下が、おじゃまキャラのコントロールプログラムです。

構造ですが、2601~2604でタイマーのカウントダウンと種類ごとへの分岐、2610~2624行が移動型の処理、
2630~2650行が停止型の処理で、2690行はループの終端です。
いままでと同様に順を追って解説していきますね。

2601行はループのトップで、EMには登録されているおじゃまキャラの個数が入っています。
その後、おじゃまキャラごとに格納されているタイマーのカウントダウンを行っています。
(使っている配列変数はET(EnemyTimerの略)です)

2602行は、タイマーが残っていたら2690行に飛び、NEXTで登録おじゃまキャラ分、ループします。
タイマーが0になって、停止タイプ、移動タイプのそれぞれに分岐するのが2604行の処理です。
(使っている配列変数はEV(EnemyVariationの略)です)

2610~2624行は移動型の処理です。

2610行でタイマーを再設定し(設定値5)、現在の座標にある仮想当たりマップをクリアします。

2612行で表示を消していますが、ここで使われている配列変数EAに注目します。
配列変数EAには、その行の先頭のVRAMアドレスが格納されています。
なぜこんなことをするのか?理由があります。
まず、移動タイプは左右にしか動かないため、Y座標は固定となります。
という事は、その行の先頭アドレスである&HF302+(Y*120)の計算は、一度行って覚えておいて、
次回からはその値を使えばよくなります。
実はZ80 CPUで掛け算を行うと非常に処理がかかるので、その処理を回避しているんですね。
ですので、そのあとに続くEA(I)+X+Xは、+X*2と掛け算を使わないで足し算だけで実現させています。

2614行は移動方向(使っている配列変数はED(EnemyDirectionの略)です)を取得し、
次の移動先に障害物があれば、方向を反転しています。

2616行は更新された移動方向と移動先の座標の計算です。

2618行は移動先にボールが有ったら何もせずにループ判定に飛びます。
これはボールに突っ込んでも反射処理はできないので、一旦停止することで処理を回避する工夫です。

2620行はようやく決まったX座標の更新と仮想当たり判定マップへの登録処理です。

2622行で移動型のキャラの表示を行っています。

2624行はループ判定へ飛んで自身の処理を終了させます。

2630~2650行は、停止型の処理になっています。

まず2630行で最大出現数のチェックを行っています。
すでに最大出現数まで達していたら、タイマー値を設定する2650行へ飛んで、追加の表示は行いません。

2632~26338行で出現位置をランダムで決め、かつ出現位置が空白かをチェックしています。
空白でなければ、再度出現位置をランダムで振り直します。

2640行は出現数をカウントし、仮想当たり判定マップへの登録を行っています。
なお停止型の当たり番号は3です。

2642行は実際に画面に表示します。
ここでPOKEを使わないで、LOCATE命令/PRINT命令で行っているのは、処理速度的にあまり変わらないためです。
要はプログラムを読みやすい方にしているだけです。

2650行は、タイマー値の再設定です。
なおWTは、レベルが進むごとに少なくなっていきます。

2690行ですが、ループのNEXTとなっています。

そして、これでプログラムの解説は終了となります。
お疲れ様でした~。

エンディング?

さてさてみなさんはレベルいくつまで到達できましたか?
自分で作っといてなんですが、残機3つでは6レベルをクリアできていませんw
(ちょっと難しくしすぎたかな…)

さらに今回のBASIC講座は中級者以上でないと、理解が難しいかもしれませんね(^^;

もともとは、このぐらいの内容のBASIC講座を予定していたんですが、規模が大きいのと概念的に難しいかも?と思い、
前回までの内容にとどめていました。
ただいつか、完全版を公開しようと機会をうかがっていたんです。

なにはともあれ公開できたので、めでたしめでたし。

さて、ひとつおまけがあります。
run 3000 を行うと、3010行にあるLV値にあわせて、指定のレベルの面構成をチェックすることができます。
もし、「オリジナルのレベルを作ってみよう!」と思う方は、ぜひ活用してみてくださいね。
(レベルを増やしたら、4000行と4050行の変更を忘れずに)

またゲームスタート時に指定のレベルから開始したい場合は、1030行にあるLVの値を変えると良いですよ。

ではでは(^^)/




一方、その頃どこかで…。

「…今回、追加のBASIC講座があったようだな」
「フフフ…、あれはホビーパソコンを扱う上で基本…」
「移植性の高いBASICプログラムなど笑止」
「では、いよいよもってアレを投入するべきか…」
「そ、それは、さすがに時期尚早ではッ!?」
「かまわぬッ!古の言語!マシン語を召喚せよ!」

…なんてね(^^;

©HAL Laboratory, Inc.