初心者がUnityでなんかしちゃうぞBlog

Unity初心者は、ゲーム開発の夢を見るか?

【JSON】 Unityのゲームでセーブとロードを実装したい!

f:id:yuu9048:20190526161733g:plain

ゲームを作る上で大切なのは、データの保管です。せっかく遊んだのにデータが保存されないゲームでは勿体無いですよね。

Unityではデータの保存には色々なアプローチがあります。一番お手軽なのはPlayerPrefsというアプローチ。intやstring型のデータを保存することができます。 たとえば、シューティングゲームのハイスコアやステージのクリアフラグなど単体で扱う簡単なデータならこれで管理してしまえばあっという間にデータの保存と読み込みを実現可能です。

しかし、基本的にローカルに保存する設定保存のためのような使い方がおそらく本筋であり、複雑化するゲームデータを保存するのには色々な工夫が必要となってきます。たとえば複数セーブに対応しようと思うと大変な労力が必要となります。、

そこで、今回はよく言われているJSONを使って外部ファイルにデータを保存し、それを読み込んだりするのに挑戦してみたいと思います。レッツ実装セーブ&ロード!

クリッカーゲームを作る

おいおいまたかよと思われるかもしれませんが、今回もクリッカーゲームを作りながらJSONについて学んでいきます。 今回はより簡略化して、uGUIのボタンをメインに使ってしまおうかと思います。

下準備

まずはさくっと下地をつくります。uGUIでボタンを1個作ってシーンに配置していきましょう。 デザインなどは特になんでも良いです。今回は「押しちゃいけないボタンを押す」という画期的なゲームになります。(どこかで聞いたことある内容ですね…)

f:id:yuu9048:20190526141528p:plain

ついでにテキストも置いておきます。

f:id:yuu9048:20190526150832p:plain

コードを書くぞ(クリッカー編)

JSONのためにクラス構造でデータを扱うと良いらしいので、クラスを作っていきます [System.Serializable] をつけて宣言しないといけないらしいのでそれをカバーしていきましょう。

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

public class Test : MonoBehaviour 
{

    [SerializeField] Text counterText;

    [System.Serializable]
    public class PlayerData {
        public int clickCount;
        public string playerName;
    }

    PlayerData myData = new PlayerData();

    public void OnClickEvent() {
        myData.clickCount++;
        counterText.text = myData.clickCount.ToString();
    }
}

ここの記述でJSONに渡すためにデータ構造を定義しています。下準備のようなものですね JSONシリアライズするために必要らしいのでつけています。あとはPlayerDataというクラスがあり、そのフィールドにはclickCountplayerNameというものがあるだけです。

    [System.Serializable]
    public class PlayerData {
        public int clickCount;
        public string playerName;
    }

これを書いてボタンにアタッチ、ボタンのOnClickでOnClickEventを呼ぶようにしています。 ついでにテキストもアタッチしておきましょう!

f:id:yuu9048:20190526152139p:plain

これで基本的なクリッカーのシステムができあがりました f:id:yuu9048:20190526153135g:plain

セーブシステムを作る

プレイヤーネームをつけてファイルを保存し、そして読み込めるようにしていきたいと思います。 各種UIを良い感じに配置していきましょう

f:id:yuu9048:20190526153850p:plain

ちょっと雑なやり方にはなりますが、さっきのクリッカーボタンのスクリプトを編集してInputFieldへの参照も確保しておきます

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

public class Test : MonoBehaviour 
{
    [SerializeField] InputField inputArea;
    [SerializeField] Text counterText;

    [System.Serializable]
    public class PlayerData {
        public int clickCount;
        public string playerName;
    }

    PlayerData myData = new PlayerData();

    public void OnClickEvent() {
        myData.clickCount++;
        counterText.text = myData.clickCount.ToString();
    }
}

セーブ機能を書いていく

下記サイトを参考にセーブする仕組みを書いていきます www.sejuku.net

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.IO;

public class Test : MonoBehaviour 
{
    [SerializeField] InputField inputArea;
    [SerializeField] Text counterText;

    [System.Serializable]
    public class PlayerData {
        public int clickCount;
        public string playerName;
    }

    PlayerData myData = new PlayerData();

    public void OnClickEvent() {
        myData.clickCount++;
        counterText.text = myData.clickCount.ToString();
    }

    public void SavePlayerData() {
        StreamWriter writer;
        var playerName = inputArea.text;
        myData.playerName = playerName;

        string jsonstr = JsonUtility.ToJson(myData);

        writer = new StreamWriter(Application.dataPath + "/save"+ playerName + ".json", false);
        writer.Write(jsonstr);
        writer.Flush();
        writer.Close();
    }
}

string jsonstr = JsonUtility.ToJson(myData); でいよいよJSONを利用しています。PlayerData型で作ったデータ構造体をJSON型に変換しています。 たった一行で実装できるのでとっても簡単ですね。

さっき作成したセーブボタンのOnClickにSavePlayerDataをアタッチしておきます。 それでは名前をつけて保存してみましょう!!

f:id:yuu9048:20190526155429p:plain

f:id:yuu9048:20190526155723p:plain

セーブされているのが確認できました!

ロードシステムを作る

JSONにしてセーブするのですから、ロード機能を作るには逆です。JSONからクラスデータに戻してあげればOKですね。

    public void LoadPlayerData() {
        string datastr = "";
        var playerName = inputArea.text;
        StreamReader reader;

        reader = new StreamReader(Application.dataPath + "/save" + playerName + ".json");
        datastr = reader.ReadToEnd();
        reader.Close();

        myData = JsonUtility.FromJson<PlayerData>(datastr); // ロードしたデータで上書き
        Debug.Log(myData.playerName + "のデータをロードしました");
        counterText.text = myData.clickCount.ToString();
    }

入力されている名前のセーブデータを読み込んできます。その後はJsonUtility.FromJson<PlayerData>(datastr)で読み込んだデータを、PlayerData型に変換しているイメージです。 ロードしたデータをそのまま現在のデータに上書きして、テキストエリアにも反映しています。あとはロードボタンに対してLoadPlayerDataをアタッチしておきましょう。

実践編

それでは、さっきセーブしたデータをロードしてみましょう!

f:id:yuu9048:20190526161351g:plain

画面の通り、セーブしたデータをちゃんと読み込むことができています。 見づらいですがコンソール画面には「PlayerName」のデータを読み込んだログが表示されているので、ちゃんとファイルから各種データが読み込めていることがわかります。

それでは一連の流れでやってみましょう! f:id:yuu9048:20190526161733g:plain

  1. カウンターを5回になるまでクリック
  2. [test1]でセーブ
  3. さらに5回クリック
  4. [test2]でセーブ
  5. さらに5回クリック
  6. [test1]をロード(カウント5回がロードされる)
  7. [test2]をロード(カウント10回がロードされる)

セーブとロードの基本的な仕組みを無事にゲームとして実装することができました! 違う名前で共存できるので複数セーブデータにも対応できますね。 データの扱いも比較的カンタンなのでほしいデータをすぐ参照したりすることもできて便利そうです。JSONを使った外部ファイルへのセーブデータ出力。ぜひ活用していきたい機能ですね!