状態遷移とゲームループ

次に、タイトル画面をクリックしたら⇒プレイ画面、プレイ画面をクリックしたら⇒クリア画面、クリア画面をクリックしたら⇒タイトル画面。。。とループできる仕組みを作っちゃおう

この3つの画面をとりあえず作って表示する。
今、まだ状態の切り替え処理を書いていないので、無理やり状態をPLAY、CLEARに変更して表示させる。
TitleDraw();、PlayDraw();、ClearDraw();の宣言と定義は書いたし、状態が遷移した際にそれを呼び出すswitch-case文はもう書いてあるので、状態さえ変われば呼び出される関数が変わるはずである。

"状態の無理やり切り替え"
//ゲーム状態の変数と、状態の初期化
GAME_STATE state = GAME_STATE::TITLE;
//プレイ画面を呼び出す
//GAME_STATE state = GAME_STATE::PLAY;
//クリア画面を呼び出す
//GAME_STATE state = GAME_STATE::CLEAR;

これを直接書き換えて、switch-case文に突入することでプレイ画面とクリア画面が正しく呼び出されるか確認できる。

次に、クリックしたら、次の状態に移動するって処理を書いてみます。
今ある関数は、

void TitleUpdate();
void PlayUpdate();
void ClearUpdate();
void TitleDraw();
void PlayDraw();
void ClearDraw();

だが、○○Drawは、現在の盤面や画面の描画処理のみを、入力や変数の変更は○○Updateのみで行うようにすることで、プログラムが作りやすくなる。
(決めたら、そのルールを破らないのは大事!)
現在描画処理はできているので、各Update関数でクリックしたときに、状態を移行する処理を書いていけば、今回の目的を達成できる。
とりあえず、タイトル⇒プレイ画面の移行を書いてみよう。
画面のクリックは、授業でやった通り MouseL.pressed()でtrue,falseで取得できる。
後は、押されたときに次の状態に移行してやればいい。
stateはグローバル変数にしといたから、どこからでもアクセスできるよ!

"クリックの検出"
void TitleUpdate()
{
	if (MouseL.pressed())
	{
		ステート(state)をプレイ(GAME_STATE::PLAY)に移行
	}
}

書いたら動作させてみて、タイトル画面からプレイ画面に移行できたか確認しよう!
同じようにプレイ画面⇒クリア画面、クリア画面⇒タイトル画面、の移行も書いてみよう

実際のゲーム内での状態移行

これができたら、ゲーム状態のループはほぼ完成。
実際のゲーム内ではクリックで移行ではなく、例えば、

  • スタートの文字を押したら、プレイ画面へ移行してゲーム開始
  • パズルが完成したら、クリア画面に移行
  • クリア画面で10秒おめでとうメッセージを表示したらタイトルに戻る

などの方法で状態を移行していくことになる。
早速スタートボタンを作ってみよう!
ゲームタイトル(THE SLIDE PUZZLE)の下の「Push~」を押したらゲーム開始するように書き換えていこう。
文字をクリックしたら。。。は、授業で取り扱ったのでみんなできるはず。
でも、やってみると困ったことになるんだよね。。。

"スタートボタンクリックでプレイ画面へ"
void TitleDraw()
{
    省略
        //"スタートボタンになる文字列"を表示
	if (FontAsset(U"BUTTON_FONT")(StartStr).drawAt(TextStyle::Outline(0.2, Palette::Black), startPoint + ajustSpace, Palette::White).leftClicked())
	{
		state = GAME_STATE::PLAY;
	}
}

あぁ。。。Update以外で、状態の移行書いちゃった。
こうなると、関数の役割分担と、プログラムの構成がめっちゃくっちゃになります。(2,3年生になって、実際にこんなことになってる人も多いです)

無理やりだけど解決策

もう少しC++の授業が進んで、classとか覚えるとこういう問題に対処しやすくなりますが、今回はスタートボタンの存在する領域をRect型で取得して、
そんで、そのRect領域(矩形=四角形)の中をクリックしたら状態遷移をする。
なので、Rect startButtonRec;を「グローバル変数」で用意、Draw内でタイトル文字の領域を計算してstartButtonRecにぶち込む(Draw内ではこれだけ)、UpdateでstartButtonRecのクリック検出⇒状態遷移
という流れ。
さあやってみよう!

Fontの描画領域の四角形は Font(ごにょごにょ).draw().asRect()って、やると取得できるので、これをつかってゆくぅ
Rectは.leftClicked()とかで、true,falseで

"UpdateにはUpdateの仕事がある"
省略
    //ゲーム状態の変数と、状態の初期化
    GAME_STATE state = GAME_STATE::TITLE;
    Rect StartButtonRect;
省略
void TitleUpdate()
{
	if (スタートボタン押したら)
	{
		ステートをPLAYに移行
	}
}
省略
void TitleDraw()
{
省略	//ボタンの描画領域を取得
	StartButtonRect = FontAsset(U"BUTTON_FONT")(StartStr).drawAt(TextStyle::Outline(0.2, Palette::Black),
											 startPoint + ajustSpace, Palette::White).asRect();
}

これで、

  • Drawは描画処理と、領域の取得のみ、(ほんとは描画のみがいいんだけどね、最大限の譲歩)
  • Updateは、状態や変数の更新処理

の仕事わけがさっきよりはうまくいってるはず!
さてやってみよう!

次はいよいよプレイ画面を作っていくよ!
プレイ画面でも同じように、UpdateとDrawの仕事をうまく分けて書いていくことを心がけよう!

その3 プレイ画面の作成へ

  • game-engineer/classes/2023/something-else/summertime-special-cource/slidepuzle-siv3d-2.txt
  • 最終更新: 5カ月前
  • by root