top of page

2Dランゲーム編

Lesson5 ステージセレクトを作ろう

5-1

5-1 複数のステージを作成

5-1 複数のステージを作成

1

必要なものを
​プレハブ化する

 ここまでのレッスンでステージ1は一通り完成しました。

 次はステージ2、ステージ3を作成しましょう。

​ ステージを作成する前に、ゲームを実行するために必要なオブジェクトを事前に全てプレハブ化しておきましょう。

 まずはPrefabフォルダ内を整理します。Prefabフォルダ内にGimmickPrefabフォルダを作成して、今まで使ってきたプレハブを入れてください(Ctrlキーを押しながらクリックすると複数選択できます)

 Prefabフォルダ内にGamePrefabフォルダを作成して、その中に今までStage1シーンで使っていたオブジェクトをドラッグ&ドロップしてプレハブ化してください。

 

プレハブ化するオブジェクト:MainCamera、Game、UnityChan、Grid、BackGroundA、BackGroundB、Goal

2

ステージ2
​シーンを作る

 これ以降、プレハブ化したオブジェクトを操作する際は基本的にプレハブ側を操作するので注意してください。

 プレハブ化できたらScenesフォルダ内に新しいシーンStage2を作成してください。

3

メインカメラを
​削除する

 Stage2シーンを開いて、最初からあるMain Cameraを削除しておいてください。

4

必要なオブジェクトを追加する

 Stage2シーン内にGamePrefabフォルダ内のプレハブをドラッグ&ドロップで追加してください。Stage1での座標はプレハブをクリックすると確認できるので参考にしてください。Stage1と同じように遊べる状況になったらOKです。

​ または、Stage1シーンにあるオブジェクトをコピー&ペーストしてStage2シーンに配置しても構いません(その方が楽です)

5

背景を変更する

 それではステージ2を制作していきましょう。

 

 ステージ1と全く同じ背景では面白くないので、背景画像を違うものに差し替えてみてください。「Sprite」→「BackGround」内にある好きな背景を、BackGroundオブジェクトのSpriteにドラッグ&ドロップすることで変更できます。画像に合わせてScaleやSizeを調整してください。

​※ 元となったプレハブから改変した項目の左側には青い印がつきます

5

タイルマップを
​リセットする

 次はタイルマップをリセットしましょう。

 事前にStage1に戻ってGridのプレハブ化を解除しておいてください。これを忘れるとStage1のギミックが全て消えてしまうので注意しましょう。

 ​Stage2のStage_Mainオブジェクトを選択して、TileMap右端の3つの点があるボタンを押してください。開いたメニューの「Reset」をクリックすることでタイルマップをリセットできます

​ Stage_Back、Decorationも同じようにリセットしてください。

 Gimmickだけはオブジェクトを直接削除する必要があります。

 「Prefab」→「GamePrefab」内のGridをダブルクリックして開いてください。

 Gridプレハブを開いて、Gimmickの子オブジェクトを全て削除してください。

 これでStage2シーンのタイルマップが全て初期化されます。

6

ステージ2を作る

 ステージ1と同じように地形、装飾、ギミックを配置していきましょう。設定した背景に合うパーツを配置してみてください。

 タイルマップの使い方を忘れた場合はLesson2を確認してください。特にステージ1と違うパーツを使う場合はSpriteEditorから当たり判定の設定が必要です。

 Active Tilemapの項目の右側に警告が出ていますが、これはタイルマップにプレハブを使っていることが原因で出ているものなので気にしなくて構いません。

 Active Tilemapを変更した際に警告ウィンドウが出た場合は「Scene」ボタンを選択してください。

 タイルマップが複数あるため、設置対象のタイルマップを間違えないように注意してください。

​ 一通りの地形やギミックを配置してステージ2を完成させましょう。Stage1と同じように地形→装飾→ギミックの順番で配置していくのがオススメです。

 ステージの地形に関する小ネタです。

 Unityには2D Sprite Shapeという機能があります。スプラインを使って画像を変形させることができる機能です。

​ これを使って滑らかな坂道を作ってみましょう。

 まずは使用するスプライトを用意しましょう。自前で素材を用意するか、左のサンプルを使用してください。

​ 左のサンプル画像はタイルマップ用スプライトの一部をトリミングして一部を加工したものです。

 プロジェクト内に画像を追加して、スプライトの設定をしてください。

 Sprite ModeはSingle、Mesh TypeはFull Rectにしましょう。

 実装には直接関係ありませんが、Filter ModeをPoint(no filter)に、CompressionをNoneに設定することでドット絵向けの表現になります。

 Sprite Editorを開いて、Borderを設定しましょう。画像を左端、中央、右端に分割します。

 「Create」→「2D」→「Sprite Shape Profile」を選択しましょう。

2_5_a4

 追加したSprite Shape Profileを選択して、インスペクター内のSpritesに先ほど追加した画像を指定してください。ちなみに複数指定してパターンを作ることもできます。

 作成したSprite Shape Profileをシーン上にドラッグ&ドロップしてください。

 Sprite Shape ControllerのEdit Splineボタンをクリックして、シーン上でスプラインを操作してください。デフォルトでは頂点で画像が切れてしまうため、Tangent Mode中央のボタンをクリックして画像の端を繋げてください。

​ 極端な坂を作ると壁として判定されてしまうので角度には注意しましょう。

 このままでは当たり判定がないため地形として機能しません。Polygon Collider 2Dを追加してください。Edit Colliderをクリックすると当たり判定の形を確認できます​。Offsetの値を操作して当たり判定を調整してください。

 場合によっては頂点を動かして手動で当たり判定を調整しましょう。その場合、Sprite Shape Controller内にあるUpdate Colliderのチェックは外しておいてください

 ジャンプできるように、地面のオブジェクトにGroundタグを設定してください。

 これで滑らかな地形を作成できました。法線もしっかり取得できているので、プレイヤーも地形に合わせて角度が変わります。ギミックを置いて遊んでみてください。

Unity Tips!

7

ステージ3を作る

 ステージ2と同じ手順でステージ3も作ってみましょう。

​ せっかくなのでステージ3はまた別の地形や背景を使ってみると良いでしょう。

 Stage3シーンを作成→プレハブを配置→タイルマップをリセット→ステージ作成

 サンプルではステージ3までしか作っていませんが、物足りない方はさらにステージを追加しても構いません。​オリジナルのギミックや素材を使って様々なステージを作ってみてください。

5-2

5-2 ステージセレクトを作成

5-2 ステージセレクトを作成

1

ステージセレクトシーンを作成

 ステージが完成したのでステージセレクトを実装して、それぞれのステージを遊べるようにしましょう。まずは前のステージをクリア済みかどうかなどの概念は置いておいて、単純にクリックしたステージを遊べるようにします。

 新しくStageSelectシーンを作成してください。

2

背景を作成

 StageSelectシーンを開いてください。

 まずは背景の画像を作成します。「UI」→「Image」から画像を追加してください。

 

