【プチコン4講座】あっちむいてホイを作ろう:仕組み構築編
こんにちは。継続の錬金術士なおキーヌです。
ブログ毎日更新は252日目になります。
プチコン4で作るミニゲーム講座記念すべき第1回目は「あっちむいてホイ」に決定しました。
プログラミングの基本的な仕組みを学べることと、前回軽く作り方を説明したからというのもありますが。
まずはCUIで作りましょう。
CUIとは前回説明した通り、いわゆる黒い画面のことですね。
なので見た目的にちょっと退屈かもしれませんが、面白いゲームを作るためには
仕組みを理解しておかないと応用が効きづらいのでまずはCUI編での作成です。
それではプチコンミニゲーム講座第1回目を始めようと思います。
ゲームループを作ってみよう
まず初めに、ゲームループという概念を覚えておいてほしいです。
プログラムというのは基本的に上から下に命令を処理していきます。
そして一番下まで行くとプログラムは終了してしまいます。
特に重たい処理をしなければ基本的に1フレームでプログラムが終わってしまいます。
1フレーム=1秒間のうちの1/60の時間です。
メチャクチャ早いですね。
そんな一瞬でゲームが終わってしまってはゲームですらないので、
まずはゲームにするためにプログラムのループを作らなければいけません。
なにはともあれ、ゲームループというものを作ってみましょう。
LOOP
ENDLOOP
はい、これだけです。
これだけ!?って思うかもしれませんが、これはただのループの開始と終わりですね。
この間にプログラムを書いていきます。
例えば文字を表示する「PRINT」文を使ってコードを書いてみると
LOOP
PRINT "ゲームループちゅうだぞ"
ENDLOOP
このようになります。
これを実行すると、すさまじい速さで「ゲームループちゅうだぞ」
という文字が流れ続けます。
ループする速度が早すぎて流れているのすらわからないかと思います。
もうちょっとわかりやすいようにしましょう。
LOOP
PRINT "ゲームループちゅうだぞ"
VSYNC
ENDLOOP
VSYNCという命令を入れました。
これは仕組みを理解するのはちょっと難しいので説明を省略しますが、
とりあえずループ処理の最後に置いておく命令と思っておいてくれればOKです。
どうしても今詳しく知りたいという人はプチコン4公式リファレンスのVSYNCの項目を参照してください。
ゲームループについてはここまでです。
変数を使って状態を保存しよう
プチコン4のチュートリアルを一通りやっている人は分かっていると思いますが、
最初なので軽く説明します。
変数とは入れ物です。
数字を入れたり、文字列を入れたり、命令を入れたり様々なモノを入れられます。
しかし保持できるモノは1つだけです。
2つ以上モノを入れることは変数ではできません。
他のモノを入れるときは前に入っていたモノに足して1つにしてやるか、入れ替えるかになります。
2つ以上モノを入れたいときは、変数ではなく配列という仕組みを使わなければいけません。
配列に関してはまた後日説明しようと思います。
変数に関しては言葉にして説明するより実際に使ってみたほうが意味が理解できると思うので
早速変数を使ってみましょう。
VAR WORD = "プチコン4はたのしいZOY"
LOOP
PRINT WORD
VSYNC
ENDLOOP
PRINTには文字列ではなくて変数を与えています。
さて、これは何が画面に表示されるでしょうか。
……そう、WORDという変数に入っている「プチコン4は楽しいZOY」が表示されますね。
では次の場合は何が表示されるでしょうか?
VAR WORD = "プチコン4はたのしいZOY"
WORD = "NO CREATE NO LIFE"
LOOP
PRINT WORD
VSYNC
ENDLOOP
表示されるのは
- プチコン4はたのしいZOY
- NO CREATE NO LIFE
- プチコン4はたのしいZOYNO CREATE NO LIFE
のどれでしょうか?
……イコールは代入になるので中身を右のモノにするという命令です。
なので中身は「NO CREATE NO LIFE」になります。
では次に文字列ではなく数字にしてみましょう。
VAR WORD = 0
LOOP
PRINT WORD
VSYNC
ENDLOOP
変数名がWORDのままですが気にしなくてもOKです。
これだと0が表示され続けますね。
では次のようにするとどうでしょうか?
VAR WORD = 0
LOOP
WORD = WORD + 1
PRINT WORD
VSYNC
ENDLOOP
WORDという変数にプラス1をしてループが繰り返されています。
つまりどんどんWORDの中身の数値が足されていくわけなので、
1・2・3・4…とループする度に数値が増えていきます。
数値の場合はこうやって数同士を足して1つの数値にできますが、
文字列の場合に足し算をすると、文字列同士がそのままくっついて1つの文字列となります。
つまり数値でもダブルクォート(文字列をかこっているちょんちょん)でかこって文字列として書いて足し算をすると
"1" + "1" = "11"
という風になるわけです。
数値と文字列の違いはプログラミングをする上で必ず知っておかなければいけないので
今ここでしっかりと覚えておきましょう!
変数の話をするとそれだけで軽く数記事書けてしまうのでこの辺にしておきます。
条件式を作ってループを断ち切ろう
ゲームというのは条件で様々な状況に変化しますよね。
あっちむいてホイの場合、勝ち負けに応じて表示する文字を変化させたいと思います。
条件式は先ほど使った変数を使えば柔軟な条件を作ることが出来ます。
例えばライフが0になったらゲームオーバーにしたい
という時に、ライフが3の時の条件・ライフが2の時の条件・ライフが1の時の条件・ライフが0の時の条件
と書くのではなくて、ライフが0になったときの条件だけを書くべきです。
もちろんライフが1の時に音楽を変化させたい!となった場合は、
ライフ1の時の条件式を書くのは正解です。
ライフが0の時にゲームオーバーにしたいというだけなのであれば、
ライフが3の時の条件式を書く必要性は一切ありませんね。
それではライフが0になったときにゲームループを脱出する条件を作ってみましょう。
' ライフを500に設定
VAR LIFE = 500
' ループ開始
LOOP
' もし ライフが 0 になったときはループを抜ける
IF LIFE == 0 THEN BREAK
' ライフを1つ減らす
LIFE = LIFE - 1
' 現在のライフの数値を表示
PRINT LIFE
' 垂直同期
VSYNC
ENDLOOP
' ゲームオーバー表示
PRINT "GAME OVER"
今回は意味が分かりやすいようにそれぞれの処理にコメントを付けてみました。
コメントはシングルクォート「’」を先頭に置くことによってコメントとみなされます。
キーボードで言うとSHIFTを押しながら数字の7を押すと打ち込めます。
コードの中身を説明すると、変数名WORDをLIFEに変えています。
数値が少ないと一瞬で終わってしまうので500と多めに設定しています。
そしてループに突入し、まずはライフの監視です。
もしライフが0になったらBREAKという命令を実行して、ループを強制的に抜けます。
ライフが0になっていなければそのまま処理を進めます。
ライフを1つ減らして、また最初のループに戻ります。
そしてまたライフを関して0かどうかを調べる。
以上の繰り返しです。
ライフが0になってBREAKという命令が動いた場合、
移行の処理を無視してループの外側に処理がうつります。
つまり最後に書いてある「GAME OVER」のPRINT命令が動くわけですね。
そしてそれ以降何もないのでプログラムが終了します。
条件処理に関してはこれで終わりです。
コントローラー入力処理を作ってみる
このままではゲームとは言えないので、コントローラーで動かせるようにしてみます。
といってもコントローラーの処理って思った以上に複雑で、
初心者の人には意味が分からないと思うのでコピペで使えるようにしてみました。
(コピペと書いてますがPCからプチコン4にコピペはできないとおもうので1語1句そのまま打ち込んでください。)
def D_Controller
から
end
までがコントローラーの処理の塊です。
' ライフを500に設定
VAR LIFE = 500
' 向き
VAR DIRECT = 0
' 相手の向いている
VAR ENEMY_DIRECT = 0
' コントローラー命令
'───────────────────────────────
def D_Controller
' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
VAR B = BUTTON(0)
' Xボタン処理
if (B AND 1 << #B_RUP) != 0 then
DIRECT = 0
PRINT "Xがおされているよ"
' Bボタン処理
elseif (B AND 1 << #B_RDOWN) != 0 then
DIRECT = 1
PRINT "Bがおされているよ"
' Yボタン処理
elseif (B AND 1 << #B_RLEFT) != 0 then
DIRECT = 2
PRINT "Yがおされているよ"
' Aボタン処理
elseif (B AND 1 << #B_RRIGHT) != 0 then
DIRECT = 3
PRINT "Aがおされているよ"
endif
end
' ループ開始
LOOP
' コントローラー命令の実行
D_Controller
' もし ライフが 0 になったときはループを抜ける
IF LIFE == 0 THEN BREAK
' ライフを1つ減らす
LIFE = LIFE - 1
' 現在のライフの数値を表示
PRINT LIFE
' 垂直同期
VSYNC
ENDLOOP
' ゲームオーバー表示
PRINT "GAME OVER"
一気にコードが増えて混乱するかもしれませんが、コントローラー命令の仕組みは結構単純で
- 現在のボタンの状態を取得
- それぞれのボタンがどれか押されていたら押されているボタンの文字を表示する
というシンプルな仕組みです。
更に、あっち向いてホイに使う向き用の変数をプレイヤー用と相手用の2つを作っています。
ボタンが押されたらプレイヤー用の向きの変数が書き換わるようになっているので
相手側はランダムで向きを変えるようにすればあっち向いてホイの基本的な仕組みは完成です。
しかし現状はボタンを押せるだけであって特にゲームには影響していません。
乱数を使って一つのゲームにしてみよう
折角ボタンで向きを変更できるので、相手の向きもランダムで変更してみましょう。
ランダムな数値を発生させるのはとても簡単です!
ENEMY_DIRECT = RND(4)
これだけですね。
ENEMY_DIRECTは最初の方に宣言しているので、
それにRND関数を使って0~3の数値をランダムで取得しています。
RNDのカッコの中にある数値は発生させる個数です。
プログラミングは基本的に0から数字が始まるので
0・1・2・3
で4つですね。
4は含まれないので注意してください。
これをループの中に組み込みます。
一旦すべてのコードをまとめてみましょう。
' ライフを500に設定
VAR LIFE = 500
' 向き
VAR DIRECT = 0
' 相手の向いている
VAR ENEMY_DIRECT = 0
' コントローラー命令
'───────────────────────────────
def D_Controller
' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
VAR B = BUTTON(0)
' Xボタン処理
if (B AND 1 << #B_RUP) != 0 then
DIRECT = 0
PRINT "Xがおされているよ"
' Bボタン処理
elseif (B AND 1 << #B_RDOWN) != 0 then
DIRECT = 1
PRINT "Bがおされているよ"
' Yボタン処理
elseif (B AND 1 << #B_RLEFT) != 0 then
DIRECT = 2
PRINT "Yがおされているよ"
' Aボタン処理
elseif (B AND 1 << #B_RRIGHT) != 0 then
DIRECT = 3
PRINT "Aがおされているよ"
endif
end
' ループ開始
LOOP
' 相手の向きをきめる
ENEMY_DIRECT = RND(4)
' コントローラー命令の実行(プレイヤーの向きをきめる
D_Controller
' もしプレイヤーの向きと相手の向きが同じなら
IF ENEMY_DIRECT == DIRECT THEN
' ライフを1つ減らす
LIFE = LIFE - 1
ENDIF
' もし ライフが 0 になったときはループを抜ける
IF LIFE == 0 THEN BREAK
' 現在のライフの数値を表示
PRINT LIFE
' 垂直同期
VSYNC
ENDLOOP
' ゲームオーバー表示
PRINT "GAME OVER"
少し処理を加えました。
流れとしては
- お互いの向きを決定する
- 向きが同じならプレイヤーの勝ち
- プレイヤーが勝ったら相手のライフを削る
- ライフを調べて0じゃなければ最初に戻る
- 繰り返し
- ライフが0になったらループを抜けてゲーム終了
一旦プレイヤーは無敵にしてライフを敵のモノとしてみましょう。
ライフを全て削り切ったら勝ちです。
一度実行してみましょう。
どうでしょうか?
たぶんものすごく不満があると思います。
というのもコントローラーの制御が無い状態なのでこのまま実行すると、
1回ボタンを押したらまるで連打されているかのような状態になります。
本来はこれにボタンの制御を加えなきゃいけません。
ボタンを押しっぱなしで動いてしまうはあっち向いてホイゲームとしては欠陥品ですね。
シューティングだといいんですが、1回1回の判断が重要なゲームの場合は致命的過ぎます。
今回はココまでです。
次回はコントローラー処理を改善してみましょう。
今回使ったプチコンの命令一覧
- 変数
- IF(条件式) THEN – ELSEIF(条件式) – ELSE – ENDIF
- LOOP – ENDLOOP
- BREAK
- VSYNC
最初にしてはちょっと多いですね……
もうちょっと絞り込んでやろうかと思ったのですが
ゲームとして成立させるためには最低限これらが必要になってしまいました。
コントローラ命令に関しては今は無視しておいてOKです。
次回詳しく説明します。
今は意味が分からなくても最初は書き写すだけでOKです。
書き写して動かせるようになれれば上出来です!
意味なんて後から知ればいいだけなので、
まずは手段を問わずコードを書いて動かせるようになればOKです!
基本的にミニゲームは3回の講座で完結を目指してみます。
もしかすると内容によっては5回とかいくかもしれませんが、
なるべく短い記事数で完結させようと思います。
それでは第1回目はこの辺で終わりましょう。