ゲームデータについて(9)ジェネリックの利用

これまで紹介したサンプルプログラムは固定データ・可変データを1種類ずつしか持っていませんが、大抵のゲームでは多数用意することになると思います。
今のままでは各クラス間で重複するコードが多く、データの種類が追加される度にこれらを全て書く必要があります。
C#ジェネリック(C++のテンプレート)を利用することでコード量の削減が見込めます。

固定データクラスのジェネリック

Assets/GameData/FixedData/FixedDataManager.csを修正

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

public static class FixedDataManager
{
    // ここからを追加.
    [Serializable]
    public class FixedDataList<T> where T : class
    {
        private List<T> _list = new List<T>();
        public List<T> list
        {
            get { return _list; }
        }

        public int GetDataCount()
        {
            return list.Count;
        }

        public bool CheckValidIndex(int index)
        {
            return (index >= 0) && (index < GetDataCount());
        }
        public T GetData(int index)
        {
            if (!CheckValidIndex(index))
            {
                Assert.IsTrue(false);
                return null;
            }
            return list[index];
        }
        public void Add(T data)
        {
            list.Add(data);
        }
    }

    private static FixedDataList<FixedMonsterData> _monsterList = null;
    public static FixedDataList<FixedMonsterData> monsterList
    {
        get { return _monsterList; }
    }
    // ここまでを追加.
    // ここからを削除.
    //private static List<FixedMonsterData> _monsterList = null;
    //public static List<FixedMonsterData> monsterList
    //{
    //    get { return _monsterList; }
    //}
    // ここまでを削除.

(中略)

    private static void Initialize()
    {
        // この行を下の行に差し替え.
        //_monsterList = new List<FixedMonsterData>();
        _monsterList = new FixedDataList<FixedMonsterData>(); 
    }

(中略)

    public static void Serialize()
    {
        // この行を下の行に差し替え.
        //_monsterList = ReadSerializedData<List<FixedMonsterData>>("FixedData/FixedMonsterData");
        _monsterList = ReadSerializedData<FixedDataList<FixedMonsterData>>("FixedData/FixedMonsterData"); 
    }

    // ここからを削除.
    //public static int GetMonsterDataCount()
    //{
    //    return _monsterList.Count;
    //}
    //public static bool CheckValidMonsterIndex(int index)
    //{
    //    return (index >= 0) && (index < GetMonsterDataCount());
    //}
    //public static FixedMonsterData GetMonsterData(int index)
    //{
    //    if (!CheckValidMonsterIndex(index))
    //    {
    //        Assert.IsTrue(false);
    //        return null;
    //    }
    //    return _monsterList[index];
    //}
    // ここまでを削除.
}

Assets/Editor/FixedDataConverter.csを修正

            // この行を下の行に差し替え.
            //List<FixedMonsterData> monsterList = FixedDataManager.monsterList;
            FixedDataManager.FixedDataList<FixedMonsterData> monsterList = FixedDataManager.monsterList;

Assets/GameData/TextData/TextDataManager.csを修正

            // モンスター名.
            case REPLACE_TYPE.MONSTER:
                {
                    if (counter >= replaceMonsterParams.Count)
                    {
                        Assert.IsTrue(false);
                        break;
                    }
                    int id = replaceMonsterParams[counter];
                    // この行を下の行に差し替え.
                    //strAdd = FixedDataManager.GetMonsterData(id).GetName();
                    strAdd = FixedDataManager.monsterList.GetData(id).GetName();
                }
                break;
可変データクラスのジェネリック

VariableDataManagerクラスのGetCharacterDataFormID(int)関数はCharacterDataクラスのメンバint _idを参照しています。

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

        CharacterData characterData = characterList.Find(x => x.id == id); // ここでメンバ_idを参照.
        if (characterData == null)
        {
            Assert.IsTrue(false);
            return null;
        }

        return characterData;
    }

