まるとこ学習帳

Unityでゲームを作っています。学んだことの記録を書きます。

【RPG風バトル】シーン間でデータを渡してみた【Unity】(前編)

!注意!
今回の方法で本当に安全にデータを引き渡せているか確証が得られていないので、
参考にされる際は各自検証の上ご使用ください。

やってみたいこと

前回、RPG風の戦闘シーンを作りましたが、戦闘シーンから他のシーンに
データを渡せたらいいなと思ったのでやってみました。

前回の記事はこちら
marutoko.hatenablog.com

一般的なRPGの場合、フィールドやダンジョンで敵にエンカウントしたら
戦闘シーンになって、戦闘でダメージを負い戦闘が終わったら
HPが減った状態でフィールドなどに戻りますよね。
それを簡単な形で再現できたらいいなと思ったわけです。

シーン間でデータをやりとりする方法はいろいろあるようですが・・・
こんな記事を見つけました。
note.com

うーん、グローバルなオブジェクトを持つ方法は簡単だけど危険もあるんですね。
初心者の私的には危険はなるべく避けたいので、この記事での
「シーンAがシーンBにデータをセットをする」方法を試してみました。
具体的には「戦闘シーンからダンジョンシーンにプレイヤーHPを渡す」
というのをやってみようと思います。

シーンを追加

ダンジョンシーンを追加します。
File → New Sceneで「DungeonScene」を追加。
スクリプトをアタッチするための空オブジェクトをHierarchy→Create Emptyで
作成、名前を「GameManager」とします。

参考記事内では"GameObject.FindWithTag"を使いタグでゲームオブジェクトを
見つけているので、GameManagerにタグを設定しておきます。

余談ですが、オブジェクトを見つける場合、GameObject.Find は処理が重いんですね...!
Hierarchyから直接参照するか、Tagを使って参照するのが良いそうです。
参考はこちら
squmarigames.com

さて、オブジェクトにタグを追加する方法は
GameManagerのInspectorのTagの右のプルダウンを押して Add Tag をクリック。
Tagsの+を押してNew Tag Name に「GameManager」と入力すると
GameManagerという名前のタグを追加できるので、
その後Tag右のプルダウンからGameManagerを選ぶことで
GameManagerオブジェクトにGameManagerタグを追加することができます。

↓Tagを作るとプルダウンに作ったタグが表示されるのでGameManagerをクリック。

スクリプトを追加

GameManagerオブジェクトにアタッチするスクリプトを作ります。
スクリプト名は「GameManagerScript」にしました。
ここでプレイヤーの残りHPを受け取るための変数(Phprem)を用意します。

戦闘シーンでプレイヤーの初期HPを100にしていたので、
最大HPは100ということにしてPhpremも100に設定し、
Start内のDebug.LogでPhpremの数値を表示するようにします。

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

public class GameManagerScript : MonoBehaviour
{
    public int Phprem = 100; //プレイヤーの残りHP

    private void Start()
    {
        Debug.Log("ダンジョンシーンに切り替わった");
        Debug.Log("プレイヤーの残りHP " + Phprem);
        
    }
}

このスクリプトをGameManagerにアタッチします。
このまま再生すると、Consoleには「プレイヤーの残りHP 100」と表示されますね。

戦闘前のHPは100ということで、これが戦闘シーンでダメージを受けて
ダンジョンシーンに戻ってくると残りHPが減って表示されるように、
BattleControllerスクリプトを編集していきます。

BattleControllerスクリプトを編集

戦闘シーンを開き、 File → Save As で名前を「BattleScene」として
保存しておきます。
次にBattleControllerスクリプトを編集します。

