LoginSignup
15

More than 3 years have passed since last update.

プレイデータの保存/読込

Last updated at Posted at 2018-02-10

概要

ゲームのプレイデータの保存を、Userクラスに集約して、jsonにして保存するやり方。生徒向け。
※クラスのカプセル化については割愛
2020/5/21 カプセル化したパターンも追記

例で想定する仕様は、クエストごとにプレイデータが存在するってかんじ。
ユーザーに保存する予定の構造を以下と想定する。

- ユーザー
    - ニックネーム
    - レベル
    - 経験値
    - クエスト結果(配列)    
        - クエストID
        - チャレンジ回数
        - クリア回数
        - ハイスコア

データを入れるクラスを作成

新規でUser.csQuestResult.csを作成する。
Unityで.csファイルを作成した時に最初から書いてあるやつは邪魔だから全部消してよし。
MonoBehaviourは継承させない。

User.cs
using System.Collections.Generic;

[System.Serializable]
public class User {
    public string nickname;
    public int level, exp;
    public List<QuestResult> questResults;
}
QuestResult.cs
[System.Serializable]
public class QuestResult {
    public int
        id,
        challengeCount,
        clearCount,
        highScore;
}

Inspectorに出してみる

新規でGameManager.csを作成し、中身を下記のように編集する。

GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour {

    public User user;

    void Start () {
        //あとで
    }

    void Update () {
        //あとで
    }
}

CreateEmptyで空のGameObjectを作成してGameManager.csをくっつけると、
Inspectorでこのように表示されるはず。
https://gyazo.com/39468ef08515fedafaba29255244dfca

jsonにして保存する

jsonとは

この記事が分かりやすかった気がするので読んで。
https://qiita.com/SotaSuzuki/items/c3b46c4e24c1ca9b4d37

コンソールにjsonを出力してみる

UnityでJsonを扱う場合は、JsonUtilityっていう便利なクラスが用意されてます。

GameManager.csで、スペースキーを押したらUserデータをjsonにしてコンソールに出してみる、
んだけど、データになんか入ってたほうがおもしろいと思うのでちょっといれてみよう。
https://gyazo.com/dce7bcf31270aea4963919b7ebbe9d68

GameManager.csを編集

GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour {

    public User user;

    void Start () {
        //あとで
    }

    void Update () {
        if (Input.GetKeyDown(KeyCode.Space)) {
            string json = JsonUtility.ToJson( user );
            print ( json );
        }
    }
}

実行してSpaceキーを押してConsoleを見てみるとこんな感じになってるはず
https://gyazo.com/b508ac19c76a9552d76799addb437ce7

ちなみにこの文字列を展開してあげるとこんな感じ

{
  "nickname": "imadake398yen",
  "level": 39,
  "exp": 12345,
  "questResults": [
    {
      "id": 1,
      "challengeCount": 4,
      "clearCount": 2,
      "highScore": 78
    },
    {
      "id": 2,
      "challengeCount": 6,
      "clearCount": 4,
      "highScore": 92
    }
  ]
}

jsonを展開してみる方法はたくさんあるけどwebサービスでもあるのでリンク乗っけとく。
コンソールに出てきたやつをコピペすれば展開してくれる。
http://www.ctrlshift.net/jsonprettyprinter/

PlayerPrefsで保存 -> 読み込み

じゃあ、レベルがあがったら保存する。っていうふうにしてみるか。
とりあえずスペースキー押したらレベルアップにしとくね。とりあえず。

GameManager.csを編集

GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour {

    const string _SaveKey = "UserData";
    public User user;

    void Start () {
        //読込
        string json = PlayerPrefs.GetString(_SaveKey);
        user = JsonUtility.FromJson<User>(json);
    }

    void Update () {
        if (Input.GetKeyDown(KeyCode.Space)) {
            LevelUp();
        }
    }

    void LevelUp () {
        user.level += 1;
        Save();
    }

    //保存
    void Save () {
        string json = JsonUtility.ToJson( user );
        PlayerPrefs.SetString(_SaveKey, json);
    }
}

これで再生時に読み込み → スペースキーでレベルアップして保存ができてることが
Inspectorで確認できると思う。
逆に現状はInspectorでしか確認できないからTextやらに表示させたり、
nicknameをInputFieldで編集したりしたらいいと思います。

Userクラスのカプセル化をやってみる

想定仕様は察してください。
GameManagerは葬ります。

User.cs
using UnityEngine;
using System.Collections.Generic;
using System.Linq;

[System.Serializable]
public class User {

    [SerializeField] private string nickname;
    [SerializeField] private int id, level, exp;

    public string Nickname { get{ return nickname; } }
    public int Level { get{ return level; } }
    public int Exp { get{ return exp; } }
    public List<QuestResult> QuestResults {
       get { return QuestResult.GetByUserID(id); }
    }

    private static User self;
    public static User Self {
        get { return (self != null) ? self : Load(); }
    }

    const string SAVE_KEY = "UserData";
    private static User Load () {
        if (PlayerPrefs.HasKey(SaveKey)) {
            string json = PlayerPrefs.GetString(SAVE_KEY);
            self = JsonUtility.FromJson<User>(json);
        } else {
            self = new User();
            self.level = 1; //これはコンストラクタをサボってる
        }
        return self;
    }

    private void Save () {
        string json = JsonUtility.ToJson(self);
        PlayerPrefs.SetString(SAVE_KEY, json);
    }

    public void UpdateNuckname (string nickname) {
        this.nickname = nickname;
        Save();
    }

    public void LevelUp () {
        level += 1;
        Save();
    }

    public void SaveQuestResult (int id, ResultState state, int score) {
        var result = questResults.Find(q => q.ID == id);
        if (result == null) 
            questResults.Add( new QuestResult(id, state, score) );
        else result.UpdateData(state, score);
        Save();
    }
}
QuestResult.cs
using UnityEngine;
using System.Collections.Generic;
using System.Linq;

[System.Serializable]
public class QuestResult {
    [SerializeField] private int
        id,
     userId,
        challengeCount,
        clearCount,
        highScore;

    public int ID { get{ return id; } } 
    public int ChallengeCount { get{ return challengeCount; } } 
    public int ClearCount { get{ return clearCount; } }
    public int HighScore { get{ return highScore; } }

    //ロードは後で書く
    public static List<QuestResult> All { get; set; }

    public static List<QuestResult> GetByUserID (int userId) {
        return All.Where(q => q.userId == userId).ToList();
    }

    public QuestResult (int id, int userId, ResultState state, int score) {
        this.id = id;
        this.userId = userId;
        this.clearCount = clearCount;
        this.highScore = score;
        this.challengeCount = 1;
    }

    public void UpdateData (ResultState state, int score) {
        challengeCount += 1;
        clearCount = (state == ResultState.Clear) ? clearCount++ : clearCount;
        highScore = (highScore < score) ? score : highScore;
    }
}

public enum ResultState {
    Clear,
    Failed
}

外部からの使用例はこんなかんじ。

var user = User.Self;

user.UpdateNickname("たかし");

user.LevelUp();

user.SaveQuestResult(questID, resultState, score);

書いてて思ったけどexpを受け取ってlevelは直接あげないほうが良かったね
まぁ仕様書いてないしね

レベルアップしたときにUnityEventでOnLevelUpとか仕込んでいきたいね。
気が向いたら更新します。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15