このままではジェネリックを利用できないので、可変データのベースクラスとしてメンバint idのみを持つBaseVariableDataクラスを作成し、ジェネリック型制約(where句)を利用して実装、CharacterDataはBaseVariableDataの派生という形にします。
Assets/GameData/VariableData/BaseVariableData.csを作成

using System;

[Serializable]
public class BaseVariableData
{
    private int _id = -1;
    public int id
    {
        get { return _id; }
        set { _id = value; }
    }

    public void Init(int _id)
    {
        id = _id;
    }

    public void Clear()
    {
        id = -1;
    }

    public void Copy(BaseVariableData data)
    {
        id = data.id;
    }

    public bool IsValid()
    {
        return id != -1;
    }
}

Assets/GameData/VariableData/CharacterData.csを修正

using System;

[Serializable]
// ここからを削除.
//public class CharacterData
//{
//    private int _id = -1;
//    public int id
//    {
//        get { return _id; }
//        set { _id = value; }
//    }
// ここまでを削除.
// ここからを追加.
public class CharacterData : BaseVariableData
{
// ここまでを追加.

(中略)

    public void Init( int _id, int _level, int _hp, int _mp, int _attack, int _defense, int _agility )
    {
        // この行を下の行に差し替え.
        //id = _id;
        base.Init(_id);
        level = _level;
        hp = _hp;
        mp = _mp;
        attack = _attack;
        defense = _defense;
        agility = _agility;
    }

    // ここからを削除.
    //public void Clear()
    //{
    //    id = -1;
    // ここまでを削除.
    // ここからを追加.
    new public void Clear()
    {
        base.Clear();
    // ここまでを追加.
        level = 0;
        hp = 0;
        mp = 0;
        attack = 0;
        defense = 0;
        agility = 0;
    }    
    
    public void Copy( CharacterData data )
    {
        // この行を下の行に差し替え.
        //id = data.id;
        base.Copy(data); 
        level = data.level;
        hp = data.hp;
        mp = data.mp;
        attack = data.attack;
        defense = data.defense;
        agility = data.agility;
    }

    // ここからを削除.
    //public bool IsValid()
    //{
    //    return id != -1;
    //}
    // ここまでを削除.
    // ここからを追加.
    new public bool IsValid()    
    {
        return base.IsValid();
    }
    // ここまでを追加.

Assets/GameData/VariableData/VariableDataManager.csの修正

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

public static class VariableDataManager
{
    // ここからを追加.
    [Serializable]
    public class VariableDataList<T> where T : BaseVariableData
    {
        private List<T> _list = new List<T>();
        private List<T> list
        {
            get { return _list; }
            set { _list = value; }
        }

        public void Add(T data)
        {
            list.Add(data);
        }

        public void Clear()
        {
            list.Clear();
        }

        public T GetData(int index)
        {
            if (index < 0 || index >= list.Count)
            {
                Assert.IsTrue(false);
                return null;
            }

            return list[index];
        }

        public T GetDataFromID(int id)
        {
            if (id < 0)
            {
                Assert.IsTrue(false);
                return null;
            }

            T data = list.Find(x => x.id == id);
            if (data == null)
            {
                Assert.IsTrue(false);
                return null;
            }

            return data;
        }
        public int GetDataCount()
        {
            return list.Count;
        }
    }

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

    // ここまでを追加.
    // ここからを削除.
    //private static List<CharacterData> _characterList = null;
    //private static List<CharacterData> characterList
    //{
    //    get { return _characterList; }
    //    set { _characterList = value; }
    //}

(中略)

    private static void Initialize()
    {
        // この行を下の行に差し替え.
        //characterList = new List<CharacterData>();
        characterList = new VariableDataList<CharacterData>();
    }

(中略)

    public static void LoadData(FileStream fileStream)
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        // この行を下の行に差し替え.
        //characterList = binaryFormatter.Deserialize(fileStream) as List<CharacterData>;
        characterList = binaryFormatter.Deserialize(fileStream) as VariableDataList<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;
    //}
    // ここまでを削除.