Skip to main content
ARShow
ARShow
 首页 » 资源教程 » Unity3D教程

Unity3D-游戏序列化

2016年09月02日 22:56:4512250

序列化 就是把一个内存对象变为与地址无关的可传输的数据格式,通常是文本格式;反序列化反之。一般情况下,对象序列化指把一个对象用 XML,YAML 或 json 文本表示。 尽管 Unity 场景文件是 yaml 格式的,但并没有提供运行时内置支持。Unity 已内置 json 支持。 
现在我要对我之前写的飞碟游戏进行初步的序列化。 
实现序列化后,游戏版本信息以及每个关卡的规则信息都选择json文本保存,这里,我把它们保存在游戏根目录下的Data文件夹中: 

Unity3D-游戏序列化 Unity3D教程 第1张

 

Unity3D-游戏序列化 Unity3D教程 第2张

Unity3D-游戏序列化 Unity3D教程 第3张

 
启动游戏的效果: 

Unity3D-游戏序列化 Unity3D教程 第4张

 
接下来就是代码的部分了。 
FileManager: 
在Scripits文件夹下新建FileManager.cs,它主要有两个工作:1、读取游戏版本信息并返回json;2、在游戏中读取关卡信息并返回json。 
代码如下:

using UnityEngine;  using System.Collections;  using Com.Mygame;  public class FileManager : MonoBehaviour {  
    public string url;

    SceneContriller scene = SceneContriller.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  
        }
    }
}  12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455

接下来就是让游戏使用这些json信息了, 
首先对关卡信息的处理,在我用来存放关卡Judge中加入用来与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 LevelInfo  
{  
    public string color;  
    public int each_num_min;    public int each_num_max;    public float speed;  
    public int round;  

    public static LevelInfo CreateFromJSON(string json)  
    {  
        return JsonUtility.FromJson<LevelInfo>(json);  
    }  
}  1234567891011121314151617181920212223242526

[SerializeField]表示类的可序列化 
http://www.ceeger.com/Script/Attributes/SerializeField.html 
在这两个类里都有一个CreateFromJSON的方法,输入为一条 json 字符串。该方法使用了JsonUtility.FromJson(jsonString)系统方法,返回的是解析 json 字符串后生成的该类对象。类的私有变量和 json 的变量是一一对应的,根据你预期的json信息来决定有什么变量。 
SceneContriller: 
然后为了让FileManager读取了版本信息后,能让场景控制器读取到该信息,需要增加一个方法。 
代码如下:

public void stageGameInfo(string json) {  
            GameInfo data = GameInfo.CreateFromJSON(json);
            _version = data.version;  
            _totalRound = data.totalRound;
        }12345

接着就是关卡信息,为了能在游戏过程中能读取关卡信息并得到json,有两步。 
首先,修改AddRound方法: 
代码如下:

public void AddRound() {
            turn = 0;            if (++Round > _totalRound) {  
                Round = 1;  
                score = 0;
            }            string file = "disk_level_" + Round.ToString() + ".json";  
            _fileManager.loadLevelJson(file);
        }123456789

然后就是添加stageLevel 方法接收FileManager读取关卡信息返回的json 
代码如下:

public void stageLevel(string json) {  
            LevelInfo data = LevelInfo.CreateFromJSON(json);  

            Color color;  
            if (!ColorUtility.TryParseHtmlString(data.color, out color)) {  
                color = Color.gray;  
            }  

            int each_num_min = data.each_num_min;
            int each_num_max = data.each_num_max;  
            float speed = data.speed;

            judge.each_num_min = each_num_min;
            judge.each_num_max = each_num_max;
            judge.speed_ = speed + 0.0f;
            judge.color = color;
        }  1234567891011121314151617

使用ColorUtility.TryParseHtmlString能够知道某颜色的颜色代码。将读取到json转换为相应的对象,然后传给负责规则管理的Judge类。 
其他: 
修改UserInterface,使得能够显示版本信息,这里就不细说了。 
以上就是飞碟游戏的初步序列化工作了。

但是,正常的发布的游戏还有版本更新,接下来就是版本更新的内容了。读取的方式同样可以使用WWW类。如果版本不一样,就提示用户更新,然后下载远程服务器端的关卡文本到本地。

在Scripits文件夹下新建VersionUpdate.cs负责版本检查和更新的工作。

public string url;public string cachedFile;
SceneContriller scene = SceneContriller.GetInstance();  123

这里准备三个变量,url存放服务器的地址,cachedFile存放版本信息存储位置,scene获得场景控制器。

然后就是检查版本信息,判断是否需要升级了

代码如下:

public void CheckVersion(string name) {
        url = "file:///C:/Users/Administrator/Desktop/disk_server/Data/";
        cachedFile = Application.dataPath + "/Data/";
        StartCoroutine (CheckVersion_ (name)); 
    }

    IEnumerator CheckVersion_(string name)  
    {  
        if (url.Length > 0)  
        {  
            WWW www = new WWW(url + name);  
            yield return www;  
            if (!string.IsNullOrEmpty(www.error))  
                Debug.Log(www.error);  
            else  if (scene.CheckVersion(www.text.ToString()))
                DownLoadNewVersion();
        }  
    }123456789101112131415161718

发现本地版本与服务器的版本不一样后,就调用DownLoadNewVersion方法,下载新版本到本地。

代码如下:

public void DownLoadNewVersion() {
        DownLoadVersion ("game_info.json");
        DownLoadVersion ("disk_level_1.json");
        DownLoadVersion ("disk_level_2.json");
        DownLoadVersion ("disk_level_3.json");
    }public void DownLoadVersion(string name)  
    {  
        url = "file:///C:/Users/Administrator/Desktop/disk_server/Data/";
        cachedFile = Application.dataPath + "/Data/";
        StartCoroutine(DownLoadVersion_(name));  
    }  

    IEnumerator DownLoadVersion_(string name)  
    {  
        if (url.Length > 0) {
            WWW download = new WWW (url + name);            yield return download;            if (!string.IsNullOrEmpty (download.error)) {
                Debug.Log (download.error);  
            } else {
                System.IO.FileStream cache = new System.IO.FileStream (cachedFile + name, System.IO.FileMode.Create);                cache.Write (download.bytes, 0, download.bytes.Length);                cache.Close ();
            }
        }
    }1234567891011121314151617181920212223242526272829

为了能够检查游戏版本是否最新,我是在场景控制器中添加了一个方法,来接收从服务器得到的json,并进行判断。

public bool CheckVersion(string json) {
            GameInfo data = GameInfo.CreateFromJSON(json);            if (_version != data.version) {
                _version = data.version + " (Update!)";  
                _totalRound = data.totalRound;                return true;
            } else {                return false;
            }
        }12345678910

然后尝试发布游戏,就发现并不能得到本地的版本信息和关卡信息!

简单粗暴的解决方法:把准备好的Date文件夹复制到游戏文件夹下。

一般的解决方法:persistentDataPath 用户数据存储位置,这才是一般游戏用户数据存储的地方,而dataPath app 的位置,不同平台不一样,在安卓平台也打包到 apk ,不能写。正确的做法应该是修改路径成persistentDataPath。

这样,游戏本地与服务器版本同步就实现了!另外,如果用户需要保存当前的游戏进度,那么可以使用 JsonUtility.ToJson(obj) 把用户当前的游戏状态写入文本。这些读写操作其实都是大同小异的。

评论列表暂无评论
发表评论