【プチコン4講座】ゲームループ実装とスプライト表示とキー操作
こんにちは。継続の錬金術士なおキーヌです。
ブログ毎日更新は151日目になります。
前回「【プチコン4講座】複数レイヤーを持つマップを作ろう」でマップデータをレイヤー別に作りました。
今回はプチコン4でゲームループを実装してスプライトを表示し、それを操作したいと思います。
ゲームループとスプライトはゲーム作りの基本中の基本の講座になるので、
RPG以外を作りたい人にも必ず役に立つというか必須の技術なので覚えておきましょう。
基本的にプチコンBIGでやったことのプチコン4バージョンの記事です。
下記の記事も参照してもらうと、もっと理解が深まると思います。
【プチコン講座】プレイヤーを表示して操作してみよう:ドット移動編
それではプチコン4でRPG作り第3回目を始めましょう。
ゲームループを作ろう
前回までは実行したら即プログラムが終了したと思います。
ゲームループというものを作ってその中でゲームを動かす感じになります。
ゲームループが動き続けていればプログラムが止まることはありません。
市販のゲームも基本的にはこのゲームループの中で色々なことをしています。
今まで書いたコードの一番下に書いてくれればOKです。
関数はどこに書いてもいいので、関数より上に書いても問題ありません。
私は関数はまとめて一番下に置いておくタイプです。
ここは好きにしてくれて構いません。
意味がわからない場合は記事最後に今回作ったコードのまとめを貼り付けているので、
動かない場合は私のコードを真似てください。
それではゲームループを作ってみましょう。
' ゲームループを制御するフラグ
VAR GAME_LOOP_FLAG = TRUE
' ゲームループ
LOOP
PRINT "ゲームループちゅうです"
WAIT 2
IF GAME_LOOP_FLAG THEN BREAK
VSYNC 1
ENDLOOP
他の講座やプチコン公式では「ラベルとGOTO命令」を使ったゲームループを作っていることが多いですが、私のプチコン講座では極力GOTOを使わないように心がけています。
なぜGOTOを使わないかは下記の記事を参照していただけるとよくわかると思います。
【プチコン講座】GOTOを捨ててループと関数でかかって来いよ!
プチコンBIG講座ではWHILEループを使いましたが、
プチコン4ではLOOP ~ ENDLOOPという制御命令が追加されましたのでそれを使います。
本番ではループを抜けることはないですが、一応ループを抜けるためのフラグも用意しました。
それでは実行してみてください。
「ゲームループ中です」
という文字が2秒ごとに表示されていくと思います。
基本的にこの中にプログラムを書いてゲームを作っていきます。
しかしここに全てプログラムを書いていくと見辛くなるので、
ある程度まとまったら関数化してしまいみやすくするのがプログラミングの基本です。
関数別に分けておくと、もしバグが出た時にメンテナンスがしやくなるメリットがあります。
「VSYNC 1」は簡単に言うとゲームのフレームレートを一定化にしてくれるものです。
これはぶっちゃけ今のところ理解する必要性がないのでゲームループの最後に実行するものだと思っておいてください。
詳しく知りたい人は「プチコン VSYNC」でググってみましょう。
スプライトを表示しよう
スプライトとは、要は画像です。
プレイヤーがベタ塗りの四角や丸でもゲームは作れますが、
正直今の時代そんなものが動いていてもゲーム作りのモチベーションが上がりません。
ゲーム作りはモチベーション維持がとても大切です。
私も画像表示をするのが難しくてグラフィック表示で四角や丸をつかってゲームを作ろうとしましたが、モチベーションが維持できずに何度も挫折しました。
プチコン4ではスプライトという便利な機能があって簡単に画像が表示できます。
これを使わない手はありません。
画像も書かなくても公式で用意してくれているのでそれを使いましょう。
スプライトを使用するにはSPSETという命令を使います。
実際に使ってみましょう。
ダイレクトモードでも使えますよ。
SPSET 0,500
左上に赤毛の勇者が登場したと思います。
しかし文字に隠れて見辛いので、移動させてしまいましょう。
SPOFS 0,100,100
これで移動してくれました。
今後はこのキャラクターを動かしていこうと思います。
しかし動かすたびにこの命令を打ち込まないといけないのは大変ですね。
プチコン4はコントローラーがあるのでコントローラーでキャラクターを動かせるようにしたいと思います。
プチコンBIGの講座でも同じことをしましたが、WiiUとSwitchではコントローラーがまったくもって違うものになっています。
1個持ちだったり2個持ちだったりとかなり仕様が変わってしまっているため、
コントローラーに関しては前記事に頼らずしっかり書いていきます。
十字キー操作でスプライトを動かしてみよう
以下、プチコンの対応しているSwitchのコントローラーの持ち方です。
- 0 => フル 携帯モード、Joy-Con2本持ち、Proコントローラー
- 1 => 2本持ち Joy-Con2本持ち
- 2 => 横持ち Joy-Con(L)/(R)横持ち、Proコントローラー
- 3 => 縦持ち Joy-Con(L)/(R)縦持ち
今回作っていくゲームは一人用なので0番目を基準に作っていきます。
まずはコントローラーを認識させるところから始めましょう。
どこのボタンが押されたかを表示する関数を作ろう
プチコン4にはボタンの状態を管理してくれる関数がすでに用意されています。
「BUTTON」関数です。
引数にはコントローラーのIDと機能を何にするか決めることができます。
何ができるかは詳しくは公式リファレンスをみてください。
DEF D_CONTROLLER
VAR B = BUTTON(0)
PRINT B
END
この関数をゲームループにいれて動かしてみください。
何も押していないと0が連続で表示されますが、コントローラーのボタンを押すとよくわからない数字が表示されます。
押されている状態の数値を返してくれているのですが、このままではよくわかりません。
この辺はコンピュータのビット演算的なことを理解していないと理解しづらいです。
勉強していてはゲームが出来上がりませんので、一旦こうしたらキー入力ができるんだと覚えておけばOKです。
2進数や16進数の理解はプログラミングだけではなくゲーム作りにおいても結構重要になるので、勉強しておくとよいでしょう。
それではAボタンを押された時に「Aボタンが押されました」、押されていないときは「はなされています」という文字を出るようにしてみましょう。
プチコン4の公式リファレンスに準じてコードを書きます。
DEF D_CONTROLLER
VAR B = BUTTON(0)
' #B_RRIGHTは右コントローラーの右ボタンつまりAボタンの意
VAR BTN_A = 1 << #B_RRIGHT
IF (B AND BTN_A) != O THEN
PRINT "Aボタンが押されています"
ELSE
PRINT "はなされています"
ENDIF
END
実行してみてAボタンを押したら表示が切り替わります。
さっきも言いましたがなぜ動くかは一旦おいといてください。
今はどのボタンが押されたかわかる状態になればOKです。
離されたらこのようになります。
この仕組みは一回作ってしまえば流用できるので呼び出すだけでいいので、
ゲームを完成させるのに仕組みはあまり気にしなくても大丈夫です。
詳しく知りたい人はやはり2進数と16進数の勉強をしっかりとしましょう。
今回作るのは単純なRPGなので、実際に使うボタンは十字キーとABXYの8つでいいでしょう。
ひとまずキャラクターを十字ボタンで動かせるようにしたいのでこの4つのボタンの制御をしてみましょう。
DEF D_CONTROLLER
VAR B = BUTTON(0)
' #B_RRIGHTは右コントローラーの右ボタンつまりAボタンの意
VAR BTN_UP = 1 << #B_LUP
VAR BTN_DOWN = 1 << #B_LDOWN
VAR BTN_LEFT = 1 << #B_LLEFT
VAR BTN_RIGHT = 1 << #B_LRIGHT
IF (B AND BTN_UP) != O THEN
PRINT "うえ がおされています"
ELSEIF (B AND BTN_DOWN) != O THEN
PRINT "した がおされています"
ELSEIF (B AND BTN_LEFT) != O THEN
PRINT "ひだり がおされています"
ELSEIF (B AND BTN_RIGHT) != O THEN
PRINT "みぎ がおされています"
ELSE
PRINT "はなされています"
ENDIF
END
実行して十字ボタンを押すと対応した文字が出るはずです。
Switchのジョイコンは十字キーではなく十字ボタンなんですよね。
なので上を押しながら下も押せる仕様になっていますので、色々と気をつけなければいけません。
このプログラムを動かすと、全ての十字ボタンを押すと「うえ」が優先的に表示されます。
というのも、プログラムは上から順に実行していくので、全部押されていたら全ての条件に一致することになりますが、一番上に書かれているものが優先されて動きます。
全部同時に認識させたい場合はIF~ELSEIFではなく全部IFにしてみると認識できます。
キャラクターの移動に全部のボタンを認識させる必要性はないのでこのままでいきます。
キャラクターを表示して操作してみよう
スプライトを出す練習はやりましたので、ボタン操作と組み合わせてキャラクターを操作してみましょう。
ついでに一旦ここまでの全てのコードを書きます。
ACLS
'--------------------------
' 変数・配列の定義
'--------------------------
' 変数宣言
VAR G_I = 0, G_J = 0, ¥
G_BGW = 0, G_BGH = 0
' ゲームループを制御するフラグ
VAR GAME_LOOP_FLAG = TRUE
' 空配列宣言
DIM MAPDATA[]
'--------------------------
' プログラム初期化処理
'--------------------------
' マップデータファイル読み込み
MAPDATA = LOADV("DAT:MAP001")
' マップデータ配列の先頭2つを取り出す
G_BGW = SHIFT(MAPDATA)
G_BGH = SHIFT(MAPDATA)
' マップデータの描写
D_MAP_DRAW
' プレイヤーキャラクターの描写と初期化【追加部分】
SPSET 0, 500
SPVAR 0, "X", 0
SPVAR 0, "Y", 0
SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")
'--------------------------
' ゲームループ開始
'--------------------------
LOOP
' コントローラー関数呼び出し
D_CONTROLLER
' ゲームループフラグ監視
IF GAME_LOOP_FLAG THEN BREAK
' フレームレート安定
VSYNC 1
ENDLOOP
'--------------------------
' 以下関数群
'--------------------------
' マップデータの描写関数
DEF D_MAP_DRAW
' マップチップ配列を描写
FOR G_J = 0 TO G_BGH-1
FOR G_I = 0 TO G_BGW-1
' ローカル変数CPに計算した値を入れる
VAR CP = G_I + ( G_J * G_BGW )
' CASE文でMAPDATA[CP]の内容に応じて処理を切り替える
CASE MAPDATA[CP]
' 0のとき
WHEN 0:
' 地面を表示
TPUT 0, G_I, G_J, CHR(&HE8C9)
' 1のとき
WHEN 1:
' 木を表示
TPUT 0, G_I, G_J, CHR(&HE8CA)
ENDCASE
NEXT
NEXT
END
' コントローラー関数
DEF D_CONTROLLER
VAR B = BUTTON(0)
' #B_RRIGHTは右コントローラーの右ボタンつまりAボタンの意
VAR BTN_UP = 1 << #B_LUP
VAR BTN_DOWN = 1 << #B_LDOWN
VAR BTN_LEFT = 1 << #B_LLEFT
VAR BTN_RIGHT = 1 << #B_LRIGHT
IF (B AND BTN_UP) != O THEN
SPVAR 0, "Y", SPVAR(0, "Y") - 1
ELSEIF (B AND BTN_DOWN) != O THEN
SPVAR 0, "Y", SPVAR(0, "Y") + 1
ELSEIF (B AND BTN_LEFT) != O THEN
SPVAR 0, "X", SPVAR(0, "X") - 1
ELSEIF (B AND BTN_RIGHT) != O THEN
SPVAR 0, "X", SPVAR(0, "X") + 1
ENDIF
' キャラクターの移動
SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")
END
実行してもらうと、十字ボタンでキャラクターを動かせると思います。
今は当たり判定をつけていないので自由に動き回れます。
追加した部分の説明をすると
ゲームループの前にスプライトの初期化を行なっています。
SPSETとSPOFSはすでにやりましたね。
SPVARはスプライト専用の変数を利用できるようにします。
SPVARの使い方
設定する時は、SPVAR 【スプライト番号】, 【変数名】, 【設定する値】
値を読みたい時は、SPVAR( 【スプライト番号】, 【変数名】)
で使用できます。
設定する時は()なしで値を読みたいときは()有りと覚えておくといいでしょう。
そして、コントローラーの関数で上下左右の時に+1やら−1やらしていますね。
スプライト変数を増減しています。
そしてコントローラ関数の最後にSPOFSでキャラクターを自身の変数を利用して移動させます。
──キャラクターを操作することができましたが、これはドット移動と言われる移動法で上下左右好きな時に動けます。
今はIFELSEIFENDIFにしていますが、ELSEIFをやめて全部IFにかえてみると斜め移動もできますよ。
ただ、全部のボタンを押すと一切動かなくなりますが(笑)
よくあるRPGはドット移動ではなく、グリッド移動といってマス目単位でうごいていますよね。
今回作るゲームはグリッド移動で完成させていきます。
次回はグリッド移動を実装したいところですが、現在のソースコードをプチコン4で追加された命令などを使ってもうちょっと効率よく書き換えてみたいと思います。
それでは。