您的位置:首页 > 移动开发 > Unity3D

Unity3D 游戏序列化及发布

2016-05-22 21:36 387 查看
在之前的作业中做了一个打飞碟的小游戏。这次作业是修改这个游戏使之通过从json文件中读取一些设置数据。事实上,在之前那个作业中,我将飞碟的数据分离出来放在DiskData这个类中。在这个类里面用数组(当时初学C#还不清楚C#中的List用法)存放每一轮有多少飞碟以及每个飞碟的颜色、大小、初始位置、发射速度和发射时间。那个代码放在下面,请不要笑。

不清楚当时我是怎么想的,为什么不单独用个类将属性组织在一起呢?而且没有使用随机数导致这个游戏飞碟动作很单一,使得游戏性很差。

//飞碟的信息,由上至下为每局第1、2、3、4、5号飞碟的所有数据
public class DiskData {
public static Color[] color = {
new Color(0, 0, 0),
new Color(0, 0, 0),
new Color(0, 0, 0),
new Color(0, 0, 0),
new Color(0, 0, 0),

new Color(0, 0, 255),
new Color(0, 0, 0),
new Color(255, 0, 0),
new Color(0, 255, 0),
new Color(0, 0, 0),

new Color(111, 111, 33),
new Color(0, 255, 0),
new Color(0, 0, 255),
new Color(100, 155, 0),
new Color(200, 5, 1)
};
public static Vector3[] scale = {
new Vector3 (5, 1F, 5),
new Vector3 (5, 1F, 5),
new Vector3 (5, 1F, 5),
new Vector3 (5, 1F, 5),
new Vector3 (5, 1F, 5),

new Vector3 (2, 0.5F, 2),
new Vector3 (2, 0.5F, 2),
new Vector3 (2, 0.5F, 2),
new Vector3 (2, 0.5F, 2),
new Vector3 (2, 0.5F, 2),

new Vector3 (1, 0.1F, 1),
new Vector3 (1, 0.1F, 1),
new Vector3 (1, 0.1F, 1),
new Vector3 (1, 0.1F, 1),
new Vector3 (1, 0.1F, 1),
};
public static Vector3[] position = {
new Vector3 (-4, 0, 0),
new Vector3 (-4, 0, 0),
new Vector3 (-4, 0, 0),
new Vector3 (-4, 0, 0),
new Vector3 (-4, 0, 0),

new Vector3 (-3, 0, 1),
new Vector3 (1, 0, 5),
new Vector3 (3, 1, 2),
new Vector3 (4, 0, 3),
new Vector3 (-3, 1, 3),

new Vector3 (3, 1, 3),
new Vector3 (-2, 0, -4),
new Vector3 (6, 0, 1),
new Vector3 (-3, 2, -1),
new Vector3 (3, -1, -3)
};
public static Vector3[] speed = {
new Vector3 (4, 4, 4),
new Vector3 (4, 4, 4),
new Vector3 (4, 4, 4),
new Vector3 (4, 4, 4),
new Vector3 (4, 4, 4),

new Vector3 (10, 5, 5),
new Vector3 (10, 5, 5),
new Vector3 (10, 5, 5),
new Vector3 (10, 5, 5),
new Vector3 (10, 5, 5),

new Vector3 (-5, 7, -9),
new Vect
4000
or3 (7, 0, 9),
new Vector3 (-7, 0, -8),
new Vector3 (5, 6, -10),
new Vector3 (-7, -2, -4)
};
public static float[] genTime = {
1, 3, 5, 7, 9,

1F, 2.5F, 3.6F, 4.8F, 5.9F,

1F, 2F, 3F, 4F, 6F
};
}
总之这个代码仅供娱乐,但表明我们当时非常需要一个能存放数据的地方。现在通过序列化这个方法,将飞碟的数据放在json文件中,需要时通过从json文件中将数据读出即可。

首先我们需要一个game_info的一个json文件来保存游戏基本信息,如游戏版本、游戏轮数、每轮飞碟数。这个json内容如下:

{
"version":"1.0",
"totalRound":3,
"diskPerRound":5
}


之后还需要几个文件存放飞碟数据。毫无疑问,以这个样例来说,5个飞碟每轮,一共3轮,总共15个飞碟,若一个飞碟一个文件是不现实的。考虑到修改的方便,全放在一个文件也不合适。这里将每轮的飞碟放一个文件,3个文件即可。对于每个文件,保存有颜色、大小、位置、速度、发射时间等信息。这里将位置和速度设定为最小位置、最小速度和最大位置、最大速度,用这之间的随机数据使游戏的游戏性增强同时减少数据存储量。这里以disk_data_0.json为例,其他的只是数据有变化。

{
"color": {
"r": 0,
"g": 0,
"b": 0
},
"scale": {
"x": 5,
"y": 1,
"z": 5
},
"positionXmin": -4,
"positionYmin": 0,
"positionZmin": 0,
"positionXmax": -3,
"positionYmax": 1,
"positionZmax": 1,
"speedXmin": 4,
"speedYmin": 4,
"speedZmin": 4,
"speedXmax": 5,
"speedYmax": 5,
"speedZmax": 5,
"waitTime": 2
}


相比于我之前用一个类并在这个类中声明一堆静态数组来存储,无论是从美观还是方便各个方面都是完爆之前的做法,更坚定了要修改代码的决心


要从这些json文件中读数据,首先要有相应的可序列化类,这些类均要有处理json字符串的静态函数,于是很容易得到下述两个类:

[SerializeField]
public class DiskJSON {
public Color color;
public Vector3 scale;
public float positionXmin, positionYmin, positionZmin;
public float positionXmax, positionYmax, positionZmax;
public float speedXmin, speedYmin, speedZmin;
public float speedXmax, speedYmax, speedZmax;
public float waitTime;

public static DiskJSON CreateDiskJSONFromJson(string json) {
return JsonUtility.FromJson<DiskJSON> (json);
}
}
[SerializeField]
public class GameInfo {
public string version;
public int totalRound;
public int diskPerRound;
public static GameInfo CreateFromJson(string json) {
return JsonUtility.FromJson<GameInfo> (json);
}
}


作用非常明白,将json文件读进这两个类的对象中。而可序列化的要求使得开头必有[SerializeField]。

有了这两个类,接下来要让这两个类能够读取数据。

对于一个游戏,首先应在开始读入游戏的版本等基本信息。后面的飞碟的生成数量也在基本信息中,故先考虑GameInfo。要想在游戏开始时就开始读,这里通过继承MonoBehaviour的FileManager类的Start()的调用来实现。毕竟已经有一堆的Manager了,再加一个也无妨。代码如下:

private string _url;
void Start() {
SceneController.GetInstance ().fileManager = this;
SetGameInfo ("file:///E:/3dGame/ShootDisk/Assets/data/game_info.json");
}
public void SetGameInfo(string url) {
_url = url;
StartCoroutine (ReadGameInfo());
}
public IEnumerator ReadGameInfo() {
WWW www = new WWW (_url);
yield return www;

if(!string.IsNullOrEmpty(www.error))
Debug.Log(www.error);
else
SceneController.GetInstance().SetFromJson(www.text);
}


在Start()中传入文件地址,把这个对象添加给场景控制器的同时开始一个协程,通过WWW类完成文件的读取。在完成将文件内容以字符串形式读出后,把这个字符串交给场景控制器,由场景控制器完成字符串解析,并进行版本匹配、游戏轮数和每轮飞碟数的设置。

注意到一旦场景控制器这边设置好基本信息,接下来就应该开始读飞碟数据了。毕竟读飞碟数据需要知道游戏有几轮,知道游戏有几轮也就意味着有几个文件要读。知道基本信息,那就准备开始读飞碟数据吧。

按照上述思路,场景控制器的SetFromJson()方法代码如下:

public GameInfo gameInfo = new GameInfo ();
public void SetFromJson (string json) {
gameInfo = GameInfo.CreateFromJson (json);
fileManager.InitDiskData ();
}
这样读完基本信息,反过来场景控制器请求FileManager初始化飞碟数据,要读飞碟数据,方式和读基本信息差不多。唯一就是考虑一下轮数,根据轮数求到文件url后读即可,代码如下:

public void InitDiskData() {
for (int i = 0; i < SceneController.GetInstance ().gameInfo.totalRound; ++i) {
string url = "file:///E:/3dGame/ShootDisk/Assets/data/disk_data_" + i + ".json";
SetDiskData (url);
}
}
public void SetDiskData(string url) {
_url = url;
StartCoroutine (ReadDiskData ());
}
public IEnumerator ReadDiskData() {
WWW www = new WWW (_url);
yield return www;

if (!string.IsNullOrEmpty (www.error))
Debug.Log (www.error);
else
DiskData.AddDiskData (www.text);
}
这里我希望尽可能多的使用原代码,不想有太多改动。这里通过修改之前丑得不行的DiskData类,让它承担将json字符串转换为熟知的飞碟数据。

后悔当初没有用List,这里将DiskData类改成存放List的地方,List中每个元素就是一个飞碟全部数据,包括颜色、大小、位置、速度、生成时间等。由于json文件中存放的是每个飞碟生成的等待时间,稍加计算即可得到每轮生成这个飞碟的时间。结果代码如下,这里将之前贴过得DiskJSON再贴出来,便于查看:

public class DiskData {
public static List<Disk> DiskDataList = new List<Disk>();
public static void AddDiskData(string json) {
List<Disk> dl = Disk.CreateDiskFromJSON (json);
DiskDataList.AddRange (dl);
}
}
public class Disk {
public Color color;
public Vector3 scale;
public Vector3 position;
public Vector3 speed;
public float genTime;

public static List<Disk> CreateDiskFromJSON(string json) {
List<Disk> diskList = new List<Disk> ();
float init_time = 1;
for (int i = 0; i < SceneController.GetInstance().gameInfo.diskPerRound; ++i) {
DiskJSON dj = DiskJSON.CreateDiskJSONFromJson (json);
Disk d = new Disk ();
d.color = dj.color;
d.scale = dj.scale;
d.position = new Vector3 (Random.Range(dj.positionXmin, dj.positionXmax),
Random.Range(dj.positionYmin, dj.positionYmax),
Random.Range(dj.positionZmin, dj.positionZmax));
d.speed = new Vector3 (Random.Range(dj.speedXmin, dj.speedXmax),
Random.Range(dj.speedYmin, dj.speedYmax),
Random.Range(dj.speedZmin, dj.speedZmax));
d.genTime = init_time + dj.waitTime * i;

diskList.Add (d);
}
return diskList;
}
}
[SerializeField]
public class DiskJSON {
public Color color;
public Vector3 scale;
public float positionXmin, positionYmin, positionZmin;
public float positionXmax, positionYmax, positionZmax;
public float speedXmin, speedYmin, speedZmin;
public float speedXmax, speedYmax, speedZmax;
public float waitTime;

public static DiskJSON CreateDiskJSONFromJson(string json) {
return JsonUtility.FromJson<DiskJSON> (json);
}
}


一共读3个json文件,每个json文件会生成这一轮所有的飞碟数据,这些飞碟数据在CreateDiskFromJson方法中以一个List形式返回,总的DiskData中的静态成员DiskDataList将这些List全组合在一起。这样原本的代码文件基本不需要修改就可以得到使用,省得对大量代码修改之后变得面目全非,自己也乱了套。

这个代码比最之前的五个数组,简直一个天上一个地下。当我们修改游戏内容时只需修改json文本文件即可,而无需修改代码。美观上来说,这个代码看起来逻辑性更强,更加舒服。这里贴出之前丑陋的代码,一是表明当时我们其实很需要一种存储数据的方法,受限知识水平只能想到另外写一个类;二是对比一下,感觉用json文本真是好太多了,相较于之前蠢的不行的方法。

最后将游戏发布出来,测试一下,看上去没什么变化,所有功能依然齐全。但实际上,从代码可读性以及以后对游戏内容的修改,各个方面好得真不是一点。很开心能将之前丑陋的代码彻彻底底的删掉,再也不用那么愚蠢的做法了。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: