【プチコン4講座】クリアフラグを実装しよう
こんにちは。なおキーヌです。
ブログ毎日更新は265日目になります。
プレイヤーの移動・壺の移動・壁の実装・各種ぶつかり判定を実装してきました。
ゲームの仕組みとして最後になるのはクリアフラグの実装ですね。
クリアフラグの管理は数値の変数を用意して管理していきます。
一定の数になったらステージクリアという仕組みにしましょう。
思ったより楽な処理方法を見つけてしまったので、少し短い記事になってしまいますがご了承ください。
それではプチコンミニゲーム講座第14回目を始めようと思います。
ダイヤ印の上に壺が乗るとクリアフラグを立たせる
現状、クリアフラグであるダイヤ印のスプライトはなにも判定を行っていません。
今まではプレイヤーに対して壁or壺・壺に対して壁or壺とややこしかったのですが、
クリアフラグに関して動きませんし、壺としか判定を行わないのでココまでやってきた人はもう楽勝ではないでしょうか?
早速クリアフラグと壺の判定を取ってみましょう。
' クリアフラグストック用
VAR CLEAR_FLAG = 0
' クリア判定数値
VAR GOAL_POINT = 3
' クリアフラグ全てが壺に触れているかどうかを調べる
FOR G_I = 11 TO 10+GOAL_POINT
IF (SPHITSP(G_I,1,GOAL_POINT) == -1)
' どれか1つでも触れていなければクリアフラグをリセットしてループを抜ける
CLEAR_FLAG = 0
BREAK
ELSE
' 壺がゴールに触れていたら加算
INT CLEAR_FLAG
ENDIF
NEXT
基本的な処理はこれで完成です。
本当は壺を1つクリアフラグに乗せたらクリアポイントが加算されてっていうのを
書こうと思っていたのですが、加算するフラグの処理に迷ってしまいました。
よく考えたら壺とゴールの数は同じですしゴールの数も変数に組み込んでいるので、
ループで全てのクリアフラグが壺にぶつかっているかチェックするだけでよかったのです。
なのでゴールポイント分forで回し、ぶつかっていたらポイント加算。
1つでもぶつかっていなかったらポイントリセットしてループを抜ける。
といった感じですね。
最後にポイントを調べてクリアフラグに満たしていたらステージクリアにできます。
クリアフラグを満たすとステージクリア
先ほども言ったようにクリアフラグの衝突判定を抜けてから、
ループ後にフラグチェックをして条件を満たしていたらゲームクリアとします。
' ゲームクリア状態であればメインループから抜けてゲームEND
IF CLEAR_FLAG >= GOAL_POINT THEN THEN
BEEP 71
BREAK
ENDIF
クリアシーンを用意していないので一旦ゲームループを抜けることにしました。
今回は倉庫番ライクゲームを作るという名目なので、
ゲームの仕組み以外は割愛させていただくことをお許しください。
#petitcom #プチコン4 #NintendoSwitch
ソウコバンライクゲーム
クリアフラグ実装でとりあえず完成ここからのクオリティアップは君たちしだいだ!!(丸投げ pic.twitter.com/nmWzPq8H35
— なおキーヌ@ゲームクリエイターLv5 (@naokeyzmt) September 21, 2019
後はステージ増やして作っていくだけ
少し短いですが、これで倉庫番ライクの基本的な仕組みは完成しました。
後はステージを作ってステージごとにゴールポイントと壺の数を調整したりしていかなければいけません。
こればかりはマップエディタが無いと少々面倒臭いので、
倉庫番ライクゲームの作り方については今回で最終回となります。
後日、気が向いたらソースコードを最適化してみようと思います。
その時は関数化のお勉強の記事にするかもしれません。
あ、そういえばテキストスクリーンについて触れる前に終わってしまいましたね……
壁のスプライトが思ったよりもいい感じだったので、
別に床黒くても問題ないと思ったので次回のミニゲームに回すことにしました。
最後に完成版のソースコードを載せておきます。
それでは
' 画面クリア
ACLS
' チップサイズ定数
CONST #CS = 16
' ステージの左上座標
CONST STAGE_ORIGIN_X = 0 * #CS
CONST STAGE_ORIGIN_Y = 0 * #CS
' ゴールと木箱の個数
VAR OBJ = 5
' クリアフラグストック用
VAR CLEAR_FLAG = 0
' クリア判定数値
VAR GOAL_POINT = 3
' ループ用変数
VAR G_I = 0, G=J = 0
' 描写カウント
VAR G_WALL_DRAW_COUNT = 0
VAR G_JAR_DRAW_COUNT = 0
VAR G_GOAL_DRAW_COUNT = 0
' マップの縦横サイズ
VAR G_BGW = 9
VAR G_BGH = 9
' マップデータ
DIM MAPDATA[] = [\
1,1,1,1,1,0,0,0,0,\
1,0,0,0,1,0,0,0,0,\
1,0,2,2,1,0,1,1,1,\
1,0,2,0,1.0.1.3.1,\
1,1,1,0,1,1,1,3,1,\
0,1,1,0,0,0,0,3,1,\
0,1,0,0,0,1,0,0,1,\
0,1,0,0,0,1,1,1,1,\
0,1,1,1,1,1,0,0,0\
]
' ボタンカウント用変数
VAR UP_BTN = 0, DOWN_BTN = 0, LEFT_BTN = 0, RIGHT_BTN = 00
' 衝突判定用変数
VAR HIT_OBJ = -1
' プレイヤースプライトの準備
SPSET 0, 721
' スプライト管理番号0番目に「X」という名前の変数を宣言し、0を代入する
SPVAR 0, "X", #CS
' スプライト管理番号0番目に「Y」という名前の変数を宣言し、0を代入する
SPVAR 0, "Y", #CS
' スプライトの向き情報を設定
SPVAR 0, "DIRECT", 0
' スプライト衝突判定の準備
SPCOL 0
' 壺スプライトの準備
FOR G_I = 1 TO OBJ
SPSET G_I, 265
SPVAR G_I, "X", G_I * #CS
SPVAR G_I, "Y", 0
SPCOL G_I
SPOFS 0, SPVAR(G_I,"X"), SPVAR(G_I,"Y")
NEXT
' 壺を置くための目印スプライト準備
FOR G_I = 11 TO OBJ+10
SPSET G_I, 224
SPVAR G_I, "X", G_I * #CS
SPVAR G_I, "Y", 0
SPCOL G_I
SPOFS 0, SPVAR(G_I,"X"), SPVAR(G_I,"Y")
NEXT
' 壁用スプライト
FOR G_I = 51 TO OBJ+200
SPSET G_I, 1469
SPVAR G_I, "X", G_I * #CS
SPVAR G_I, "Y", 0
SPCOL G_I
SPOFS 0, SPVAR(G_I,"X"), SPVAR(G_I,"Y")
NEXT
' マップ描写
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]
' 1(壁)のとき
WHEN 1:
' 壁スプライトを設置
SPOFS 51+G_WALL_DRAW_COUNT, (G_I * #CS) +STAGE_ORIGIN_X ,(G_J * #CS) +STAGE_ORIGIN_Y
' 次のスプライト管理番号を使うためにカウント
INC G_WALL_DRAW_COUNT
' 2(壺)のとき
WHEN 2:
' 壺スプライトを設置
SPOFS 1+G_JAR_DRAW_COUNT, (G_I * #CS) +STAGE_ORIGIN_X ,(G_J * #CS) +STAGE_ORIGIN_Y
' 壺は動かすので座標を記憶しておく
SPVAR 1+G_JAR_DRAW_COUNT, "X", (G_I * #CS) +STAGE_ORIGIN_X
SPVAR 1+G_JAR_DRAW_COUNT, "Y", (G_J * #CS) +STAGE_ORIGIN_Y
' 次のスプライト管理番号を使うためにカウント
INC G_JAR_DRAW_COUNT
' 2(壺)のとき
WHEN 3:
' ゴールスプライトを設置
SPOFS 11+G_JAR_DRAW_COUNT, (G_I * #CS) +STAGE_ORIGIN_X ,(G_J * #CS) +STAGE_ORIGIN_Y
' 次のスプライト管理番号を使うためにカウント
INC G_GOAL_DRAW_COUNT
ENDCASE
NEXT
NEXT
' ループ開始
LOOP
D_Controller
' クリアフラグ全てが壺に触れているかどうかを調べる
FOR G_I = 11 TO 10+GOAL_POINT
IF (SPHITSP(G_I,1,GOAL_POINT) == -1)
' どれか1つでも触れていなければクリアフラグをリセットしてループを抜ける
CLEAR_FLAG = 0
BREAK
ELSE
' 壺がゴールに触れていたら加算
INT CLEAR_FLAG
ENDIF
NEXT
' ゲームクリア状態であればメインループから抜けてゲームEND
IF CLEAR_FLAG >= GOAL_POINT THEN THEN
BEEP 71
BREAK
ENDIF
VSYNC
ENDLOOP
' コントローラー関数
'───────────────────────────────
def D_Controller
' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
VAR B = BUTTON(0)
' 上ボタン処理
if (B AND 1 << #B_LUP) != 0 then
D_BtnPressCount #B_LUP
else
if UP_BTN >= 1 then
UP_BTN = 0
endif
endif
' 下ボタン処理
if (B AND 1 << #B_LDOWN) != 0 then
D_BtnPressCount #B_LDOWN
else
if DOWN_BTN >= 1 then
DOWN_BTN = 0
endif
endif
' 左ボタン処理
if (B AND 1 << #B_LLEFT) != 0 then
D_BtnPressCount #B_LLEFT
else
if LEFT_BTN >= 1 then
LEFT_BTN = 0
endif
endif
' 右ボタン処理
if (B AND 1 << #B_LRIGHT) != 0 then
D_BtnPressCount #B_LRIGHT
else
if RIGHT_BTN >= 1 then
RIGHT_BTN = 0
endif
endif
' プレイヤー移動
D_PLAYER_MOVE
end
' プレイヤー移動関数
'───────────────────────────────
DEF D_PLAYER_MOVE
' 仮移動用配列
DIM ADD_POS[] = [0,0]
' ボタンカウントに応じて向きを変更と仮移動数値を設定
IF UP_BTN == 1 THEN
SPVAR 0, "DIRECT", #B_LUP
ADD_POS[1] = -#CS
ELSEIF DOWN_BTN == 1 THEN
SPVAR 0, "DIRECT", #B_LDOWN
ADD_POS[1] = #CS
ELSEIF LEFT_BTN == 1 THEN
SPVAR 0, "DIRECT", #B_LLEFT
ADD_POS[0] = -#CS
ELSEIF RIGHT_BTN == 1 THEN
SPVAR 0, "DIRECT", #B_LRIGHT
ADD_POS[0] = #CS
ENDIF
' プレイヤーを仮移動させる
SPOFS 0, SPVAR(0,"X")+ADD_POS[0], SPVAR(0,"Y")+ADD_POS[1]
' プレイヤーと壁の判定をする
HIT_OBJ = SPHITSP(0,51,200)
' 壁の衝突判定をとる
IF (HIT_OBJ != -1) THEN
' 壁に当たっていればプレイヤーの座標を元に戻す
SPVAR 0, "X", SPVAR(0,"X")
SPVAR 0, "Y", SPVAR(0,"Y")
ELSE
' プレイヤーと壁があたっていなければ壺の衝突判定を取る
HIT_OBJ = SPHITSP(0,1,10)
' 壺に当たっていて居るかチェック
IF HIT_OBJ != -1 THEN
' 壺を仮移動させる
SPOFS HIT_OBJ, SPVAR(HIT_OBJ,"X")+ADD_POS[0], SPVAR(HIT_OBJ,"Y")+ADD_POS[1]
'壺を仮移動させた先は壁かどうか
IF SPHITSP(HIT_OBJ,51,200) != -1 THEN
' BEEP 52
' ぶつかってたら動かせないはずなのでプレイヤーと壺の座標を戻す
SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")
SPOFS HIT_OBJ, SPVAR(HIT_OBJ,"X"), SPVAR(HIT_OBJ,"Y")
ELSE
' 壺を仮移動させた先は壺かどうか
IF SPHITSP(HIT_OBJ,1,10) != -1 THEN
' BEEP 52
' ぶつかってたら動かせないはずなのでプレイヤーと壺の座標を戻す
SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")
SPOFS HIT_OBJ, SPVAR(HIT_OBJ,"X"), SPVAR(HIT_OBJ,"Y")
ELSE
' BEEP 100
' プレイヤーも壺も移動できたので座標変数の変更を確定
SPVAR 0, "X", SPVAR(0,"X")+ADD_POS[0]
SPVAR 0, "Y", SPVAR(0,"X")+ADD_POS[1]
SPVAR HIT_OBJ, "X", SPVAR(HIT_OBJ,"X")+ADD_POS[0]
SPVAR HIT_OBJ, "Y", SPVAR(HIT_OBJ,"Y")+ADD_POS[1]
ENDIF
ENDIF
ELSE
' 壺に当たっていなければプレイヤーは普通に移動するので座標変数を確定
SPVAR 0, "X", SPVAR(0,"X")+ADD_POS[0]
SPVAR 0, "Y", SPVAR(0,"Y")+ADD_POS[1]
ENDIF
ENDIF
' プレイヤーの向きを初期化
SPVAR 0, "DIRECT", 0
END
' ボタンカウント関数
'───────────────────────────────
def D_BtnPressCount A_Button
' ボタンカウント分岐
case A_Button
' 渡されたボタンが上だったら
when #B_LUP
' ボタンカウント配列の数が256未満かどうか調べる
if UP_BTN < 256 then
' 条件に一致したら+1する
INC UP_BTN
endif
' 渡されたボタンが下だったら
when #B_LDOWN
' ボタンカウント配列の数が256未満かどうか調べる
if DOWN_BTN < 256 then
' 条件に一致したら+1する
INC DOWN_BTN
endif
' 渡されたボタンが左だったら
when #B_LLEFT
' ボタンカウント配列の数が256未満かどうか調べる
if LEFT_BTN < 256 then
' 条件に一致したら+1する
INC LEFT_BTN
endif
' 渡されたボタンが右だったら
when #B_LRIGHT
' ボタンカウント配列の数が256未満かどうか調べる
if RIGHT_BTN < 256 then
' 条件に一致したら+1する
INC RIGHT_BTN
endif
endcase
end