シーンが切り替わるタイミングで次のシーンに残りHPを渡したいのですが、
具体的には「デリゲート」という仕組みを使います。
デリゲートとはどんなものかというと、シーンの遷移を検知して、
シーン遷移のタイミングで登録したメソッドが呼び出される仕組みということです。
便利!!!(今知った

デリゲートについては「Unityの教科書」の作者様が簡単に説明して
くださってますのでご参考まで。

nn-hokuson.hatenablog.com

というわけで、さっそくスクリプトを編集します。
その前に下準備。
シーンの遷移をする場合、Build Settings にシーンを登録しておく必要があります。
現在開いているシーンを保存できるので、シーンを開いてから
File → Build Settings → Add Open Scenes より
「DungeonScene」と「BattleScene」を追加しておきます。

さて、どのタイミングで戦闘シーン → ダンジョンシーンに切り替えるかですが、
戦闘に勝利したときだと思うので、全ての敵を倒した時に呼ばれる
「AllEnemyDead」メソッドの最後にシーン遷移の記述をしていくことにします。

BattleController.cs(途中部分)

// 冒頭部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement; // ←シーン遷移をするために追加

// 中略

void AllEnemyDead()
    {
        clear = GameObject.Find("Clear");
        script = clear.GetComponent<ClearTween>();
        script.Clear();
        Debug.Log("ゲームクリア");
        
        // ここから追加
        NextScene();

        void NextScene()
        {
                // イベントにメソッドを登録
                SceneManager.sceneLoaded += GameSceneLoaded;

                // ダンジョンシーンに切り替え
                SceneManager.LoadScene("DungeonScene");

            void GameSceneLoaded(Scene next, LoadSceneMode mode)
            {
                // シーン切り替え後のスクリプトを取得
                var gameManager = 
                    GameObject.FindWithTag("GameManager").GetComponent<GameManagerScript>();

                // データを渡す処理
                gameManager.Phprem = Php; //次のシーンにプレイヤーの残りHPを渡す

                // イベントからメソッドを削除
                SceneManager.sceneLoaded -= GameSceneLoaded;

   // 追加ここまで
            }
        }
    }

全ての敵を倒した時に呼ばれるAllEnemyDeadメソッドの最後に、データを次のシーンに
渡しつつシーン遷移させるNextSceneメソッドを追加します。

その中でデータ引き渡しをするGameSceneLoadedメソッドを作成し、
シーン切り替え時に実行することにします。
SceneManager.sceneLoaded += (メソッド名); と書くことでシーンが読まれるときに
実行されるメソッドを登録できるので、その後
SceneManager.LoadScene("DungeonScene"); でダンジョンシーンをロードさせます。
そうすると、ダンジョンシーンがロードされるときにGameSceneLoadedメソッドが実行されます。

GameSceneLoaded内でvar gameManagerを宣言し、"GameManager"タグが付いている
オブジェクトを見つけてアタッチされているGameManagerScriptを取得します。
gameManager.Phprem = Php; で
GameManagerScriptの中のPhprem(ダンジョンシーンで表示される残りHP)に
Php(戦闘シーンでバトル終了後に残ったプレイヤーのHP)を渡します。

最後にSceneManager.sceneLoaded -= GameSceneLoaded; でイベントから
メソッドの登録を解除しておく必要があります。
"これを忘れてしまうとシーン切り替えのたびにこのメソッドが呼ばれてしまう"とのことです。
(ここは理解がまだ曖昧です)

実行してみる

ひとまずこれで、戦闘シーンからダンジョンシーンに残りHPを渡す処理を作ることが
できたと思うので実行してみます。

戦闘シーン後半〜ダンジョンシーンに切り替わった後までの実行結果です。
戦闘シーンでプレイヤーがダメージを受けて残りHPが40になり、クリア後
ダンジョンシーンが切り替わっても残りHPが40になっていて、無事残りHPを
引き渡せたことがわかります。

戦闘シーンからダンジョンシーンに残りHPを引き継がせることができました!
やった!!!と喜びたいところですが、まだ冒険は続きます。
ダンジョンシーンから戦闘シーンへも残りHPを渡さないと、再び戦闘になったときに
HPが全回復してしまします。
これではヌルゲーになってしまいますので、次回、今回と同様に
ダンジョンシーンから戦闘シーンに残りHPを引き渡してみたいと思います。

〜後編に続く〜