ゲームデータについて(4)可変データ

本記事では可変データの初期化・セーブロード処理について説明をします。

可変データクラスの実装

味方キャラクターのデータを作るケースを例とします。
Assets/GameData/にVariableDataというディレクトリを作成し、Assets/GameData/VariableData/にCharacterData.csというファイルを作成します。

using System;

[Serializable]
public class CharacterData
{
    private int _id = -1;
    public int id
    {
        get { return _id; }
        set { _id = value; }
    }
    private int _level = 0;
    public int level
    {
        get { return _level; }
        set { _level = value; }
    }
    private int _hp = 0;
    public int hp
    {
        get { return _hp; }
        set { _hp = value; }
    }

    private int _mp = 0;
    public int mp
    {
        get { return _mp; }
        set { _mp = value; }
    }

    private int _attack = 0;
    public int attack
    {
        get { return _attack; }
        set { _attack = value; }
    }

    private int _defense = 0;
    public int defense
    {
        get { return _defense; }
        set { _defense = value; }
    }

    private int _agility = 0;
    public int agility
    {
        get { return _agility; }
        set { _agility = value; }
    }

    public CharacterData()
    {
        Clear();
    }

    public void Init( int _id, int _level, int _hp, int _mp, int _attack, int _defense, int _agility )
    {
        id = _id;
        level = _level;
        hp = _hp;
        mp = _mp;
        attack = _attack;
        defense = _defense;
        agility = _agility;
    }
    
    public void Clear()
    {
        id = -1;
        level = 0;
        hp = 0;
        mp = 0;
        attack = 0;
        defense = 0;
        agility = 0;
    }    
    
    public void Copy( CharacterData data )
    {
        id = data.id;
        level = data.level;
        hp = data.hp;
        mp = data.mp;
        attack = data.attack;
        defense = data.defense;
        agility = data.agility;
    }
    
    public bool IsValid()
    {
        return id != -1;
    }
}
可変データ管理クラスの作成

可変データも固定データと同様に種類が増えていく想定なので一元管理するクラスを作ります。
Assets/GameData/VariableDataにVariableDataManager.csを作成します。

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

public static class VariableDataManager
{
    private static List<CharacterData> _characterList = null;
    private static List<CharacterData> characterList
    {
        get { return _characterList; }
        set { _characterList = value; }
    }

    static VariableDataManager()
    {
        Initialize();
    }
    private static void Initialize()
    {
        characterList = new List<CharacterData>();
    }

    public static CharacterData GetCharacterData(int index)
    {
        if (index < 0 || index >= characterList.Count)
        {
            Assert.IsTrue(false);
            return null;
        }

        return characterList[index];
    }
    public static CharacterData GetCharacterDataFormID(int id)
    {
        if (id < 0)
        {
            Assert.IsTrue(false);
            return null;
        }

        CharacterData characterData = characterList.Find(x => x.id == id);
        if (characterData == null)
        {
            Assert.IsTrue(false);
            return null;
        }

        return characterData;
    }
    public static int GetCharacterDataNum()
    {
        return characterList.Count;
    }
}
初期化処理

可変データは固定データのように外部ファイルをロードする必要はありませんが、ゲーム開始時に特定の数値で初期化をする必要があります。
先ほど作成したVariableDataManager.csに以下関数を追記します。
初期化時の値は固定データとして管理した方がよいかもしれません。

    public static void CreateData()
    {
        characterList.Clear();
        for (int index = 0; index < 4; ++index)
        {
            CharacterData characterData = new CharacterData();
            characterData.Init(index, 10, 10, 10, 10, 10, 10); // TODO:キャラクターの初期データは固定データとして管理する.
            characterList.Add(characterData);
        }
    }
初期化処理の呼び出し

先ほど作成した可変データの初期化処理をGameDataManagerから呼ぶようにします。
Assets/GameData/GameDataManager.csを開いて以下のように修正します。

    public static void Initialize()
    {
        // 固定データのロード.
        FixedDataManager.Serialize();

        // ここからを追加.
        // 可変データの新規作成.
        VariableDataManager.CreateData();
        // ここまでを追加.
    }

GameDataManager.Initialize();が呼ばれる時に可変データの初期化も行われることを確認します。

セーブロード処理

可変データはゲームの進行によって変化するデータですので、セーブロード処理を作る必要があります。
Assets/GameData/VariableData/VariableDataManager.csを以下のように修正。

using System.Collections.Generic;
using System.IO;    // これを追加.
using System.Runtime.Serialization.Formatters.Binary;    // これを追加.
using UnityEngine.Assertions;

(略)
    private static void Initialize()
    {
        characterList = new List<CharacterData>();
    }

    // ここからを追加.
    public static void SaveData(FileStream fileStream)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();

            binaryFormatter.Serialize(memoryStream, characterList);

            fileStream.Write(memoryStream.ToArray(), 0, (int)memoryStream.Length);
        }
    }

    public static void LoadData(FileStream fileStream)
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        characterList = binaryFormatter.Deserialize(fileStream) as List<CharacterData>;
    }
    // ここまでを追加.

追加したセーブロード関数をGameDataManagerから呼び出すようにします。
Assets/GameData/GameDataManager.csを開いて以下のように修正します。

// ここからを追加.
using System.IO;
// ここまでを追加.

public static class GameDataManager
{

(略)

    // ここからを追加.
    // セーブ処理.
    public static void SaveData(int fileNo)
    {
        using (FileStream serializeFile = new FileStream("SaveData" + fileNo + ".bin", FileMode.Create))
        using (MemoryStream memoryStream = new MemoryStream())
        {
            VariableDataManager.SaveData(serializeFile);
        }
    }

    // ロード処理.
    public static void LoadData(int fileNo)
    {
        using (FileStream serializeFile = new FileStream("SaveData" + fileNo + ".bin", FileMode.Open))
        {
            VariableDataManager.LoadData(serializeFile);
        }
    }
    // ここまでを追加.

}

適当な場所で以下関数を呼んでセーブ・ロードが正常に動作することを確認する。

GameDataManager.SaveData(int);
GameDataManager.LoadData(int);

これで可変データの初期化・セーブ・ロードができるようになりました。

セーブデータの生成場所について

上記のサンプルコードではセーブデータファイルはUnityのプロジェクトの直下に生成されます。
Unity Editor上ならこれでも問題ないと思いますが、各プラットフォームで動作させる場合は適切なパスを指定しましょう。