​ 追加されたCanvasを選択して、UI Scale Modeを「Scale With Screen Size」に設定してください。Reference ResolutionはX=620 Y=480 に設定します。

 作成したImageにわかりやすいように名前をつけておいてください(サンプルでは「BG」)

 座標は原点にして、Widthには620、Heightには390を入力しましょう。

​ Sauce Imageに「Sprite」→「UI」→「StageSelect」内にあるStageSelectBGをドラッグ&ドロップしてください。

 

 これで背景画像の作成は完了です。

3

ステージ選択
​ボタンを追加

 次にステージを選択するためのボタンを作成します。UnityのUIでは「Button」という機能を使うことで、クリックで動作するボタンを簡単に実装することができます。

​ ヒエラルキーから「UI」→「Button - TextMeshPro」を選択してください。シーン内にボタンが追加されたらOKです。

4

ボタンの画像を

​設定する

 ボタンとして使う画像の設定をします。Lesson4でスコア用のウィンドウ画像を設定した時と同じです。

 「Sprite」→「UI」→「StageSelect」内にあるStageButtonを選択してください。

​ Sprite Modeを「Multiple」、Mesh Typeを「Full Rect」に変更したらApplyボタンをクリックしましょう。

 設定が変更できたらSpriteEditorを起動してください。

 SpriteEditorが開いたら、ボタン画像全体を囲うようにドラッグしてください。

 ドラッグすると右下にSpriteウィンドウが表示されるので、Border内に画像の分割サイズを設定しましょう。今回はLTRB全てに10を入力してください。

 入力できたら右上のApplyをクリックして、SpriteEditorを閉じてください。

 Buttonオブジェクトを選択して、名前をわかりやすいものに変更してください(サンプルではStage1)

​ ボタンの大きさを好みに調整して、SauceImageに先ほど設定したスプライトStageButtonを設定しましょう。

