【プチコン4講座】クリアフラグを実装しよう

プチコン4

プチコン4 パズルゲーム クリアフラグ実装

こんにちは。なおキーヌです。

ブログ毎日更新は265日目になります。

プレイヤーの移動・壺の移動・壁の実装・各種ぶつかり判定を実装してきました。

ゲームの仕組みとして最後になるのはクリアフラグの実装ですね。

クリアフラグの管理は数値の変数を用意して管理していきます。

一定の数になったらステージクリアという仕組みにしましょう。

思ったより楽な処理方法を見つけてしまったので、少し短い記事になってしまいますがご了承ください。

それではプチコンミニゲーム講座第14回目を始めようと思います。

  1. ダイヤ印の上に壺が乗るとクリアフラグを立たせる
  2. クリアフラグを満たすとステージクリア
  3. 後はステージ増やして作っていくだけ

ダイヤ印の上に壺が乗るとクリアフラグを立たせる

現状、クリアフラグであるダイヤ印のスプライトはなにも判定を行っていません。

今まではプレイヤーに対して壁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

クリアシーンを用意していないので一旦ゲームループを抜けることにしました。

今回は倉庫番ライクゲームを作るという名目なので、
ゲームの仕組み以外は割愛させていただくことをお許しください。

後はステージ増やして作っていくだけ

少し短いですが、これで倉庫番ライクの基本的な仕組みは完成しました。

後はステージを作ってステージごとにゴールポイントと壺の数を調整したりしていかなければいけません。

こればかりはマップエディタが無いと少々面倒臭いので、
倉庫番ライクゲームの作り方については今回で最終回となります。

後日、気が向いたらソースコードを最適化してみようと思います。

その時は関数化のお勉強の記事にするかもしれません。

あ、そういえばテキストスクリーンについて触れる前に終わってしまいましたね……

壁のスプライトが思ったよりもいい感じだったので、
別に床黒くても問題ないと思ったので次回のミニゲームに回すことにしました。

最後に完成版のソースコードを載せておきます。

それでは

' 画面クリア
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