[Unity]脱出ゲームでオブジェクトの表示状態とかを保存したい

脱出ゲームのデータを保存したい

スマホ脱出ゲームを開発中

Unity2019.4.1f

ゲーム中のデータを保存したい。

保存したいのは

・部屋の場所
・所持アイテム
・削除したアイテム
・各オブジェクトの表示状態
とかとか

 

スクショ

 

ヒエラルキーの表示・非表示を切り替えて進んでいく感じになってる

なので表示状態の保存ができないとまずい

軽いデータならPlayerPrefsを使う

PlayerPrefsだと{“key” ,value}のようにキーと値を保存できる。


PlayerPrefs.SetInt()
//だと整数型

PlayerPrefs.SetString()
//だと文字列型で保存できる。


//読み出す時
PlayerPrefs.GetString()

//保存は
PlayerPrefs.Save()

 

stringならJason形式という保存の形式で複数のキーと値のセットを1つの変数に保存できる。
rubyのハッシュ みたいなものっぽい。
{“key”:value, “key”: value}
Jasonの読み書きをする変数はSerializalebeで宣言しないといけないとのことといけない。

Unityでゲームデータのセーブ・ロードを行う方法 | Unityを使った3Dゲームの作り方(かめくめ)

Unityでゲームデータのセーブ・ロードを行う方法
Unityのゲームで進行具合やキャラクターのステータスをデータとして保存しておき、それらのデータを読みだして続きから再開するというのは必須の機能になっています。 その為に今回はゲームデータのセーブとロードが出来るようにしてみたいと思います。

 

シリアライズってのが何をしているのかはこちらの説明が分かりやすかった↓
データを一直線に並べることのようです。
【Unity】スクリプトがシリアライズされる仕組み

【Unity】スクリプトがシリアライズされる仕組み
本記事では、シーンやPrefabなどのインスペクタから設定した値がどのように保存されるかについて解説していきます。 シリアライズとは シリアライズとは直列化と訳され、複数の要素をある規則に従い一直線にデ ...

 

データが大きくなってきたらcsvとかで外部に書き出し
ユーザーデータがある場合はサーバーで保管

今回はPlayerPrefsを使うと良さそう
jasonはよくわからなかったので、今回は使わない

 

もうちょっと勉強しないと

JSONの形式を完全理解して読み書きできるようになるための記事 | 侍エンジニア塾ブログ(Samurai Blog) – プログラミング入門者向けサイト

JSONの形式を完全理解して読み書きできるようになるための記事 | 侍エンジニアブログ
この記事では「 JSONの形式を完全理解して読み書きできるようになるための記事 」といった内容について、誰でも理解できるように解説します。この記事を読めば、あなたの悩みが解決するだけじゃなく、新たな気付きも発見できることでしょう。お悩みの方はぜひご一読ください。

 

ひとまず簡単な方法でやってみる

所持アイテムとか変数を用意していたものはその値を保存しておいて、起動時にいれる
シーンの状態とかもシーン名をキーで保存しておき、起動時にそれを元にシーンをロードする
リセットする時はPlayerPrefsのデータを消せば良い

public class SaveGameData : MonoBehaviour
{
    GameObject GM;
    GameManager script;
    // Start is called before the first frame update
    void Start()
    {
        GM = GameObject.Find("GameManager");//GameManagerのオブジェクトを取得
        script = GM.GetComponent<GameManager>();

        //起動時にデータを読み込んでGameManagerに値を渡す。キーがなければ0を返す
        script.room_now = PlayerPrefs.GetInt("Room_now");//保存してあるRoom_nowの番号
        script.wallNo = PlayerPrefs.GetInt("wallNo");  //保存してあるWallNoの番号
        LoadAllActive();
    }

    public void SaveSceneData()//保存まとめ
    {
        PlayerPrefs.SetInt("Room_now", script.room_now); //現在のRoom_nowの番号を保存
        PlayerPrefs.SetInt("wallNo", script.wallNo);    //現在のWallNoの番号を保存
        SaveAllActive();
        PlayerPrefs.Save();
    }
     public void SaveAllActive()//表示状態の保存用
     {
     }
     public void LoadAllActive()//表示状態のロード用
     {
     }
}

 

とりあえず部屋の番号とかは簡単にこんな感じで保存することにした

これで一応保存できてた

GameManager.csという別のスクリプトにRoom_nowとかWallNoは入っている

 

たぶんいい書き方ではないけどハマりそうだったのでとりあえず簡単なやり方で進むことにする

悩んだのは表示状態の保存の方

 

オブジェクトの表示状態の保存方法

今のところできなかった

できました

 

下記のリンクを参考に表示状態を記録するスクリプトをひとまず書いてみた

<参考>
unity3d – Unityのシーンにgameobjectsの状態を保存してロードする

https://stackoverrun.com/ja/q/10094967

 

オブジェクトごとに個別にPlayerPrefsのスクリプトを書きたくない
キーの作成時に名前に変数を使えばスクリプトは少なくて済む

シーン名+オブジェクト名をキーの名前にしておく
オブジェクトの表示を切り替える時は、そのキーに表示状態を一緒に保存しておく
あとは起動時にその値をチェックして、表示を切り替える。

この場合はオブジェクトの名前がシーン内で固有でないといけない

 

状態の保存までは下記ののスクリプトをオブジェクトごとに付ければできた


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