​ また、ImageTypeをTiledに変更してください(しなくてもOK)

 ImageTypeでは分割した画像の表示方法を設定できます。

 Slicedでは中心に該当する画像は引き延ばされ、Tiledでは敷き詰められます(横線の数を数えるとわかりやすいと思います

​ 使用する画像によって使い分けてください。

5

テキストを設定
​する

 子オブジェクトであるTextも同じように設定していきます。

 オブジェクトの名前をわかりやすいものに変更してください(サンプルではStage1Text)

 テキストの位置を調整して、ステージ名を入力しましょう。フォントはLesson4でダウンロードしたものを使用します。

​ 他にもフォントサイズやアウトラインなどを好みで調整してください。

※ 新しいMaterial Presetを使いたい場合は3Dアクションゲーム編の4-1を参考にしてください

 同じようにクリア用のテキストとハイスコア用のTextMeshProを追加してください。​位置や表示方法の設定はお好みで構いません。

6

ボタンの判定を
​修正する

 ここまで実装できたらゲームを実行してみてください。

 ボタンをクリックすることでボタンが反応することを確認できます。

​ しかし、ボタン外に表示されている子オブジェクトをクリックしてもボタンが反応してしまいます。

 このままではボタンとして少し不自然なので修正していきましょう。

 

 UIがクリックされたかどうかを検知するためにはRay(レイ)が使われています。レイはLesson1でも説明した通り、見えない光線を発射して衝突したオブジェクトの情報を取得する機能です。

 スクリーンがクリックされた時、実はクリック位置から奥方向にレイが発射されています。そのレイがボタンに衝突した時に、ボタンが押されたとして判定しています。

 逆に、UIにレイが衝突しないように設定することでクリックに反応しないようにすることができます。

​ レイが衝突するかどうかを決めている項目は「Raycast Target」になります。この項目にチェックが入っているオブジェクトにはレイが衝突するようになります。

 ボタン本体にはレイの衝突判定を行ってほしいので、Raycast Targetはチェックを入れておきます。子オブジェクトであるテキストのRaycast Targetのチェックを外しましょう。

 TextMeshProではRaycast Targetの項目は通常表示されていませんが、デバッグモードにすることで見ることができます

 インスペクターの右上にある3つの点をクリックして、モードを「Normal」から「Debug」に変更してみてください。隠されていた色々な項目が表示されます。

 TextMeshPro内のRaycast Targetという項目を探して、チェックを外しましょう。ステージ名、クリア用テキスト、ハイスコア用テキスト全てのRaycast Targetのチェックを外してください

 設定が終わったら、モードを「Debug」から「Normal」に戻してください。

 デバッグモードではprivateにしている変数の中身も見ることができます(編集はできません)

​ 項目が多くてややこしいので普段はノーマルモードにしておいた方が良いですが、デバッグの際にはデバッグモードにすることで効率よくデバッグを行えるようになります。

Unity Tips!

 ここまでの手順が終わったら実行して、ボタンのクリックが正常に判定できることを確認してください。

 

​ 今後もゲームを作っていてボタンやクリック動作が正常に反応しない時は、まず対象のRaycast Targetのチェックが入っているか確認するようにしましょう。

7

ステージ選択
​ボタンを複製

 ボタンをコピーして、ステージ2とステージ3のボタンも作成しましょう。ステージ名のテキストだけを適切な内容に変更しています。

 今回はステージが3つだけなのでボタンをそのまま複製していますが、ステージの追加など今後を見越した開発をするならボタンをプレハブ化して、ステージの数だけ生成した方が良いでしょう。ステージが100個になったり、ステージ間に新しいステージを追加したいといった状況を想定すると、ボタンをコピーして配置していくのはかなり大変な作業になってしまいます。単純作業はプログラムに任せることで、短時間で高クオリティのゲームが完成します

​ 「ボタンをプレハブ化して生成する」実装については、近い内容の解説をLessonEXのモンスター図鑑で行っているので、たくさんステージを作成したい場合は参考にしてみてください。

Unity Tips!

 これでステージセレクトは完成です。

 後はお好みで装飾やアニメーションを追加してみてください。

5-3

5-3 ステージ選択ボタンの実装

5-3 ステージ選択ボタンの実装

1

ステージ切替
​処理を実装

 それでは「ボタンが押されたらシーンを切り替える」処理を実装していきましょう。

 Buttonコンポーネントはボタンが押された時に実行する関数を指定することで、ボタンが押された時に様々な処理を実行することができます。

 SceneButtonスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;  // シーンを扱う時に必要

 

public class SceneButton : MonoBehaviour
{
    // ボタンが押された時に呼ばれる関数
    // 引数には変更先のシーン名を入れる

    public void SceneChange(string sceneName)
    {
        // シーンを呼び出す
        SceneManager.LoadScene(sceneName);
    }
}

2

ボタンの設定

 外部から呼び出す関数なのでpublicにしておく点に注意してください。

 コードが書けたら保存してください。StageButtonオブジェクトのButtonコンポーネントにある、On Click()内のプラスボタンをクリックしましょう。

 先ほど作成したSceneButtonスクリプトをアタッチしてください。

 プラスボタンを押した時に、On Click()内にいくつか項目が追加されたと思います。その中でNoneと表示されている部分に、SceneButtonコンポーネントをドラッグ&ドロップしてください。

 ドラッグ&ドロップしたことでNo Functionと表示されているボタンが押せるようになります。

​ クリックすることでプルダウンメニューが開くので「SceneButton」→「SceneChange」と選択してください。ここでボタンが押された時にどの関数を実行するのか設定することができます。

 関数を指定すると、SceneChange関数を宣言した際に作成した引数sceneNameを入力するための枠が追加されます。

​ ここに遷移先のシーン名を入れることで、SceneChange関数内で入力した引数が使用されるようになります。

 ステージ1の遷移先になるシーン名はStage1なので「Stage1」と入力してください。

​ ここまでと同じ手順でステージ2、ステージ3のボタンも完成させてみましょう。Buttonは画面を操作するゲームでは欠かせない機能なのでここで慣れておいてください。

3

ステージ選択時の
エラーを修正

 これでステージセレクトのボタンは完成です。

 しかし、実際にボタンを押してシーンを切り替えようとすると下記のようなエラーが発生してしまうはずです。シーンを扱う過去のレッスンを参考にして、このエラーを解決してみてください

 ステージ1、ステージ2、ステージ3のボタンをクリックしてそれぞれのステージへ遷移することを確認してみてください。

4

フェード用
​キャンバス作成

 これでシーンは切り替わりますが、一瞬で画面が切り替わるのは違和感があります。一度画面を暗転させて、暗転が終わってからシーンを切り替えるようにしましょう。

 画面を暗転させるためには「最前面に真っ黒な画像をだんだん表示させる」→「完全に表示されたらシーンを切り替える」→「遷移先のシーンで最前面に表示した画像をだんだん透明にする」という流れを作る必要があります。

 少しだけ複雑な手順を踏みますが、フェードの処理は他のゲームでも再利用できるので頑張って作ってみましょう。

​ まずはフェード用に新しいCanvasを作成してください。名前はFadeCanvasにしておきます。

 Canvas下にImageを追加してください。名前はFadeImageにしておきます。

 まずはCanvasの設定をしましょう。

 CanvasコンポーネントのSort Orderは、複数のCanvasがあった場合の表示優先度を設定する項目です。

 これからフェード用に真っ黒な画像を表示しますが、それがステージセレクトやゲームのUIより後ろに表示されると困ります。そのため、フェード用の画像は他のUIより前面に表示されるように設定する必要があります。

​ Sort Orderの値が大きいCanvasほど前面に表示されます。0より大きければ何でも構わないのですが、後ほど他のUIを追加する可能性も見越して、5など余裕を持った値にしておきましょう。

​ Canvas Scalerも他のCanvasと同じ設定にしておいてください。

 次は画面全体に画像を引き伸ばしましょう。今回は今までとは別の方法でサイズを調整します。

 FadeImageのRect Transform左上にあるアンカー   をクリックしてください。

 アンカーをクリックすると色々な項目が表示されます。Altキーを押しながら下のボタンをクリックしてください。画面全体にフェード画像が引き伸ばされたらOKです。

 Colorを選択して、全ての項目を0にしてください。A(Alpha)の項目は不透明度です。

​ これで画像が真っ黒かつ透明になります。

5

フェード用の
​処理を実装

 次はフェード処理のスクリプトを書きましょう。

​ 新しいスクリプトFadeSceneを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   // Imageを扱う時に必要
using UnityEngine.SceneManagement;  // Sceneを扱う時に必要

 

public class FadeScene : MonoBehaviour
{
    bool m_fadeStart = false;   // trueなら一連の処理を開始

 

    bool m_fadeMode = false;    // falseなら暗くなる trueなら明るくなる
    float m_alpha = 0.0f;       // 画像の不透明度
    [SerializeField]
    float FadeSpeed = 1.0f;     // フェードの速度(大きいほど速い)

 

    // 遷移先のシーン名を保存
    string m_sceneName;
    // 自身が使用するImageを保存
    Image m_image;

    // フェード開始
    public void FadeStart(string sceneName)
    {
        // フェード開始の準備をする
        m_fadeStart = true;
        m_sceneName = sceneName;

 

        // 自分の子オブジェクトにアタッチされているImageを取得する
        m_image = transform.GetChild(0).GetComponent<Image>();

 

        // 自身はシーンをまたいでも削除されないようにする
        DontDestroyOnLoad(gameObject);
    }

 

    void Update()
    {
        // フェードが開始していないなら中断
        if (m_fadeStart == false)
        {
            return;
        }

 

        // フェード処理
        if (m_fadeMode == false)
        {
            // 画面を暗くする
            m_alpha += FadeSpeed * Time.deltaTime;

 

            // 完全に暗くなったのでシーンを変更する
            if (m_alpha >= 1.0f)
            {
                SceneManager.LoadScene(m_sceneName);
                // 明るくするモードに変更
                m_fadeMode = true;
            }
        }
        else
        {
            // 画面を明るくする
            m_alpha -= FadeSpeed * Time.deltaTime;

 

            // 完全に明るくなったので自身を削除する
            if (m_alpha <= 0.0f)
            {
                Destroy(gameObject);
            }
        }

 

        // 最後に不透明度を設定する
        Color nowColor = m_image.color;
        nowColor.a = m_alpha;
        m_image.color = nowColor;

    }
}

 コードが長いため一見難しく見えるかもしれませんが、やっていることは前述した「最前面に真っ黒な画像をだんだん表示させる」→「完全に表示されたらシーンを切り替える」→「遷移先のシーンで最前面に表示した画像をだんだん透明にする」という処理になります。

【プログラムの解説】

​・transform.GetChild(x) で、自身のx番目の子オブジェクトを取得することができます。今回は自身の0番目の子オブジェクトなのでFadeImageを取得することになります。

​・DontDestroyOnLoad(gameObject); を実行すると、引数にしたオブジェクトはシーンの変更に巻き込まれて消えないようになります。これを実行しないとシーンを切り替えた際にStageSelectと一緒にフェード画像も巻き込まれて消えてしまい、フェードが成立しなくなるので注意しましょう。

​ ここまで書けたら保存して、FadeCanvasにFadeSceneスクリプトをアタッチしてください。

6

​プレハブ化する

 Prefabフォルダ内に他のプレハブと区別できるように新しいフォルダFadePrefabを作成してください。

 FadePrefabフォルダ内にインスペクターからFadeCanvasをドラッグ&ドロップして、FadeCanvasをプレハブ化してください。プレハブ化できたら、インスペクター内のFadeCanvasは削除してください。

7

フェード処理を
​行う

 最後にSceneButtonスクリプトを修正して、FadeCanvasを生成&初期化できるようにしましょう。

 赤い部分のコードを追加・修正してください。

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

 

public class SceneButton : MonoBehaviour
{
    [SerializeField]
    GameObject FadeCanvas;

 

    // ボタンが押された時に呼ばれる関数
    // 引数には変更先のシーン名を入れる

    public void SceneChange(string sceneName)
    {
        // フェード用のCanvasを作成
        GameObject fadeCanvas = Instantiate(FadeCanvas);
        // FadeSceneを取得してフェードを開始
        fadeCanvas.GetComponent<FadeScene>().FadeStart(sceneName);

    }
}

 ボタンが押された時にFadeCanvasを作成して、そのFadeCanvasにアタッチされているFadeSceneに対して「フェードを開始してね、遷移先のシーンの名前は(sceneName)だよ」と教えている形になります。

 以降の不透明度変更やシーン遷移などの処理は全てFadeSceneスクリプトがやってくれるので、後のことは一切気にせずにフェードを開始することができます。

 ここまでコードが書けたら保存して、各ボタンのSceneButtonコンポーネント内にあるFadeCanvasに、先ほどプレハブ化したFadeCanvasをドラッグ&ドロップしてください。

​ 3つ全てのボタンに対して設定が完了したら、ゲームを実行してボタンを押したときにフェード処理が動作するか確認してください。

 このフェード処理は他のゲームでも使いまわすことができるので、お好みで改造して自分のゲームでもフェード処理を実装してみてください。

​ フェード用キャンバスに文章を表示してローディング中にTIPSを表示したり、アニメーションを表示したりと色々工夫することができます。

5-4

5-4 クリア状況の保存

5-4 クリア状況の保存

1

セーブデータを
​定義する

 ここまでのレッスンでボタンを押すことで各ステージに遷移できるようになりました。

 今は全てのステージに自由に移動できますが、次は「プレイ可能フラグをロード」して「プレイ可能フラグが立っているステージのみプレイできる」ようにしましょう。

 セーブという直観的でない要素を扱うので難しい点もありますが、すぐに全て理解する必要はありません。わかるところから読み解いていってください。

​ まずはプレイ可能フラグを格納する場所が必要です。ついでにクリア済みフラグとハイスコアも保存できるようにします。

 新しくSaveDataスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 

[System.Serializable]
public class SaveData //  : MonoBehaviour が消えているので注意!
{
    // 各ステージに対する情報を格納するための構造体
    [System.Serializable]
    public struct Stage_Data
    {
        public bool isPlay;    // プレイ可能かどうか
        public bool isClear;   // クリア済みかどうか
        public int highScore;  // ハイスコア
    }

 

    // 各ステージの情報
    public Stage_Data[] StageData;

}

 このスクリプトは今までのスクリプトとは色々と違う点に気付いたでしょうか?Unity特有のコードは一切なく、セーブデータを定義するためだけのクラスになっています。いきなり使いこなすのは難しいかもしれませんが、少しずつ解説していきます。

 

【プログラムの解説】

​・[System.Serializable] はアトリビュートの一種で、付与したクラスや構造体はシリアライズ(シリアル化)可能になります。直観的に理解するのは難しいと思いますが「後でセーブするためにこのコードが必要なんだな」くらいの認識で構いません(参考資料

・​MonoBehaviour はUnityが用意してくれている基本クラスで、スクリプトをアタッチする機能、Start関数やUpdate関数などゲームを作る上で欠かせない処理がまとまっています。

​ セーブデータにStart関数やUpdate関数は必要ないので、今回はコメントアウトしておきましょう。

​・struct Stage_Data は構造体です。構造体は複数の変数を1つにまとめたものです。

 例えばRPGを作っていて勇者や魔法使いがいたとすると、それぞれ体力や攻撃力を持っているはずです。後から格闘家を追加したとしても、同じように体力や攻撃力のパラメータを持っています。

 そういった共通の変数(パラメータ)を管理する時には構造体を使うと便利です。構造体を使うことで、複数の変数をセットで扱うことができます。

2

セーブとロードを
​実装する

 今回はステージの開放状況、クリア状況、ハイスコアを記録していますが、後からクリア回数も記録したくなった時は構造体にパラメータを追加するだけでOKになります。ケアレスミスを防いだり、改良しやすいプログラムにするためにも構造体を活用してみましょう。

 構造体についてはC++の教科書なども参考にしてください(参考資料

​・Stage_Data[] は可変長配列です。この時点ではステージがいくつあるかわからないので、要素数を後から変更できるようにしています。可変長配列についてもC++の教科書を参考にしましょう。

​ 今までの解説になかった要素がたくさん出てきて混乱したかもしれませんが、ここで行ったことはあくまで「セーブデータのベースを用意しただけ」なので難しく考えないでください。理解できる人は累計クリア回数や取得したスコアの合計などを保存するパラメータを追加してみても良いでしょう。

​ 次はセーブデータを作成してセーブ&ロードしてみましょう。こちらも全て理解する必要はありません。

​ SaveManagerスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;    // ファイルを扱う時に必要

 

public class SaveManager : MonoBehaviour
{
    [SerializeField]
    SaveData NowSaveData;   // セーブデータ
    public SaveData GetSaveData()
    {
        return NowSaveData;
    }

 

    string m_filePath;      // 書き込み先のファイルパス
 

    // 最初に実行
    void Awake()
    {
        // 自身はシーンをまたいでも削除されないようにする
        DontDestroyOnLoad(gameObject);

 

        // 最初にセーブデータを読み込む
        m_filePath = Application.persistentDataPath + "/" + ".savedata.json";
        bool isLoad = Load();

 

        // ロードに失敗した場合はセーブデータを初期化して新規にセーブする
        if (isLoad == false)
        {
            NowSaveData.StageData = new SaveData.Stage_Data[3]; // ステージ数だけデータを作成

 

            // 簡易的に初期化
            NowSaveData.StageData[0].isPlay = true;     // ステージ1だけプレイ可能
            NowSaveData.StageData[0].isClear = false;
            NowSaveData.StageData[0].highScore = 0;

 

            NowSaveData.StageData[1].isPlay = false;
            NowSaveData.StageData[1].isClear = false;
            NowSaveData.StageData[1].highScore = 0;

 

            NowSaveData.StageData[2].isPlay = false;
            NowSaveData.StageData[2].isClear = false;
            NowSaveData.StageData[2].highScore = 0;

 

            // 初期化したデータを使って保存
            Save();
        }
    }

 

    // 現在の状況をセーブする
    public void Save()
    {
        string json = JsonUtility.ToJson(NowSaveData);
        StreamWriter streamWriter = new StreamWriter(m_filePath);
        streamWriter.Write(json); 
        streamWriter.Close();
    }

 

    // 現在の状況をロードする
    // ロードに成功したらtrue、失敗したらfalseを返す

    public bool Load()
    {
        // セーブデータがあるか確認
        if (File.Exists(m_filePath))
        {
            StreamReader streamReader;
            streamReader = new StreamReader(m_filePath);
            string data = streamReader.ReadToEnd();
            streamReader.Close();
            NowSaveData = JsonUtility.FromJson<SaveData>(data);
            // ロードできたのでtrueを返す
            return true;
        }
        // セーブデータが見つからなかったのでfalseを返す
        return false;
    }

}

3

セーブ管理用
オブジェクト作成

 こちらのスクリプトも見慣れないコードが多数あります。ただしほとんどがセーブ&ロード関連なので頻繫には使いません。無理に覚えなくても大丈夫です。

 処理の流れとしては「ファイルパスを作成」→「作成したファイルパスを用いてロードする」→「ロードに成功したらそのまま読み込む、失敗したら新しいセーブデータを作成して保存する」といったものになります。

【プログラムの解説】

​・m_filePath = Application.persistentDataPath + "/" + ".savedata.json"; では、セーブデータ書き込み先のファイルパスを作成しています。

 Application.persistentDataPath はUnityが「永続的なデータはここに保存してね」と推奨しているパスを返してくれます。

​・ロードに失敗した場合、セーブデータを作成して新規に保存する処理を行っています。

 NowSaveData.StageData = new SaveData.Stage_Data[3]; では配列の要素数を設定しています。それぞれのステージ数に応じて変更してください。

 

・​Save関数内ではセーブ用の特殊な処理を行っています。​今はあまり気にしなくても構いません。

​・File.Exists 関数は引数に指定したファイルが存在するかどうかを確認して、存在するならtrueを返す関数です。ここでセーブデータが存在するかチェックして、今後の処理を分岐させています。

​ ロード用の処理については今はあまり気にしなくても構いません。

​ コードが書けたら保存して、StageSelectシーンに空オブジェクトを追加してください。​追加した空オブジェクトの名前はSaveObjectにしておきます。

 SaveObjectにはSaveManagerタグを作成して設定しましょう。先ほど作成したSaveManagerスクリプトをアタッチしてください。

4

セーブデータに
​アクセスする

 後はセーブデータにアクセスして、各ボタンのパラメータを変更できるようにしましょう。

 StageButtonスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   // UIを扱う時に必要
using TMPro;            // TextMeshProを扱うときに必要

 

public class StageButton : MonoBehaviour
{
    [SerializeField]
    int StageNo = 0;    // ステージ番号

 

    [SerializeField]
    GameObject ClearObject;
    [SerializeField]
    TextMeshProUGUI HighScoreText;

 

    void Start()
    {
        // セーブデータを取得する
        SaveManager saveManager = GameObject.FindGameObjectWithTag("SaveManager").
            GetComponent<SaveManager>();

        // プレイ可能か判定
        if (saveManager.G
etSaveData().StageData[StageNo].isPlay == false)
        {
            // プレイ不可なので押せないようにする
            GetComponent<Button>().interactable = false;
        }

 

        // クリア済みか判定
        if (saveManager.GetSaveData().StageData[StageNo].isClear == false)
        {
            // 未クリアなので非アクティブにする
            ClearObject.SetActive(false);
        }

 

        // ハイスコアの表示
        int highScore = saveManager.GetSaveData().StageData[StageNo].highScore;
        HighScoreText.text = "HIGHSCORE:" + highScore;

    }
    
}

5

パラメータの
​設定をする

【プログラムの解説】

​・Buttonクラスのパラメータであるinteractableは、ボタンが押せるかどうかを設定するパラメータです。interactableをfalseにすると、ボタンをクリックしても反応しなくなります。

 今回は「プレイ可能フラグがfalseの時にinteractableをfalseにする」ことで、プレイできないステージを開始できないようにしています。

​ コードが書けたら保存して、Stage1やStage2などそれぞれのボタンにアタッチしてください。StageNoにはそれぞれのステージ番号(Stage1は0番、Stage2は1番)、Clear ObjectとHigh Score Textには適切なオブジェクトをドラッグ&ドロップして設定してください。

 ボタンの設定ができたらゲームを実行してみてください。

​ インスペクターには読み込んだセーブデータが表示されて、Stage1だけが遊べるようになっているはずです。

6

セーブデータの
​扱いを改良する

 実際にm_filePathで指定した場所にセーブデータが保存されているか見てみましょう。

 (ユーザー名)\AppData\LocalLow\DefaultCompany(Project Settings内のCompany Name)\(プロジェクト名)

 にセーブデータが保存されているはずです。

 メモ帳などのテキストエディタでセーブデータの中身を見ることができます。​セーブデータを開いてみると、かなり単純な形で保存されていることがわかります。

 改変も簡単にできます。ハイスコアの値を変更して保存してみてください。

 簡単にセーブデータを書き換えられました。

 このようにjson形式は単純な書き方なので、ちょっと詳しい人であれば簡単に改変できてしまいます

 今回は練習なので省略しますが、実際にゲームを配布する際にはセーブデータを暗号化するとよいでしょう。string(文字列)型を暗号化・複合化する方法は色々あるので各自で調べてみてください(参考例

Unity Tips!

 これで一通りの処理が完成しましたが、実は少しだけ不便な点が残っています。

 SaveObjectはシーンが切り替わっても消えないように設定しました。しかし、そもそもSaveObjectがStageSelectシーンにあるため、StageSelectシーンを介さないとセーブデータにアクセスできないようになってしまっています

 このままではデバッグする際にも毎回StageSelectシーンから始める必要が出てしまいます。それでは疲れますし、効率も悪いですね。

​ Unityにはどのシーンから開いたとしても最初に呼び出される関数を設定する機能があります。その関数でSaveObjectを生成して、どのシーンから始めてもSaveObjectが存在するようにしましょう。

​ 新しくGameStartスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

 

public class GameStart : MonoBehaviour
{
   // どのシーンから開始しても最初に呼ばれる関数
   [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
   static void Init()
   {
       // セーブオブジェクトを生成
       GameObject saveObject = (GameObject)Resources.Load("SaveObject");
       Instantiate(saveObject);
   }
}

7

Resources
​フォルダを作成

【プログラムの解説】

・[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] は長いですが、アトリビュートの一種です。この属性を付与した関数は、ゲーム起動時のシーンを開く前に呼ばれます。対象とする関数をstaticにする必要があるので注意しましょう。

 

 このスクリプトはどこにもアタッチしないので、今までとは違う方法で生成するオブジェクトを指定する必要があります。

 今回はプロジェクトウィンドウ内から名前でプレハブを取得してみましょう。

 プロジェクト内に「Resources」フォルダを作成してください。打ち間違えると実行時にエラーが起きるので注意してください。

8

プレハブ化する

 作成したResourcesフォルダ内に、ヒエラルキーからSaveObjectをドラッグ&ドロップしてプレハブ化しましょう。

​ プレハブ化したらヒエラルキー内のSaveObjectは削除してください。

 先ほどInit関数内に記述した (GameObject)Resources.Load("SaveObject"); は、Resourcesフォルダ内にある引数名のオブジェクトを返す関数です。ResourcesフォルダはUnity側で特殊なフォルダ名として決められており、フォルダ名が異なると正常に動作しません。

 今まではオブジェクトを指定するときにヒエラルキーからインスペクターへドラッグ&ドロップしていましたが、このような指定方法もあると覚えておいてください。

 Resourcesフォルダは何も考えずに使っていいものではありません。

 Resourceフォルダに入っているファイルはビルド時にまとめて読み込まれます。読み込んだデータはゲームの起動中は常駐するため、大量のファイルをResourceフォルダに入れているとゲームの起動が遅くなったり、メモリを圧迫したりする危険性があります。特に大きな画像や音声ファイルなどの大容量のファイルを入れていると、メモリ使用量が大幅に増加してしまいます。

 そのためResourceフォルダを使用するのはゲームの起動中に常駐するリソースや、メモリを圧迫しないリソースに絞るようにしましょう

Unity Tips!

​ ここまでできたらStageSelectシーンやStage1シーンから実行して、どのシーンからゲームを開始してもSaveObjectが生成されていることを確認してみてください。

​ これでどのシーンからゲームを始めてもセーブデータを読み込めるようになり、デバッグしやすくなりました。実際ビルドしたゲームではStage1シーンからゲームを始めることはないですが、実際のゲームで使わないとしても開発で不便と感じた場合は処理を改良するようにしましょう。

5-5

1

クリア用
​キャンバスを作成

 ちょっと上級者向けのおまけです。​早く先に進みたい人は5-5まで飛ばしてください。

 「Resourcesフォルダはゲーム開始時に一括で読み込まれて常駐する」という話を上記でしていますが、必要な時に必要な素材だけを読み込むにはどうすればよいのでしょうか。

 Unityにはアセットバンドルという機能があります。アセットバンドルはゲームの素材を一まとめで管理して、必要に応じてロード/アンロードすることができる機能です。

​ アセットバンドルはできることが色々あるので全ては解説しませんが、ここでは基本的な使い方を解説します。

​ まずは適当な素材を用意してください。画像でもモデルでもプレハブでもOKです。

 素材を選択すると下部にAssetBundleの項目があります。

​ 「New」を選択して、管理できるように名前を付けてください。

 他の素材がある場合は、そちらもアセットバンドルの設定をしてください。

 次にアセットバンドルをビルドします。

​ Editorフォルダを作成してください。こちらも特殊なフォルダで、名前が完全一致しないと動作しないので注意してください。

 Editorフォルダ内にBuildAssetスクリプトを作成して、以下のように入力してください。

​ using UnityEditor やusing System.IO を忘れないように注意してください。
​※ 以下のコードはUnityの公式リファレンスのものにコメントを加えたものです

 BuildPipeline.BuildAssetBundles関数でアセットバンドルを作成できます。

 第二引数(BuildAssetBundleOptions)ではアセットの圧縮の設定を行ったり、上書きしてビルドするかどうかなどの設定を行うことができます。第三引数(BuildTarget)ではビルド時の対象プラットフォームを設定することができます。

​ 「Assets」→「Build AssetBundles」の項目が追加されているのでクリックしてください。

 AssetBundlesフォルダが作成され、アセットバンドルが出力されていることを確認してください。

 アセットバンドルを使って画像を差し替えてみましょう。

​ 差し替え対象となるImageを作成してください。

 新しいスクリプトChangeImageを作成して、以下のように入力してください。

 ChangeImageスクリプトを作成した画像にアタッチしてください。

​ ゲームを実行するとアセットバンドルを読み込んで画像を差し替えることができます。

 必要なくなったアセットバンドルはアンロードするようにしてください。

 Unload関数の引数にfalseを入れると、このバンドルから既にロードされているオブジェクトはそのまま維持されます。trueを入れると、このバンドルからロードされたすべてのオブジェクトが破棄されます。

 素材を使うたびにアセットバンドルをロードすることは現実的でないので、実際に制作に使う場合はアセットバンドルを管理するクラスを作ると良いでしょう。

Unity Tips!
5-5 クリア処理の実装

5-5 クリア処理の実装

 SaveObjectが完成したので、クリア時の処理を実装していきます。​まずは作りかけで終わっていたクリア画面を完成させましょう。

​ Stage1シーンを開いて「UI」→「Canvas」を選択して作成してください。名前はClearCanvasにしておきます。

​ Canvasと同時にEventSystemが生成されますが、EventSystemはUIシーンにすでに含まれているためStage1シーンには必要ありません。Stage1シーンにあるEventSystemは削除しておきましょう。

 EventSystemはクリックなどの入力に応じてUIに対してレイを発射したり、イベントを送信したりしてくれるコンポーネントです。これがないとボタンをクリックしても反応しなくなってしまいます。

 EventSystemはシーン上に1つしか存在できず、複数あった場合警告文が出てしまうので注意しましょう。

Unity Tips!

 ClearCanvasのUI Scale Modeを「Scale With Screen Size」に変更して、画面サイズが変わってもCanvasのサイズが調整されるようにしてください。

2

クリア時の
​UIを作る

 ClearCanvasにクリア時のUIを追加していきましょう。

 サンプルでは黒い半透明の背景、クリアテキスト、スコア、ハイスコア、スコア更新時のテキストを表示しています。例のごとくテキストの内容は仮の値にしています。

​ 配置や設定などはお任せするので、自由にクリア画面を作ってみてください。TextMeshProの設定については3Dアクションゲーム編のLesson4を参考にしましょう。

3

戻るボタンを作成

 ステージセレクトに戻るボタンを配置しましょう。

 「UI」→「Button - TextMeshPro」を選択してボタンを追加してください。

 ボタンやテキストの位置や設定を調整してください。サンプルではボタンの背景はステージセレクトのものを流用しています。

4

アニメーションを
​作成する

 これでクリア画面のベースは完成しましたが、一瞬で表示されるだけだと味気ないのでアニメーションを追加しましょう。

​ Animationフォルダ内にClearAnimationフォルダを作成して、新しいアニメーションを追加してください。追加したアニメーションをClearCanvasにドラッグ&ドロップしましょう。

 Animationでは子オブジェクトも対象にすることができます。今回ではClearCanvasの子オブジェクトになっているテキストやボタンにもキーを設定できるというわけです。

 追加したアニメーションをダブルクリックしてAnimationウィンドウを開いてください。ClearCanvasを選択した状態で「Add Property」をクリックすると、子オブジェクトの名前も表示されることが確認できます。

​ 自由にキーを配置して、クリア画面のアニメーションを作成してください。

​※ ハイスコア更新のテキスト(サンプルだと「New Record!!」のテキスト)は後ほどスクリプトでアクティブ状態を変更するので、アニメーションでは変更しないようにしてください

 クリアアニメーションを確認して、ループ再生になっていた場合はLoop Timeのチェックを外しておいてください。

5

クリア時のUIを
​更新する

 次はクリア用の処理を作っていきます。

​ 新しいスクリプトUI_Clearを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

 

public class UI_Clear : MonoBehaviour
{
    [SerializeField]
    TextMeshProUGUI ScoreText, HighScoreText;
    [SerializeField]
    GameObject NewRecordObject;

 

    public void SetScoreText(int score,int highScore,bool isNewRecord)
    {
        // スコアテキストの更新
        ScoreText.text = "SCORE:" + score;
        HighScoreText.text = "HIGH SCORE:" + highScore;

 

        // 新記録が出た場合のみアクティブ
        if (isNewRecord)
        {
            NewRecordObject.SetActive(true);
        }
        else
        {
            NewRecordObject.SetActive(false);
        }

    }

}

6

パラメータを
​設定する

 // スコアテキストの更新 内のtextはお好みで設定してください。

 プログラムが書けたら保存して、ClearCanvasにUI_Clearをアタッチしてください。

 インスペクター内に表示された項目に、ヒエラルキーから適切なオブジェクトをドラッグ&ドロップして設定しましょう。

・Score Text → スコアの値を表示しているテキスト

・High Score Text → ハイスコアの値を表示しているテキスト

​・New Record Object → ハイスコア更新時に表示されるテキスト(オブジェクト)

7

ボタンを設定する

 ステージセレクトに戻るためのボタンに、以前作成したSceneButtonスクリプトをアタッチしてください。FadeCanvasには「Prefab」→「FadePrefab」内のFadeCanvasをドラッグ&ドロップして設定します。

 SceneButtonコンポーネントの準備ができたら、ステージセレクトと同じようにボタンがクリックされた時に呼ばれる関数を設定していきましょう。

 ① On Click()内のプラスボタンをクリックする

 ② On Click()内の左下(Noneと表示されている所)にアタッチしたScene Buttonコンポーネントをドラッグ&ドロップする

 ③ On Click()内の右上(No Functionと表示されている所)をクリックして「SceneButton」→「SceneChange」を選択する

 ④ On Click()内の右下に遷移先のシーン名「StageSelect」を入力する

8

プレハブ化する

 これでClearCanvasの準備は完了です。

 Prefabフォルダ内にClearPrefabフォルダを作成して、その中にClearCanvasをドラッグ&ドロップしてプレハブ化しましょう。

​ プレハブ化したらヒエラルキー内のClearCanvasは削除してください。

9

クリア時の
​処理を実装する

 GameManagerスクリプトを開いて赤い部分のコードを追加してください。少し長いですが、処理的にはあまり難しくありません。

~前略~

    // スコアを加算
    public void AddScore(int score)
    {
        Score += score;
        // 現在のスコアを渡してUI更新
        m_uiScore.ScoreUpdate(Score);
    }
    // スコアを取得
    public int GetScore()
    {
        return Score;
    }

 

    [SerializeField, Header("クリアキャンバス")]
    GameObject ClearCanvasObject;
    [SerializeField, Header("ステージ番号")]
    int StageNo;
    [SerializeField, Header("解放されるステージ番号")]
    int NextStageNo;

    // ゲームクリア処理
    public void GameClear()
    {
        // SaveObjectからセーブデータを取得
        SaveData saveData = GameObject.FindGameObjectWithTag("SaveManager").
            GetComponent<SaveManager>().GetSaveData();

 

        // セーブデータから現在のステージのハイスコアを取得する
        int highScore = saveData.StageData[StageNo].highScore;

        // 現在のスコアがハイスコアを上回っていたら新記録フラグを立てる
        bool isNewRecord = false;
        if(Score > highScore)
        {
            isNewRecord = true;
        }

 

        // クリアキャンバスを生成する
        GameObject clearCanvas = Instantiate(ClearCanvasObject);
        // 生成したクリアキャンバスにスコア、ハイスコアを教える
        clearCanvas.GetComponent<UI_Clear>().SetScoreText(Score, highScore, isNewRecord);

 

        // セーブデータの更新
        // クリアフラグを立てる

        saveData.StageData[StageNo].isClear = true;
        // 次のステージの挑戦可能フラグを立てる
        saveData.StageData[NextStageNo].isPlay = true;
        // ハイスコアが出た場合は現在のスコアをハイスコアとして保存する
        if (isNewRecord)
        {
            saveData.StageData[StageNo].highScore = Score;
        }

        // セーブ処理
        GameObject.FindGameObjectWithTag("SaveManager").
            GetComponent<SaveManager>().Save();
    }

 

    // Awakeなので注意
    void Awake()
    {
        // UIシーンを合成
        SceneManager.LoadScene("UI", LoadSceneMode.Additive);
    }

 

    void Start()

~後略~

10

クリア用の
​関数を実行する

 処理の流れは「タグを使ってセーブデータを取得」→「ハイスコアを更新したか判定」→「ClearCanvasを生成、テキストを更新」→「取得したセーブデータを更新」→「セーブ処理」となっています。

 コードが書けたら保存して、クリアした瞬間にGameClear関数が呼び出されるようにしましょう。

​ PlayerMoveスクリプトを開いて、赤い部分のコードを追加してください。

~前略~
 

    // クリア演出
    void GameClearAnimation()
    {
        // もう終わっている場合は中断
        if (m_claerWait)
        {
            return;
        }

 

        // アニメーションを変更
        m_animator.SetTrigger("Clear");

 

        // カメラ演出
        Camera.main.GetComponent<GameCamera>().CameraClearAnimation();

 

        // クリア処理を行う
        m_gameManager.GameClear();

 

        // ゲームクリア演出終了
        m_claerWait = true;
    }

~後略~

11

生成する

キャンバスを設定

 これでクリアした瞬間にClearCanvasが作成されたり、ハイスコアが保存されたりするようになりました。

​ コードが書けたら保存して、Gameのプレハブをダブルクリックして開いてください。

 インスペクターに先ほど追加した変数が表示されているので、ClearCanvasObjectにClearCanvasのプレハブをドラッグ&ドロップしてください。

 プレハブ側を編集すると、同じプレハブから生成した全てのオブジェクトに変更が反映されます。Stage1やStage2に1つずつ設定していかなくても、一括でクリアキャンバスの設定が完了しました。

12

ステージ番号の
​設定

 ClearCanvasを選択したときなどにインスペクターが切り替わってうまくドラッグ&ドロップできない場合は、インスペクター右上にある鍵のアイコンをクリックすることでインスペクターをロックすることができます再度クリックすることで解除できます。

 ロックされている間は他のオブジェクトをクリックしてもインスペクターの内容が変更されることはありません。

​ スムーズにインスペクターを操作するためにも覚えておくと良いでしょう。

Unity Tips!

 Stage1シーンに戻って、GameManagerに現在のステージ番号と解放されるステージ番号を設定してください。Stage2シーンとStage3シーンも同じように設定します。

​・Stage1 … StageNo=0 NextStageNo=1

・Stage2 … StageNo=1 NextStageNo=2

・Stage3 … StageNo=2 NextStageNo=2 (最後のステージは自身のステージ番号と同じにする)

2_5_74
2_5_75

 GameManagerの設定ができたら実行して、ステージをクリアすると次のステージが遊べるようになることを確認してみてください。

 また、ゲームを再プレイして、クリア状況やハイスコアが保存されていることも確認してみてください。

5-6

5-6 ゲームオーバー処理の実装

5-6 ゲームオーバー処理の実装

1

​ゲームオーバー用
​キャンバスを作成

 最後にゲームオーバーの処理を実装します。と言っても、シーンを遷移する処理は完成しているので特に難しい処理などはありません。

​ これでゲームの処理は一通り完成なので頑張りましょう!

​ ゲームクリアと同じように「UI」→「Canvas」からGameOverCanvasを作成してください。

 ゲーム中のUI用キャンバス(優先度0)とフェード用キャンバス(優先度5)の間になるように、優先度を設定してください。ここではSort Orderを1としておきます。

 Canvas ScalerのUI Scale Modeを「Scale With Screen Size」に変更して、画面サイズを設定するのを忘れないようにしてください。

2

​ゲームオーバーの
​UIを作成

 クリアキャンバスと同じように、GameOverCanvas下にUIのパーツを設置していきましょう。

​ サンプルでは半透明の黒い背景、ゲームオーバーテキスト、リトライボタン、ステージセレクトに戻るボタンを設置しています。

3

シーン切替処理を
​改造する

 ボタンがクリックされた時の処理を設定しましょう。

 リトライを演出するには SceneManager.LoadScene(現在のシーン名); という処理を行う必要があります。ですが、今のSceneButtonスクリプトにはそのような処理が実装されていないので、SceneButtonスクリプトを改造して対応しましょう。

 SceneButtonスクリプトを開いて、赤い部分のコードを追加してください。

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

 

public class SceneButton : MonoBehaviour
{
    [SerializeField]
    GameObject FadeCanvas;

 

    // ボタンが押された時に呼ばれる関数
    // 引数には変更先のシーン名を入れる

    public void SceneChange(string sceneName)
    {
        // 名前が空白だった場合、現在のシーンの名前を使う
        if (sceneName == "")
        {
            sceneName = SceneManager.GetActiveScene().name;
        }

 

        // フェード用のCanvasを作成
        GameObject fadeCanvas = Instantiate(FadeCanvas);
        // FadeSceneを取得してフェードを開始
        fadeCanvas.GetComponent<FadeScene>().FadeStart(sceneName);
    }
}

4

ボタンの設定

【プログラムの解説】
​・遷移先のシーン名が空白だった場合、現在のシーン名を置き換えて使用するように改造しています。

 SceneManager.GetActiveScene().name で現在のシーン名を取得することができます。

​ コードが書けたら保存して、各ボタンの設定をしてください。

​ 遷移先のシーン名はリトライボタンは空白(現在のシーン名に置き換わる)、ステージセレクトに戻るボタンは「StageSelect」にしましょう。

5

プレハブ化する

 ボタンの設定ができたらGameOverCanvasをプレハブ化してください。

​ プレハブ化できたらヒエラルキー内にあるゲームオーバーキャンバスは削除しておきましょう。

6

ゲームオーバーの
​処理を実装

 後はゲームオーバー時にGameOverCanvasを生成するだけになります。

​ GameManagerスクリプトを開いて、赤い部分のコードを追加してください。

~前略~

    // スコアを取得
    public int GetScore()
    {
        return Score;
    }

 

    [SerializeField, Header("ゲームオーバーキャンバス")]
    GameObject GameOverCanvasObject;
    [SerializeField, Header("ゲームオーバー画面生成時間")]
    float GameOverTime = 1.0f;
    // ゲームオーバー処理
    public void GameOver()
    {
        // 少し待ってからゲームオーバーキャンバスを生成する
        Invoke("MakeGameOverCanvas", GameOverTime);
    }
    // ゲームオーバーキャンバス生成
    void MakeGameOverCanvas()
    {
        Instantiate(GameOverCanvasObject);
    }

 

    [SerializeField, Header("クリアキャンバス")]
    GameObject ClearCanvasObject;
    [SerializeField, Header("ステージ番号")]
    int StageNo;

​~後略~

7

ゲームオーバーの
​関数を実行

 Invoke関数はクリアアニメーションの時にも使用したもので、名前が(第一引数)の関数を(第二引数)秒後に実行してくれる関数です。ゲームオーバーになった瞬間にゲームオーバーキャンバスが出てしまうと、プレイヤーが吹き飛ばされる演出が見えなくなってしまうので、サンプルでは1秒後にキャンバスを生成しています。

​ 続いて、PlayerMoveスクリプトに赤い部分のコードを追加してください。

~前略~
 

    // ゲームオーバー
    public void GameOver()
    {
        // プレイ中でないなら中断
        if (m_gameManager.GetState() != GameManager.GameState.enGameState_Play)
        {
            return;
        }

 

        // ゲームの状態を変更する
        m_gameManager.SetState(GameManager.GameState.enGameState_GameOver);

 

        // ゲームオーバー処理
        m_gameManager.GameOver();

 

        // アニメーションを変更
        m_animator.SetTrigger("GameOver");

 

        // 重力をリセット
        m_player_rb2d.velocity = Vector2.zero;
        // 重力を戻す
        m_player_rb2d.gravityScale = m_defGravity;
        // 左上に力を加える
        m_player_rb2d.AddForce(new Vector2(-5.0f, 7.0f), ForceMode2D.Impulse);

 

        // コライダーを無効にする
        GetComponent<CapsuleCollider2D>().enabled = false;
    }

 

    // ゲームクリア
    public void GameClear()
    {


​~後略~

 コードが書けたら保存して、Gameプレハブを開いてください。

8

生成する
​キャンバスを設定

 インスペクターに先ほど追加したゲームオーバーキャンバス設定用の変数が表示されているので、ゲームオーバーキャンバスのプレハブを設定してください。

 プレハブにゲームオーバーキャンバスを設定できたら、ゲームを実行してください。

 ゲームオーバーになると数秒後にゲームオーバーキャンバスが表示されたら完成です。リトライボタンを押すとステージを再挑戦でき、ステージセレクトボタンを押すとステージセレクトに戻ることも確認しておきましょう。

 これでゲームとして一通り遊べる状態になりました。

 次のレッスンではタイトル画面やエフェクト、効果音を実装してゲームを完成させましょう。

まとめ

・インスタンスをJSON形式で出力、読み込むことでセーブとロードを実装できる
​ 保存するクラスには [System.Serializable] のアトリビュートをつけてシリアライズ化可能にする

・Resourcesフォルダに入れたオブジェクトはどこからでも読み込める
 (クラス)Resources.Load(オブジェクト名)
​ ※ ただし重いデータを入れないように注意!

評価テスト

評価テスト

河原電子ビジネス専門学校
​ゲームクリエイター科

bottom of page