fx-5800P【ゲーム】:もぐら叩き(3)
fx-5800Pでもぐら叩き
e-Gadget
【2013/11/24、誤植を修正】
fx-5800P【ゲーム】:もぐら叩き(2)の続き...
前回までに作ったプログラム:
10→DimZ
35→Z[1]:36→Z[2]
37→Z[3]:21→Z[4]
22→Z[5]:23→Z[6]
31→Z[7]:32→Z[8]
33→Z[9]
For 1→B To 3
Locate 1,B,"[ ][ ][ ]"
Next
Lbl 0
RanInt#(1,4)→T
RanInt#(1,3)→X
RanInt#(1,3)→Y
T^(3)→T
9+X-3Y→N
Lbl 1
Dsz T
Goto 1
Locate 3X-1,Y,"O"
Do
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,Y," "
[ 新たなコードは、主にここに追加する ]
Goto 0
0→DimZ
軽くおさらいすると、Lbl 0 ~ Goto 0 の間で処理を繰り返すようなループ構造になっている。Do ~ LpWhile のループで叩かれたモグラの判定を行い、命中した時にこのループを抜けて、下へ処理が進む構造だ。
従って、今後追加するコードは、主に判定結果に応じた処理になるので、上に注記したように Do ~ LpWhile ループの後から Goto 0 の前までの間に、新たな処理を追加することになる。
もぐら叩きは反応の速さを競うゲームだから、反応速度を測定する機能が不可欠になる。先ずは、反応速度を調べる機能を追加する。
次に、その結果に基づいて、プレイヤーの頑張りを点数に置き換える機能が必要になる。加えて、ゲーム終了の条件も作らなくてはいけない。ユーザーのプレイをちょっと妨害する機能があれば、さらに面白くなりそうだ。
このあたりでゲーム設計をきちんと決めておく必要がありそうだ。
6.モグラが顔を出してから叩くまでの反応時間を測る
プレイヤーの頑張りを点数に反映させるために、モグラが顔を出してから、叩いて命中させるまでの反射時間の測定をできるだけ簡潔な処理で組み込みたい。
モグラが顔を出し、叩かれて顔を引っ込めるまでは、
Locate 3X-1,Y,"O"
Do
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,Y," "
この部分が制御している。プレイヤーの反射時間はこのループが回る回数に相当するわけだ。
そこで、上のDo~LpWhile ループの中に、
Isz C
を追加する。
Isz C は、本来はCを1つづつ増やしながら(インクリメントしながら)Cが0になったとき、次の処理をスキップして、次の次の処理へジャンプするコマンドだ。但し、インクリメントだけを行う時にも使える。
ここでは、Cがゼロにならないことが大前提になる。もし何かの都合で、Cが0になってしまうと、次の次のコマンドへジャンプしてしまい、予期せぬ動作をするから、ここは注意が必要だ。従ってCの初期値は0以上でないといけない。
Isz Cは、先にCを1つ増やしてから(インクリメントしてから)、ジャンプするかどうかの判定を行うので、最初にCが0であっても問題はない。しかし万一Cが-1の場合は、1回インクリメントするとC=0となってしまい、ジャンプするので、必要な処理が飛ばされて、プログラムは誤動作してしまう。
Isz C の代わりに、C+1→Cとしても良い。全く問題ない。ただ Isz C の方が動作速度が速いので、ここではIsz C を使うことにする。Isz C の処理速度については検証済みだ [2017/08/06 追記・修正]。
⇒ fx-5800P:コマンドの処理速度【再編集】
⇒ fx-5800P:変数アクセス、比較・論理演算、条件分岐の速度比較
さてここで忘れてはいけないのが、Do~LpWhileループに入るまでに、変数Cを初期化して0にしておくことだ。
0→C
Locate 3X-1,Y,"O"
Do
Isz C
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,)," "
これで、プレイヤーの反射時間が C で分かるようになった。Cが小さければ速く反応したことになるし、Cが大きければ時間がかかったことになる。
10→DimZ
35→Z[1]:36→Z[2]
37→Z[3]:21→Z[4]
22→Z[5]:23→Z[6]
31→Z[7]:32→Z[8]
33→Z[9]
For 1→B To 3
Locate 1,B,"[ ][ ][ ]"
Next
Lbl 0 : 一番下のGoto 0からジャンプしてくる
RanInt#(1,4)→T
RanInt#(1,3)→X
RanInt#(1,3)→Y
T^(3)→T
9+X-3Y→N
Lbl 1
Dsz T
Goto 1
0→C : 追加点
Locate 3X-1,Y,"O"
Do
Isz C : 追加点
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,Y," "
[ 新たなコードは、主にここに追加する ]
Goto 0 : 上のLbl 0へジャンプして、繰り返す
0→DimZ
[ 新たなコードは、主にここに追加する ] のところに、Locate 11,3,C を追加して、Cの値をリアルタイム表示させて、このプログラムを実行して遊んでみると、一回モグラを叩くたびにCが10程度であった。
7.ゲームの暫定仕様を決める
実際にプログラムを走らせてみて、1回もぐらを叩くのにCが10程度になったことを参考にして、ゲームの暫定仕様を決めようと思う。
そもそも、モグラを素早く退治すれば得点を大きくしないといけない。そこで考えたのが持ち点制。持ち点から反応時間 C を引いたものを得点とすれば、速く反応した方が得点が大きくなる。今のプログラムだと、もぐらを叩くのにC=10程度なので、持ち点を20とした。
[ 得点 ] = 20-C
一回あたり、10点程度が得られる。
この得点計算だと、Cが20を超えるとマイナス得点になって面白そうだが、それではノンビリと待ちすぎなように思ったので、Cが18以上になると、もう得点を与えないようにした。マイナスにはならないが、増えない。このほうが面白そうだ。
次に考えたのが、一回の得点は浮き沈みがあるから、それを合計してゲーム全体の点数にした方が良さそうだと言うこと。そしてその総合点をリアルタイムで表示すると楽しそうだ。総合点が大きい方がよいと言うことだ。
但し、ゲームが延々と続くのは退屈だし面白くない。どうやってゲームを終わらせるかをチョット考えた。戦闘力が高くてもスタミナが無いとそれを発揮できないと言うのがゲームの定番だ。攻撃を受けてスタミナがゼロになったら死んでしまってゲーム終了と言うのが多い。
そこで、LIFEを設定して、これが減って行きゼロになったらゲーム終了にしよう。LIFEを減らすのは、Cが18以上になった時にしてみた。得点は減らないが、確実に寿命が縮むと言うのは良さそうだ。最初に与えるLIFEは、取りあえず 5 にした。
そんな感じで、脳内シミュレーションしてみた。なんとなくゲームらしくなったかな?
具体的に、以下のように COST、PRIZE、LIFE を導入して、ゲーム進行を定義した。
1) | 持ち点について: モグラを1匹相手にするたびに、持ち点をもらう: 持ち点 = 20とする |
2) | COSTについて: COST は変数 C に格納する(偶然だが、既にそうなっている、ちょっとコジツケ) モグラを速く叩くと COST が小さくなり、遅いとCOSTが大きくなる |
3) | 得点について: モグラへの反応が速ければ得点が多くなり、遅ければ得点は少ない モグラ1匹を叩く際、最初にもらった持ち点=20から C を引いた結果が得点 得点: 20-C ・C≦17 の時(モグラへの反応が速い時): 得点 = 20-C を得る ・C>18 の時(モグラへの反応が遅い時): 得点 = 0、さらに LIFE が1つ下がる この境目の17を変数Dに格納して、判定させるようにする。 |
4) | PRIZE(総合点)について: 獲得した得点を足し合わせたものを PRIZE(総合点)とする。 PRIZE を変数 P に格納する PRIZE をリアルタイム表示する PRIZEの計算: P+20-C→P |
5) | LIFE(ゲーム継続権利)について ゲーム開始時に、LIFE をもらう: LIFE=5 とする LIFE を変数 L に格納する LIFE をリアルタイム表示する ・C>18 の時(モグラへの反応が遅い時): LIFE が1減る ・LIFE が0になると、ゲーム終了 |
あまり複雑だと、一度に機能を追加するのが面倒なので、先ずはこの程度で機能を組み込むことにする。
8.得点の機能を組み込む
得点加算の機能を追加する。
上の仕様では、C≦17 の時に 20-C の得点が得られる。そうでない場合は、得点はなくて、さらに LIFE が1つ減る。この判定の境目の17を、変数Dに入れておく。こうすると、あとで条件を変更するのが楽だ。
これをコードに表現すると、
If C≦D:Then
P+20-C→P
Else
L-1→L
IfEnd
但し、Pを0で初期化しておく必要がある。
0→P
これは、ゲームの最初の部分に置く。3x3 のモグラの穴を表示する前に書くことにする。
変数Dの初期化も、同じところに書いておく。
17→D
さて、PEIZE(総合点)とLIFEをリアルタイム表示する仕様なので、表示部分も追加しておく。
[ ][ ][ ]
[ ][ ][ ] PRIZE
[ ][ ][0] 0
LIFE:5
こんな感じの表示にしてみる。
先ずは、ゲームが始まった時の表示は、3x3 のモグラの穴の表示に続けて書く。
Locate 11,2,"PRIZE"
Locate 12,3,P
Locate 1,4,"LIFE:"
Locate 6,4,L
ゲーム進行時のリアルタイム表示は、
Locate 12,3,P
Locate 6,4,L
となる。
これらの表示は、Cの判定が終わってそれをスグに反映して表示するので、上の If C≦D:Then のブロックのスグ下に書けば良い。
If C≦D:Then
P+20-C→P
Else
L-1→L
IfEnd
Locate 12,3,P
Locate 6,4,L
ところが、実はチョット問題がある。
例えば、PRIZEが105となると、
[ ][ ][ ]
[ ][ ][ ] PRIZE
[ ][ ][0] 105
LIFE:5
次に、PRIZEが90になった場合、
[ ][ ][ ]
[ ][ ][ ] PRIZE
[ ][ ][0] 905
LIFE:5
となってしまう。
1桁目の「5」が残っているのだ。
そこで、PRIZEの値を表示する前に、一旦空白を書き込んで「105」を消しておけば良い。空白は書き込めるだけ書き込めば安全なので、今回は4個の空白を書き込む。
Locate 12,3," "
そこで、上のコードを以下のように修正する。
If C≦D:Then
P+20-C→P
Else
L-1→L
IfEnd
Locate 12,3," "
Locate 12,3,P
Locate 6,4,L
少しづつ機能追加をしてゆけば楽だ。しかし、変数の初期化や初期表示は、肝心のコードとは離れたところに書くので、これを忘れがちだ。
ここで、一旦プログラムのコードをまとめる。
10→DimZ
35→Z[1]:36→Z[2]
37→Z[3]:21→Z[4]
22→Z[5]:23→Z[6]
31→Z[7]:32→Z[8]
33→Z[9]
17→D:0→P
For 1→B To 3
Locate 1,B,"[ ][ ][ ]"
Next
Locate 11,2,"PRIZE"
Locate 12,3,P
Locate 1,4,"LIFE:"
Locate 6,4,L
Lbl 0 : 一番下のGoto 0からジャンプしてくる
RanInt#(1,4)→T
RanInt#(1,3)→X
RanInt#(1,3)→Y
T^(3)→T
9+X-3Y→N
Lbl 1
Dsz T
Goto 1
0→C
Locate 3X-1,Y,"O"
Do
Isz C
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,Y," "
If C≦D:Then
P+20-C→P
Else
L-1→L
IfEnd
Locate 12,3," "
Locate 12,3,P
Locate 6,4,L
Goto 0 : 上のLbl 0へジャンプして、繰り返す
0→DimZ
赤い文字が、今追加した部分だ。
9.ゲーム終了機能を追加する
LIFEがゼロに、つまり L=0 になると、Goto 0 をスキップして、処理をその下へ進めるように、コードを書き換える。
fx-5800P などのカシオの電卓には、それらに特有な独特な条件分岐コマンド「⇒」がある。これを使う。
取扱説明書にあるコマンドの例が分かりやすいので、転載する。
Lbl 1
?→A
A≧0 ⇒ √ ̄(A)◢:Goto 1
負の数の平方根はとれないのでエラーになる。
そこで、平方根を取るAが正か負かを先に調べて負でないなら平方根の計算をする必要がある。
このプログラム例では、⇒ の左が正しい(真)なら、次の処理を行い、正しくない(偽)なら、次の次までジャンプする機能をうまく使っている。
A がゼロ以上(負でない)なら、⇒ の次の処理「√ ̄(A)」を実行し、A がそうでないなら、次の次の処理 Goto 1 へジャンプする。
さて、取扱説明書に書いていないが、次のような使い方もできる。
Lbl 1
?→A
A⇒1÷A◢:Goto 1
これは、1÷A を計算するプログラムだが、A = 0 だと割り算できない、と言うかしてはいけない。もしA がゼロの時に1÷A を計算させるとエラーになる。そこで、A がゼロの時は、次の処理をスキップして、次の次の処理へジャンプさせる。これが上のプログラムだ。
A⇒1÷A◢:Goto 1
⇒ の左には変数だけがある。このように使う時、A がゼロ以外の時「真」となり、A がゼロの時「偽」となる。⇒ にはこのような性質がある。この性質は取扱説明書には書いていない。
この性質は、If A:Then...、Do ~ LpWhile A、While A ~ WhileEnd などでも使えるので、便利なことがある。
今のプログラムに話を戻すと、
プログラムコードの最後から2行目の Goto 0 を
Goto 0
0→DimZ
を
L⇒Goto 0
0→DimZ
と書き換えるだけで、目的が達成できる。
L がゼロでない時は「真」なので、⇒ の次の Goto 0 が実行される。
もし、L がゼロになると「偽」になるので、⇒ の次の次の処理へジャンプする。つまり Goto 0 をスキップして、次の処理 0→DimZ へジャンプして、プログラムが終了する。
⇒ コマンドは、実に便利なコマンドで、色々な局面で多用できるので、覚えておくと便利だ。
暫定仕様の機能を組み込んだプログラムをまとめると、以下のようになる。
10→DimZ
35→Z[1]:36→Z[2]
37→Z[3]:21→Z[4]
22→Z[5]:23→Z[6]
31→Z[7]:32→Z[8]
33→Z[9]
17→D:0→P
For 1→B To 3
Locate 1,B,"[ ][ ][ ]"
Next
Locate 11,2,"PRIZE"
Locate 12,3,P
Locate 1,4,"LIFE:"
Locate 6,4,L
Lbl 0 : 一番下のGoto 0からジャンプしてくる
RanInt#(1,4)→T
RanInt#(1,3)→X
RanInt#(1,3)→Y
T^(3)→T
9+X-3Y→N
Lbl 1
Dsz T
Goto 1
0→C
Locate 3X-1,Y,"O"
Do
Isz C
Getkey→K
LpWhile K≠Z[N]
Locate 3X-1,Y," "
If C≦D:Then
P+20-C→P
Else
L-1→L
IfEnd
Locate 12,3," "
Locate 12,3,P
Locate 6,4,L
L⇒Goto 0 : L=0の時Goto 0をスキップしてゲーム終了
0→DimZ
つづく...
⇒ もぐら叩き(4)
応援クリックをお願いします。励みになるので...