当我们需要读取战机的属性时,有时会发生冲突,我们不知道需要从配置中读取属性还是从存档中读取属性。

这时我们使用配置管理器,在游戏一开始时初始化所有配置,把配置信息都写入存档中。

ConfigMgr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using LitJson;

public class ConfigMgr : NormalSingleton<ConfigMgr>
{
public void Init()
{
var config = ReaderMgr.Instance.GetReader(Path.INIT_PLANE_CONFIG);
config["planes"].Get<JsonData>(data =>
{
foreach (JsonData item in data)
{
foreach (string key in item.Keys)
{
if (key == "planeId") continue;
string saveKey = KeysUtil.GetPropertyKeys(int.Parse(item["planeId"].ToJson()), key);
if (!DataMgr.Instance.Contains(saveKey))
{
DataMgr.Instance.Set(saveKey,item[key]);
}
}
}
});
}
}

GameRoot一开始时调用

1
2
3
4
5
6
7
8
private void Start()
{
//因为我们的配置会刷进存档里,如果再次修改配置的值,存档并不会更新,所以每次修改配置时清空一次存档
DataMgr.Instance.ClearAll();//+++只有修改配置后清空一次,平时可以注释掉
ConfigMgr.Instance.Init();//+++
InitCustomAttributes initCustomAttributes = new InitCustomAttributes();
//...
}

修改存档相关

IDataMemory

添加Contains方法,用于判断存档中是否有对应的值

1
2
3
4
5
public interface IDataMemory 
{
//...
bool Contains(string key);
}

DataMgr

1
2
3
4
public bool Contains(string key)
{
return m_data.Contains(key);
}

PlayerPrefsMemory

1
2
3
4
public bool Contains(string key)
{
return PlayerPrefs.HasKey(key);
}

KeysUtil

因为每一个飞机的属性值名称相同,但是飞机的id不同,我们需要一个工具来提供每个飞机的属性的精确键值。这些精确的键值能够帮助我们精确地使用DataMgr进行存档读档。

1
2
3
4
5
6
7
public class KeysUtil
{
public static string GetPropertyKeys(int id,string name)
{
return id + name;
}
}

JsonReader相关

我们需要遍历一个JsonData对象目前所有的Key值,方便ConfigMgr来设定存档所用的键值。LitJson.JsonData实现了类似IListIDictionary等接口,我们可以直接将JsonData对象放在foreach语法当中遍历,从而获取所有key值。JsonData也提供了Keys属性,为ICollection<string>类型。

IReader

1
2
3
4
5
6
7
using System.Collections.Generic;

public interface IReader
{
//...
ICollection<string> Keys();
}

JsonReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   private T GetValue<T>(JsonData data)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
try
{
if (converter.CanConvertTo(typeof(T)))
{
return (T)converter.ConvertTo(data.ToString(), typeof(T));
}
else
{
return (T)(object)data;//JsonData可以通过这种方式进行转换
}
}
catch (Exception ex)
{
Debug.LogError("当前类型转换不支持:" + typeof(T).Name + " data: " + data);
return default;
}
}

public ICollection<string> Keys()
{
if(_tempData == null) return new string[0];
return _tempData.Keys;
}

如果我们想要调用到Keys(),必须保证当前的_tempData缓存的JsonData是我们想要的数据。

但是我们可以直接获取JsonData,不使用Keys()方法,我们修改了GetValue<T>方法,让它可以返回JsonData,之所以修改是因为TypeConverter不支持转换为JsonData,我们需要(T)(object)data这种操作。

DataUtil

这个工具使用静态拓展来拓展DataMgr.Set方法,更加方便地进行类型转换。虽然我们调用PlayerPrefsMemory.Set时会自动判断数据类型,但是有些存储在Json中的数据都是string类型,我们使用这个拓展方法来把json中的数据先更准确的进行转化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using LitJson;
using UnityEngine;

public static class DataUtil
{
public static void Set(this DataMgr dataMgr, string key, JsonData json)
{
IJsonWrapper jsonWrapper = json;
switch (json.GetJsonType())
{
case JsonType.None:
Debug.LogError("当前JsonData数据为空");
break;
case JsonType.String:
DataMgr.Instance.Set(key, jsonWrapper.GetString());
break;
case JsonType.Int:
DataMgr.Instance.Set(key, jsonWrapper.GetInt());
break;
case JsonType.Long:
DataMgr.Instance.Set(key, (int)jsonWrapper.GetLong());
break;
case JsonType.Double:
DataMgr.Instance.Set(key, (float)jsonWrapper.GetDouble());
break;
}
}
}

LitJson.JsonData.GetJsonType方法是LitJson内部提供的获取类型信息的方法。使用JsonType枚举来表示每个json所代表的信息。

JsonData类实现了IJsonWrapper接口,这个接口就是用来获取Json对应的类型的值的,但是在JsonData类里面这个接口被显式调用了,这意味着我们不能使用类似jsonData.GetInt()来获取值,只能在方法内先通过声明IJsonWrapper变量来转换jsonData从而调用接口提供的方法。