public class SaveActive : MonoBehaviour
{

void OnEnable()
{
string keyName = gameObject.transform.parent.gameObject.name + gameObject.name; //親の名前+オブジェクト名でキーを作成
PlayerPrefs.SetInt(keyName, 1);//表示に切り替わった時に、名前のキーで1を保存しておく
PlayerPrefs.Save();

}

void OnDisable()
{
string keyName = gameObject.transform.parent.gameObject.name + gameObject.name; //親の名前+オブジェクト名でキーを作成
PlayerPrefs.SetInt(keyName, 0);//非表示に切り替わった時に、名前のキーで0を保存しておく
PlayerPrefs.Save();

}
}

*上記のやり方だとシーン遷移時表示状態だったオブジェクトも非表示で保存されてしまって上手くロードができなかった

 

ということで、素直に全てのオブジェクトの表示状態を記録することにしました

下記でひとまず表示状態の保存とロードはできた


 public void SaveAllActive()
    {
        //各部屋のオブジェクトの表示状態を保存する。
        int n = 0;
        while (n < 6)
        {
            //各部屋の下階層のオブジェクトを全て取得し保存する。1が表示、2が非表示。初期値の0のままのものには何もしない
            Transform[] children = script.rooms[n].GetComponentsInChildren<Transform>(true);
            foreach (var obj in children)
            {
                string keyName = script.rooms[n].name + obj.name; //部屋の名前+オブジェクト名でキーを作成

                if (obj.gameObject.activeSelf)
                {//表示状態ならオブジェクト名で1を保存
                    PlayerPrefs.SetInt(keyName, 1);//非表示に切り替わった時に、名前のキーで2を保存しておく
                    //Debug.Log($"{keyName}が" + PlayerPrefs.GetInt(keyName) + "で保存された");
                }
                else
                {
                    //、非表示ならオブジェクト名で2を保存
                    PlayerPrefs.SetInt(keyName, 2);//非表示に切り替わった時に、名前のキーで2を保存しておく
                    //Debug.Log($"{keyName}が" + PlayerPrefs.GetInt(keyName) + "で保存された");
                }



            }
            ++n;
        }
    }
    private void LoadAllActive()
    {

        //起動時に各部屋のオブジェクトの表示状態をロードする。
        int n = 0;
        while (n < 6)
        {
            //各部屋の下階層のオブジェクトを全て取得し、PlayerPrefsの保存と照らし合わせて表示を変更する。1が表示、2が非表示。初期値の0のままのものには何もしない
            Transform[] children = script.rooms[n].GetComponentsInChildren<Transform>(true);
            foreach (var obj in children)
            {
                string keyName = script.rooms[n].name + obj.name; //部屋の名前+オブジェクト名を取得
                if (PlayerPrefs.GetInt(keyName) == 1)
                {
                    obj.gameObject.SetActive(true);
                    //Debug.Log(keyName + "は" + PlayerPrefs.GetInt(keyName) + "でロードされた");
                }
                else if (PlayerPrefs.GetInt(keyName) == 2)
                {
                    obj.gameObject.SetActive(false);
                    //Debug.Log(keyName + "は" + PlayerPrefs.GetInt(keyName) + "でロードされた");

                }
            }
            ++n;
        }
    }

部屋の名前+オブジェクトの名前をキーの名前にしてる。

名前がダブらなければ大丈夫

 

結局一個一個保存する形になった

実機でも確認済み

ちょっと負荷が高いのかもしれないけど、とりあえず落ちはていない

 

<参考>

How to use GetComponentsInChildren ( Example :- to print all children gameobject'sname ) - Unity Answers
Unity is the ultimate game development platform. Use Unity to build high-quality 3D and 2D games, deploy them across mobile, desktop, VR/AR, consoles or the Web...
ヒエラルキーのオブジェクト取得
ヒエラルキーにある全てのオブジェクト取得 void Start() { // ヒエラルキー上のすべてのオブジェクトを取得 Object[] allGameObject = Resources.Find

 

あとはアプリの終了する時にも保存をしたい。

これは保存用にまとめてある関数を呼び出せばOK

   //一時停止or再開時
    private void OnApplicationPause(bool pauseStatus)
    {

        //一時停止
        if (pauseStatus)
        {
            SaveSceneData();
        }

    }
    //終了処理
    private void OnApplicationQuit()
    {
        SaveSceneData();
    }

<参考>

アプリの一時停止or終了時の処理【Unity】 – (:3[kanのメモ帳]

ゲーム中に何か取得したりとか、ステージが変わったり、とかなんか会った時にはセーブして欲しいかな。

 

Unity上でのPlayerPrefsの削除

Unityで保存してあるPlayerPrefsを削除するには

Edit → Clear All PlayerPrefs

で削除できるとのこと

 

Unityでテストしてる間もPlayerPrefsのデータが残ることを知らなかった…

そのせいでちょっとハマりました

<参考>

Unity 2018.3からPlayerPrefsを全削除するメニューアイテムが標準で追加された - Qiita
Unity 2018.3から、PlayerPrefsを削除するメニューアイテムが、標準で追加されました。 次の画像のように、Edit > Clear All PlayerPrefsを選択すると、 次の画像のようなダイアロ...

 

表示順序の保存をどうするか

あと保存できてないのはオブジェクの並び順。

Unityでボタンが重なっている時はと手前のものが押されるので、状況に合わせて順番を入れ替えたりしていた。

その順番は保存されていないので、そこは解決しないといけない