Developer

【Unity実践】#9 プロトタイプ編 ~ シーンの制御 part1 ~【Boxゲーム】
2021.02.02
Lv2

【Unity実践】#9 プロトタイプ編 ~ シーンの制御 part1 ~【Boxゲーム】

今回の内容

今回からはシーンの制御の仕組みを作成します。
クリア後に次のステージへ遷移する、ゲームオーバー時にリトライするといった機能を実装していきます。

改めての注意書きとなりますが、ステージ遷移関連の仕組みは実際のゲームで実装している仕組みとは異なっています。
ここで紹介するのは実装方法の1例として見て頂ければと思います。

※初めての方はこちらから
【第1回記事】この連載について

シーン制御の設計

いきなりスクリプト作成に入る前に、今回のシーン制御の仕様を簡単に整理します。

1.シーン名の命名ルール

「GameScene」+「ステージ番号」
例)GameScene1, GameScene2, …

2.ステージの読み込み順序

1周目は順番に選択、2周目以降はランダムとする。

例えば10ステージあるとき、
▼ステージ1-10
GameScene1
GameScene2
GameScene3

GameScene10
▼11以降
全シーンの中からランダムで選択

3.シーン番号の管理

PlayerPrefsを使って管理する。
その際、以下の2つのパラメータを使用。
StageNum ⇒ 現在何ステージ目かを管理
SceneNum ⇒ 読み込むシーンの番号を管理(GameScene1の「1」の箇所)

なぜ2つ用意しているかと言うと先ほどの2の仕様関連で、
1周目は「読み込むシーン番号(SceneNum)= 現在のステージ数(StageNum)」となるが、
2周目以降は両者がズレて行くので、別々のパラメータで管理することとしています。

上記3つの仕様を前提として、この後の実装を進めていきます。

シーンを読み込む

新規スクリプトを追加して、名前は「StageControllerScript」としましょう。

まずは以下を記述します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class StageControllerScript : MonoBehaviour
{
    public const string GAME_SCENE_NAME = "GameScene"; // シーン名のプレフィクス
    public const int GAME_SCENE_COUNT = 1;             // シーンの数

    private int stageNum;    // 現在のステージ数
    public Text stageName;      // 現在のステージ名("Stage" + stageNum)

    void Start()
    {
        // ステージ数(画面表示用 ※実際のシーン名とは必ずしも対応しない!)
        stageNum = PlayerPrefs.GetInt("StageNum", 1);
        stageName.text = "Stage" + stageNum;

        // 自分のシーンではない場合、ロードし直す
        if (GetLoadSceneName() != SceneManager.GetActiveScene().name)
        {
            LoadScene();
        }
    }

    void Update()
    {
        
    }

    // ロードするシーン名を取得
    private string GetLoadSceneName()
    {
        int loadSceneNum = PlayerPrefs.GetInt("SceneNum", 1);
        return GAME_SCENE_NAME + loadSceneNum;
    }

    // ステージを読み込む処理(現在のstageNumに対応するステージ)
    private void LoadScene()
    {
        SceneManager.LoadScene(GetLoadSceneName());
    }
}

記述ができたら、シーンに空のオブジェクトを配置して名前を「StageController」としましょう。
このオブジェクトに、StageControllerScript をアタッチしてください。

さらにインスペクタービューで、StageName に Canvas > StageName をドラッグして下さい。

この Canvas > StageName って何だったかと言うと、第2回目の記事で作成した、
画面の上部に表示するステージ名のUIでした。

順に解説していきます。

9, 10行目:定数の定義

それぞれ定数を定義しており、内容はソース内コメントの通りです。
シーンの数は現在は1としていますが、今後ステージを増やしたときにはここを直接変更します。

12, 13行目:プロパティの定義

こちらは通常のプロパティとして定義しており、内容はやはりコメントの通りです。
StageNum は先ほどの仕様3.の PlayerPrefs と対応するプロパティです。

15~26行目:Startメソッド

はじめに18行目で、現在のステージ数(StageNum)を PlayerPrefs から取得しています。
取得した番号と「Stage」の文字列を結合して、StageName の text プロパティに設定しています。
StageName は画面上部の UI だったので、ここでその表示を設定している形となります。

Startメソッド内の最後にある、22~25行目の処理ですが、
現在のシーンが本来読み込むシーンではない場合に、正しいシーンを読み込み直す処理となっています。
タスクキル等でアプリを終了・再開すると GameScene1 が読み込まれてしまうため、
PlayerPrefs を参照して正しいステージを読み直しています。

34~38行目:GetLoadSceneNameメソッド

その時点で読み込むべきシーン名を取得するメソッドを作成しています。
読み込むシーンの番号は、PlayerPrefs の SceneNum で管理しています。(仕様の3つ目)
この処理は複数の箇所で実行される想定のもと、メソッドとして作成しました。
(現状だと22行目と43行目で使用しています。)

41~44行目:LoadSceneメソッド

シーンを読み込む処理をメソッド化しています。

=====

この後もスクリプトを追記していきますが、まずはここまでの処理を理解してください。
特に大事なことは、以下の2点です。


・LoadSceneメソッドを呼び出すとその時点で読み込むべきシーンが読み込まれる
・読み込むシーンの番号は、PlayerPrefs の SceneNum で管理している

クリア、ゲームオーバーの制御

続いて、クリアやゲームオーバーと言ったゲームの状態を管理するための機能を追加していきます。
まずやることは大きく2つで、
・フラグを追加(クリア、ゲームオーバーそれぞれ)
・フラグの状態によって、UIの表示を切り替える(クリアUI、リトライUIを表示)

