在游戏设计中,序列化是一件很核心的东西。
序列化就是把一个内存对象变为与地址无关的可传输的数据格式,通常是文本格式。如果游戏没有实现序列化,那么当游戏需要进行版本更新时,将会浪费玩家大量的时间。尤其对于大型游戏来说,这种浪费是不可想象的。实现游戏序列化设计,通过数据驱动设计,使得游戏代码更加稳固 (robustness)。我们可以通过改变数据,实现游戏规则、 场景布局、游戏难度的动态改变,而不要程序员的参与。 让游戏发布后,运维与设计师进行”后设计” (Post-Design) 成为可能。
因此这次我们就来实现一个简单的序列化设计。
步骤:
首先,我们选择的是json文本来实现,因为Unity已内置json支持。
我们将需要的json文本,放入到Assets下的data(自建)文件夹中。
因为我们只是一个简单的序列化实现,所以我们要实现的序列化任务是部分,主要实现的是不同关卡中飞碟的特性。
替代的是以下部分:
switch (round)
{
case 0:
{
newDisk.GetComponent<DiskData>().color = Color.blue;
newDisk.GetComponent<DiskData>().speed = 4.0f;
float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
newDisk.GetComponent<DiskData>().direction = new Vector3(RanX, 1, 0);
newDisk.GetComponent<Renderer>().material.color = Color.blue;
break;
}
case 1:
{
newDisk.GetComponent<DiskData>().color = Color.red;
newDisk.GetComponent<DiskData>().speed = 6.0f;
float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
newDisk.GetComponent<DiskData>().direction = new Vector3(RanX, 1, 0);
newDisk.GetComponent<Renderer>().material.color = Color.red;
break;
}
case 2:
{
newDisk.GetComponent<DiskData>().color = Color.black;
newDisk.GetComponent<DiskData>().speed = 8.0f;
float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
newDisk.GetComponent<DiskData>().direction = new Vector3(RanX, 1, 0);
newDisk.GetComponent<Renderer>().material.color = Color.black;
break;
}
}
现在,我们可以用序列化的方式,来取代它们。
添加两个带有[SerializeField] 符号的类,用来对json数据进行序列化。[Serializable]标签,说明这个类可以被序列化。一个是用来存放关卡信息,另一个用来存放版本信息。json文件的数据要与这个类的内容对应起来:
[SerializeField]
public class GameInfo
{
public string version;
public int totalRound;
public static GameInfo CreateFromJSON(string json)
{
return JsonUtility.FromJson<GameInfo>(json);
}
}
[SerializeField]
public class LevelData
{
public string color1;
public string color2;
public float speed;
public static LevelData CreateFromJSON(string json)
{
return JsonUtility.FromJson<LevelData>(json);
}
}
然后设计读取json文件的FileManager类:
using UnityEngine;
using System.Collections;
using Com.Mygame;
public class FileManager : MonoBehaviour {
public string url;
SceneController scene = SceneController.getInstance();
void Awake()
{
scene.setFileManager(this);
// 获取游戏版本信息
LoadGameInfoJson("game_info.json");
}
// 传入文件名,启动协程读取文件
public void loadLevelJson(string name)
{
url = "file://" + Application.dataPath + "/Data/" + name;
StartCoroutine(LoadLevel());
}
IEnumerator LoadLevel()
{
if (url.Length > 0)
{
WWW www = new WWW(url);
yield return www;
if (!string.IsNullOrEmpty(www.error))
Debug.Log(www.error);
else
scene.stageLevel(www.text.ToString()); // 返回json字符串给scene
}
}
// 传入游戏信息文件名,启动协程读取文件
public void LoadGameInfoJson(string name)
{
url = "file://" + Application.dataPath + "/Data/" + name;
StartCoroutine(LoadGameInfo());
}
IEnumerator LoadGameInfo()
{
if (url.Length > 0)
{
WWW www = new WWW(url);
yield return www;
if (!string.IsNullOrEmpty(www.error))
Debug.Log(www.error);
else
scene.stageGameInfo(www.text.ToString()); // 返回json字符串给scene
}
}
}
为了记录下版本的信息,以及关卡数,我们需要加多两个变量来存储这两个信息:
private string _version;
private int _totalRound;
另外还要修改接口函数信息:
public interface IQueryStatus {
bool isCounting();
bool isShooting();
int getRound();
int getPoint();
int getEmitTime();
int getTotalRound ();
string getVersion ();
}
public int getTotalRound () {
return _totalRound;
}
public string getVersion () {
return _version;
}
同时,游戏的关卡是游戏过程中读取的,所以修改sceneController的nextRound()方法:
public void nextRound() {
_point = 0;
if (++_round > _totalRound) {
_round = 1; // 循环
}
string file = "disk_level_" + _round.ToString() + ".json";
_fileManager.loadLevelJson(file);
}
然后,补全stageLevel和stageGameInfo两个序列化json实例后转化为相应的游戏参数的方法:
public void stageLevel(string json) {
LevelData data = LevelData.CreateFromJSON(json);
Color color1;
if (!ColorUtility.TryParseHtmlString(data.color1, out color1)) {
color1 = Color.gray;
}
Color color2;
if (!ColorUtility.TryParseHtmlString(data.color2, out color2)) {
color2 = Color.gray;
}
float speed = data.speed;
_gameModel.setting(color1, speed, color2);
}
public void stageGameInfo(string json) {
GameInfo info = GameInfo.CreateFromJSON(json);
_version = info.version;
_totalRound = info.totalRound;
}
此时运行游戏,就可以发现游戏通过读取文档来和之前一样运行了:
我们再通过添加text,和修改useInterface,就可以将版本信息也显示出来。