在开发企业级游戏/VR/AR产品时候,我们总是希望可以总结出一些通用的技术体系,框架结构等,为简化我们的开发起到“四两拨千金”的作用。所谓“配置管理”是指一个游戏项目(软件项目),很多需要经常变化的需求或者数据,最好以配置文件的形式存在,从而代替“硬编码”方式。
这里笔者就对游戏产品中大量应用到动态加载的情形,开发出一套通用的配置管理(脚本)工具。该工具可以很方便的对于具备“键值对”特性的配置文件做统一的数据提取处理,特别适合如下应用情形等:
1:“UI预设”/“游戏对象预设”的动态加载。
2:企业级Log 日志系统中关于配置信息(日志的保存路径、日志级别信息)的动态加载。
3:资源(语言)国际化系统中关于语言信息的动态加载。
下图给出本UI框架用到的"语言国际化"对应的Json 配置文件:
(“语言国际化”中文信息的Json配置文件)
目前(2017)国际国内普遍采用的配置管理方式主要有两种: XML与Json 方式。
两者各有优缺点:
XML: 对于数据的精确表示、易读性很高。
微软很多的项目都内置对XML作为配置文件的支持。
(例如: 网站项目:ASP.Net、 WinForm 等)
缺点是读写速度慢,这个问题在移动端尤其突出。
Json: 读写速度快,但是易读性没有XML好,但是可以接受。 所以本框架项目都采用Json作为配置文件。
考虑到目前移动端游戏/VR/AR产品的大量应用,所以笔者在此重点介绍基于Json配置文件的数据解析与配置管理。(Json比传统的XML作为配置文件使用,具备解析速度快,文件尺寸小等突出优点)
什么是Json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C++、C#、Java、JavaScript、Perl、Python等)。这些特性使JSON成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。
JSON 语法 (JSON 语法是 JavaScript 对象表示语法的子集)
特点:
数据在键值对中,数据由逗号分隔。
花括号保存对象,方括号保存数组。
JSON 数据的书写格式是:名称/值对 "firstName":"John"
具体示例:
Json的解析方式:
目前(2017)国际国内对于Json 的解析主要有以下几种方式
命名空间 System.Runtime.Serialization.Json
缺点是需要编写大量代码,自己来封装一些实用方法,不推荐。
目前国内用的最多的Json解析插件: litejson
由于Unity公司也看到了Unity项目中对于大量Json 文件解析操作的需求,所以在Unity5.3以上版本开始原生提供Json的解析API,如下图:
对于Json 的初学者,为了更好的理解后面的配置管理技术讲解,特提供使用Unity的API 对Json 配置文件的解析示例:
Json基本解析示例
对于Unity 原生支持Json 解析方法的最简测试演示。
1 namespace Test
2 {
3 [Serializable]
4 public class Hero
5 {
6 //名称
7 public string Name;
8 //等级
9 public Level MyLevel;
10
11 }
12 }
13
14 namespace Test
15 {
16 [Serializable]
17 public class Level
18 {
19 public int HeroLevel;
20
21 }
22 }
1 /***
2 *
3 * Title: "SUIFW" UI框架项目
4 * 主题: 演示Unity 对Json 解析API
5
6 using System.Collections;
7 using System.Collections.Generic;
8 using UnityEngine;
9
10 namespace Test
11 {
12 public class TestUnityJson : MonoBehaviour {
13
14
15 void Start () {
16 Hero heroObj=new Hero();
17 heroObj.Name = "郭靖";
18 heroObj.MyLevel = new Level() {HeroLevel = 800};
19 //相当于如下写法
20 //Level lev=new Level();
21 //lev.HeroLevel = 800;
22 //heroObj.MyLevel = lev;
23
24 //方法1: Json 序列化工作(对象--> 文件)
25 string strHeroInfo = JsonUtility.ToJson(heroObj);
26 Debug.Log("测试1: 得到的序列化后的字符串="+strHeroInfo);
27
28 //方法2: 反序列化(Json文件--> 对象)
29 Hero heroInfo2 = JsonUtility.FromJson(strHeroInfo);
30 Debug.Log("测试2:得到反序列化对象数值,名称: "+heroInfo2.Name+" 等级: "+heroInfo2.MyLevel.HeroLevel);
31
32 //方法3: 测试覆盖反序列化。
33 Hero hero=new Hero();
34 hero.Name = "杨过";
35 hero.MyLevel = new Level() {HeroLevel = 500};
36
37 //Json 序列化
38 string heroInfo3 = JsonUtility.ToJson(hero);
39 //测试覆盖反序列化
40 JsonUtility.FromJsonOverwrite(heroInfo3, heroObj);
41 Debug.Log("测试3, 得到再次反序列化覆盖的对象信息,名称: "+heroObj.Name+" 等级: "+heroObj.MyLevel.HeroLevel);
42
43
44 }
45
46 }
47 }
对于Json 文件的实战性测试用例演示。
1 /***
2 *
3 * Title: "SUIFW" UI框架项目
4 * 主题: 对于Unity中Resource 目录下的Json 文件的解析Demo
5 * Description:
6 * 功能: yyy
7 *
8 * Date: 2017
9 * Version: 0.1版本
10 * Modify Recoder:
11 *
12 *
13 */
14 using System.Collections;
15 using System.Collections.Generic;
16 using UnityEngine;
17
18 namespace Test
19 {
20 public class TestUnityJson2 : MonoBehaviour {
21
22
23 void Start ()
24 {
25 //提取文件,得到字符串数据
26 TextAsset TaObj=Resources.Load("People");
27 //反序列化 文件-->对象
28 PersonInfo perInfo=JsonUtility.FromJson(TaObj.text);
29 //显示对象中数据
30 foreach (People per in perInfo.People)
31 {
32 Debug.Log(" ");
33 Debug.Log(string.Format("name={0},Age={1}",per.Name,per.Age));
34 }
35 }
36
37 }
38 }
以上代码解释如下:
Unity(5.3以上版本)提供的JsonUtility 提供了三个重要方法
JsonUtility.ToJson() //表示进行序列化操作,把对象序列化为字符串。
JsonUtility.FromJson() ;//表示进行反序列化操作,把Json字符串反序列化为对象。
JsonUtility.FromJsonOverwrite();//是覆盖方式进行反序列化。
有了以上技术储备,我们就可以进行开发“通用配置管理器”了。
第1步: 首先定义通用配置管理器接口与辅助类。
代码如下:
1 /***
2 *
3 * Title: "SUIFW" UI框架项目
4 * 主题: 通用配置管理器接口
5 * Description:
6 * 功能:
7 * 基于“键值对”配置文件的通用解析
8 *
9 * Date: 2017
10 * Version: 0.1版本
11 * Modify Recoder:
12 *
13 *
14 */
15
16 using System;
17 using System.Collections;
18 using System.Collections.Generic;
19 using UnityEngine;
20
21 namespace SUIFW
22 {
23 public interface IConfigManager {
24
25 ///
26 /// 只读属性: 应用设置
27 /// 功能: 得到键值对集合数据
28 ///
29 Dictionary AppSetting { get; }
30
31 ///
32 /// 得到配置文件(AppSeting)最大的数量
33 ///
34 ///
35 int GetAppSettingMaxNumber();
36
37 }
38
39 [Serializable]
40 internal class KeyValuesInfo
41 {
42 //配置信息
43 public List ConfigInfo = null;
44 }
45
46 [Serializable]
47 internal class KeyValuesNode
48 {
49 //键
50 public string Key = null;
51 //值
52 public string Value = null;
53 }
54 }
第2步: 定义Json 解析异常类。
Json 的解析过程如果出错,推荐使用我们自己定义的异常处理,为了更好的发现程序错误,所以自定义Json 解析异常类定义如下:
1 /***
2 *
3 * Title: "SUIFW" UI框架项目
4 * 主题: Json 解析异常
5 * Description:
6 * 功能:专门负责对于JSon 由于路径错误,或者Json 格式错误造成的异常,进行捕获。
7 *
8 * Date: 2017
9 * Version: 0.1版本
10 * Modify Recoder:
11 *
12 *
13 */
14
15 using System;
16 using System.Collections;
17 using System.Collections.Generic;
18 using UnityEngine;
19
20 namespace SUIFW
21 {
22 public class JsonAnlysisException : Exception {
23 public JsonAnlysisException() : base(){}
24 public JsonAnlysisException(string exceptionMessage) : base(exceptionMessage){}
25 }
26 }
第3步:定义“配置管理器”类
开发实现IConfigManager 接口的通用配置管理器
1 /***
2 *
3 * Title: "SUIFW" UI框架项目
4 * 主题:基于Json 配置文件的“配置管理器”
5 * Description:
6 * 功能:
7 *
8 * Date: 2017
9 * Version: 0.1版本
10 * Modify Recoder:
11 *
12 *
13 */
14
15 using System;
16 using System.Collections;
17 using System.Collections.Generic;
18 using UnityEngine;
19
20 namespace SUIFW
21 {
22 public class ConfigManagerByJson : IConfigManager
23 {
24 //保存(键值对)应用设置集合
25 private static Dictionary _AppSetting;
26
27 ///
28 /// 只读属性: 得到应用设置(键值对集合)
29 ///
30 public Dictionary AppSetting
31 {
32 get { return _AppSetting; }
33 }
34
35 ///
36 /// 构造函数
37 ///
38 /// Json配置文件路径
39 public ConfigManagerByJson(string jsonPath)
40 {
41 _AppSetting=new Dictionary();
42 //初始化解析Json 数据,加载到(_AppSetting)集合。
43 InitAndAnalysisJson(jsonPath);
44 }
45
46 ///
47 /// 得到AppSetting 的最大数值
48 ///
49 ///
50 public int GetAppSettingMaxNumber()
51 {
52 if (_AppSetting!=null && _AppSetting.Count>=1)
53 {
54 return _AppSetting.Count;
55 }
56 else
57 {
58 return 0;
59 }
60 }
61
62 ///
63 /// 初始化解析Json 数据,加载到集合众。
64 ///
65 ///
66 private void InitAndAnalysisJson(string jsonPath)
67 {
68 TextAsset configInfo = null;
69 KeyValuesInfo keyvalueInfoObj = null;
70
71 //参数检查
72 if (string.IsNullOrEmpty(jsonPath)) return;
73 //解析Json 配置文件
74 try{
75 configInfo = Resources.Load(jsonPath);
76 keyvalueInfoObj=JsonUtility.FromJson(configInfo.text);
77 }
78 catch{
79 throw new JsonAnlysisException(GetType() + "/InitAndAnalysisJson()/Json Analysis Exception ! Parameter jsonPath=" + jsonPath);
80 }
81 //数据加载到AppSetting 集合中
82 foreach (KeyValuesNode nodeInfo in keyvalueInfoObj.ConfigInfo)
83 {
84 _AppSetting.Add(nodeInfo.Key,nodeInfo.Value);
85 }
86 }
87
88
89
90 }
91 }
代码说明:
以上定义的“配置管理器”,可以对所有具备“键值对”特性的Json 配置文件,做统一数据提取工作,从而对于“UI预设”、“游戏对象”、“日志配置文件”、“语言国际化”等信息,可以做统一处理,极大提供开发效率。以下笔者提供本UI框架需要用到的除企业日志系统外,其他两种配置文件的截图,供参考。
(企业级Log日志中使用到的配置信息)
(“UI预设” 路径信息Json配置文件)