ひとまずはここまでを作成します。

StageControllerScript に以下を追加してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class StageControllerScript : MonoBehaviour
{
    public const string GAME_SCENE_NAME = "GameScene"; // シーン名のプレフィクス
    public const int GAME_SCENE_COUNT = 1;             // シーンの数

    private int stageNum;    // 現在のステージ数
    public Text stageName;      // 現在のステージ名("Stage" + stageNum)

    public static bool isClear;   // クリアフラグ
    public static bool isGameOver;   // ゲームオーバーフラグ

    public GameObject clearCanvas;   // クリア時のUI
    public GameObject retryCanvas;   // ゲームオーバー時のUI

    void Start()
    {
        // ステージ数(画面表示用 ※実際のシーン名とは必ずしも対応しない!)
        stageNum = PlayerPrefs.GetInt("StageNum", 1);
        stageName.text = "Stage" + stageNum;

        // 自分のシーンではない場合、ロードし直す
        if (GetLoadSceneName() != SceneManager.GetActiveScene().name)
        {
            LoadScene();
        }

        // 初期化
        isClear = false;
        isGameOver = false;
    }

    void Update()
    {
        // クリアしたとき
        if (isClear)
        {
            ShowClearCanvas();
            enabled = false;
        }
        // ゲームオーバーのとき
        else if (isGameOver)
        {
            ShowRetryCanvas();
            enabled = false;
        }
    }

    // クリアUIを表示する
    private void ShowClearCanvas()
    {
        clearCanvas.SetActive(true);
    }

    // リトライUIを表示する
    private void ShowRetryCanvas()
    {
        retryCanvas.SetActive(true);
    }

    // ロードするシーン名を取得
    private string GetLoadSceneName()
    {
        int loadSceneNum = PlayerPrefs.GetInt("SceneNum", 1);
        return GAME_SCENE_NAME + loadSceneNum;
    }

    // ステージを読み込む処理(現在のstageNumに対応するステージ)
    private void LoadScene()
    {
        SceneManager.LoadScene(GetLoadSceneName());
    }
}

追記ができたら、インスペクターから
ClearCanvas に Canvas > Clear
RetryCanvas に Canvas > Retry
をそれぞれ設定します。

では追記したスクリプトを見ていきましょう。
ポイントは、「isClear」「isGameOver」の2つのゲーム状態を管理する変数です。
追加した処理、各メソッドはこの2つに関連して動く事となり、

isClear が true ⇒ ShowClearCanvas(クリア時UIが表示される)
isGameOver が true ⇒ ShowRetryCanvas(リトライ時UIが表示される)

となっています。
ここまでできるとシーン制御の完成まであと一歩で、残りの実装としては、

・クリア画面で Nextボタンをタップすると次のシーンを読み込む
・リトライ画面で Retryボタンをタップすると現在のシーンを読み込む

これらの処理を作成するのみとなります。
今回の記事ではこの実装まではせず、次回紹介していくのですが、
実装方法としては先に作成した LoadSceneメソッドPlayerPrefs の SceneNum を利用します。

宿題がてら、少し実装方法を考えてみて頂くと良さそうです。

デバッグUIの制御

デバッグUIの表示切替も、StageControllerで管理するようにします。
StageControllerScript に以下を追加してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class StageControllerScript : MonoBehaviour
{
    public const string GAME_SCENE_NAME = "GameScene"; // シーン名のプレフィクス
    public const int GAME_SCENE_COUNT = 1;             // シーンの数

    private int stageNum;    // 現在のステージ数
    public Text stageName;      // 現在のステージ名("Stage" + stageNum)

    public static bool isClear;   // クリアフラグ
    public static bool isGameOver;   // ゲームオーバーフラグ

    public GameObject clearCanvas;   // クリア時のUI
    public GameObject retryCanvas;   // ゲームオーバー時のUI

    // デバッグ用
    private bool isDebug = true;     // デバッグフラグ
    public GameObject debugCanvas;   // デバッグメニューのUI

    void Start()
    {
        // ステージ数(画面表示用 ※実際のシーン名とは必ずしも対応しない!)
        stageNum = PlayerPrefs.GetInt("StageNum", 1);
        stageName.text = "Stage" + stageNum;

        // 自分のシーンではない場合、ロードし直す
        if (GetLoadSceneName() != SceneManager.GetActiveScene().name)
        {
            LoadScene();
        }

        // 初期化
        isClear = false;
        isGameOver = false;

        // デバッグモード表示の切り替え
        debugCanvas.SetActive(isDebug);
    }

    // 以下省略

クリアやリトライと同様に、DebugCanvasに Canvas > Debug を設定しましょう。

デバッグUI の Retryボタンや Clearボタンをタップした時の処理も次回作成しますが、
実装方法としてはクリア画面やリトライ画面と同様になるので、こちらも宿題がてら考えてみて下さい。

また、まだしばらくはデバッグUI は表示したままとしますので、isDebug の値は true としておきます。(22行目)

おわりに

今回はシーン制御の実装の1回目と言うことで、ボタンを押す手前までの処理を作成しました。
目に見えない箇所の作成だったことと PlayerPrefs等の設計に絡んだ実装がやや難易度が高めかと思いますので、
理解できるまでじっくり読んでみて下さい。

次回は各種ボタンとの関連付けを行って、シーン制御の機能が完成する予定ですのでお楽しみに!

 
 

連載目次リンク

実践Unityゲームプログラミング 連載目次
 

関連する連載リンク

「初心者のための」Unityゲーム制作 目次
 

© Unity Technologies Japan/UCL