Unity3D 序列化与动态加载

  注:本实例开发环境为Unity3D 5.3.4,开发语言为C#

  这次的任务是修改上次的ShootThatUFO打飞碟小游戏,使其具有数据驱动(data driven)游戏的特性。  

  • 游戏启动后,如果它发现 远程控制目录 有文件且版本与当前游戏不同,提示并升级。程序首页应显示升级后版本,并展示预定义的行为.
  • 如果没有升级,用户可以选择从上次 Round 和 Turn 开始,或者 从头开始。

  首先区分数据:有些数据是静态数据,有些数据是运行时数据。对他们处理的方法不一样。

  游戏计划 - 为静态的游戏参数,或者称为规则

  运行时数据 - 随游戏进行而不断更新的数据

  定义两者的数据结构,标记为可序列化:

    [System.Serializable]
    public class GamePlan {
            public int gameVersion = 1;
            public float[] intervalPerRound = {2f, 2f, 1f, 1f, 0.5f};
            public float[] tossRange = {10f, 10f, 15f, 20f, 20f};
            public float[] tossSpeed = {20f, 20f, 25f, 25f, 30f};
            public float[] ufoScale = {0.5f, 0.8f, 1f};
            public Color[] ufoColor = {Color.green, Color.yellow, Color.red};
            public float[] tossMaxAngle = {0.6f,0.6f,0.6f,0.6f,0.7f};
    }

    [System.Serializable]
    public class RunTimeGameData {
            public int gameStatus = 0;
            public float gameStartCountDown = 3f;
            public bool okToShoot = true;

            public int round = 0;
            public int point = 0;
            public int trials = 0;
            public int ufoToToss = 20;
            public int highscore = 0;
    }

  游戏初始化的思路是:获取本地游戏计划->获取远程游戏计划(等待)->比较游戏版本,是否有新版本?

            (Y)-> 使用全新的运行时数据,加载新游戏计划,复写本地版本计划,提示用户有新版本

            (N)-> 加载本地运行时数据,使用本地游戏计划,询问用户是否需要继续上一次游戏

  BaseCode是游戏初始化,版本控制,游戏数据的控制器,在BaseCode中,更新Start()方法:

  注:这里考虑到有可能会降级?并没有严格控制新版本号要大于老版本号

    IEnumerator Start () {

        string gamePlan = File.ReadAllText (Application.dataPath + "/Resources/GamePlan.json");

        gp_local = JsonUtility.FromJson<GamePlan> (gamePlan);

        string url = "file:///Users/WangYinghao/Documents/Unity%20Playground/ShootThatUFOData/GamePlan.json";

        WWW gamePlanFromJSon = new WWW(url);

        yield return gamePlanFromJSon;

        gp_remote = JsonUtility.FromJson<GamePlan>(gamePlanFromJSon.text);

        if (gp_remote.gameVersion != gp_local.gameVersion) {

                gameUpdated = true;

                string newGPLocal = JsonUtility.ToJson(gp_remote, true);

                File.WriteAllText(Application.dataPath + "/Resources/GamePlan.json", newGPLocal);

                rt = new RunTimeGameData();

                rt.gameStatus = -1;

        } else {
                string runTimeData = File.ReadAllText(Application.dataPath + "/Resources/RuntimeGameData.json");
                rt = JsonUtility.FromJson<RunTimeGameData>(runTimeData);
                Debug.Log(runTimeData);

                if (rt.gameStatus == 1 || rt.gameStatus == 2) {
                        Debug.Log("gameStatus == 1 or 2 (ingame or midgame) Continue? or start Again?");
                        rt.gameStatus = 5;
                } else {
                        rt = new RunTimeGameData();
                        Debug.Log("Start Again");
                }
        }

        sceneController = SceneController.GetInstance ();

        sceneController.setBaseCode (this);

                if (!gameUpdated) {
                        sceneController.init(gp_local, rt);
                } else {
                        sceneController.init(gp_remote, rt);
                }

        gameModel = UnityEngine.Camera.main.gameObject.AddComponent <GameModel> ();

        judge = UnityEngine.Camera.main.gameObject.AddComponent <Judge> ();

        ui = UnityEngine.Camera.main.gameObject.AddComponent <UserInterface> ();
    }
    

  

  游戏状态新增两个状态:-1 代表有新版本,5 代表等待用户决定是否继续上一次游戏

  据此,更新UserInterface类:

  (用的仍然是旧GUI。。。)

void OnGUI () {
        // Make a background box

                if (userAction.getGameStatus() == 5) {
                        GUI.Label(new Rect(20, 20, 300, 20), "Shoot That UFO! Last Time Game Unfinished! Continue?");
                        if (GUI.Button(new Rect(20, 40, 80, 20), "Continue")) {
                                userAction.gameStart();
                        }
                        if (GUI.Button(new Rect(120, 40, 80, 20), "Start Again")) {
                                userAction.startOver();
                        }
                } 

                else if (userAction.getGameStatus() == -1) {
                        GUI.Label(new Rect(20, 20, 300, 20), "Shoot That UFO! New Updates! New Version: " + userAction.getGameVersion());
                        if (GUI.Button(new Rect(20, 40, 80, 20), "Cool!")) {
                                userAction.startOver();
                        }
                }

                else {

                        if (userAction.getGameStatus() == 1) {
                                GUI.Label(new Rect(20, 20, 80, 20), "Round " + (userAction.getRound() + 1));
                                GUI.Label(new Rect(20, 40, 80, 20), userAction.getUFONum() + " to Go");
                        }

                        if (userAction.getGameStatus() == 0 || userAction.getGameStatus() == 2) {
                                GUI.Label(new Rect(20, 20, 300, 20), "Shoot That UFO! Hit Space Bar to Begin!");
                                GUI.Label(new Rect(20, 40, 100, 20), "Time to start: " + gameStartTimer.ToString("0.0"));
                                GUI.Label(new Rect(20, 100, 100, 20), "Next Round: " + (userAction.getRound() + 1));
                        }

                        if (userAction.getGameStatus() == 3) {
                                GUI.Label(new Rect(20, 20, 200, 20), "Failed! Hit Spacebar to Restart.");
                                GUI.Label(new Rect(20, 40, 100, 20), "Time to start: " + gameStartTimer.ToString("0.0"));
                        }

                        if (userAction.getGameStatus() != 4) {
                                GUI.Label(new Rect(20, 80, 150, 20), "You have " + (10f - userAction.getTrial()) + " bullets left.");
                                GUI.Label(new Rect(700, 20, 100, 20), "Highscore: " + userAction.getHighscore());
                        }

                        if (userAction.getGameStatus() == 4) {
                                GUI.Label(new Rect(20, 20, 150, 20), "Game Cleared!");
                        }

                        GUI.Label (new Rect (20, 60, 150, 20), "Your score is " + userAction.getPoint());
                        GUI.Label (new Rect(700, 40, 300, 20), "Ver: " + userAction.getGameVersion());
                }

    }

  BaseCode中,为了保存运行时数据,更新Update()代码,设定为每1秒自动保存一次 

  不希望IO影响游戏,利用协程

    void Update (){
        gameSaveInterval++;
        if (gameSaveInterval > Application.targetFrameRate){
                StartCoroutine(saveRunTimeData());
                gameSaveInterval = 0;
        }
    }

    IEnumerator saveRunTimeData() {
        Debug.Log("Here!");
        RunTimeGameData rt = sceneController.getRunTimeGameData();
        File.WriteAllText(Application.dataPath + "/Resources/RunTimeGameData.json", JsonUtility.ToJson(rt, true));
        yield return null;
    }

  在远程目录放新的游戏计划,需要改变游戏计划时,将其他version的游戏计划改名为GamePlan.json

  

实际效果:

  有更新:

    

  无更新:

     

时间: 2024-10-20 05:30:53

Unity3D 序列化与动态加载的相关文章

unity3d Resources.Load动态加载资源

初步整理并且学习unity3d资源加载方法,预计用时两天完成入门学习 Unity3d常用两种加载资源方案:Resources.Load和AssetBundle Resources.Load就是从一个缺省打进程序包里的AssetBundle里加载资源 而一般AssetBundle文件需要你自己创建,运行时动态加载,可以指定路径和来源的.其实场景里所有静态的对象也有这么一个加载过程,只是Unity后台替你自动完成 一:Resources.Load:使用这种方式加载资源,首先需要下Asset目录下创建

关于Unity3D中Resources动态加载NGUI图片的方法

在NGUI中有些图片我需要动态进行变更或者加载,怎么办? 首先在项目中创建一个Resources目录,接着把需要的图片放在这里面,可以有子文件夹么?当然可以,文件结构很重要哦~ NGUI加载图片的方法其实是加载NGUI生成的atlas,大家可以看看NGUI的图集文件(一个material.一个prefab,一张图集),我们要做的就是动态加载这个prefab(它有UIAtlas属性),然后通过图片名称更改图片. 我这里那UISprite来说明,我是这样做的: UIAtlas tu = Resour

Unity3D音频播放 动态加载组件

http://www.biquge.cc/html/156/156282/22734698.html http://www.biquge.cc/html/156/156282/22734700.html http://www.biquge.cc/html/156/156282/22734702.html http://www.biquge.cc/html/156/156282/22734704.html http://www.biquge.cc/html/156/156282/22734706.

Unity3d热更新全书-加载(二)如何在不用AssetBundle的前提下动态加载预设

Unity3D的主要构成大家都知道,首先是场景图,场景图上的节点构成一颗树. 每个节点对应一个GameObject对象 然后每个GameObject有若干个组件 有一些组件会与资源产生关系,比如MeshRenderer会关联材质,材质会关联shader和贴图 场景图的一部分可以被保存为一个预设,prefab. 有时候我们会需要用预设去复用,而预设的加载似乎只能通过AB去打包,其实不然,这里我们有一个开源的库就可以解决这个问题. 为什么不使用AB,可以见上一篇,加载(一),不使用AB一份资源全平台

Unity3D动态加载外部资源

最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质上我理解没有什么区别.Resources.Load就是从一个缺省打进程序包里的AssetBundle里加载资源,而一般AssetBundle文件需要你自己创建,运行时动态加载,可以指定路径和来源的. 其实场景里所有静态的对象也有这么一个加载过程,只是Unity后台替你自动完成了. 详细说一下细节概念:AssetBundle运行时加载:来自

unity3d动态加载dll的API以及限制

Unity3D的坑系列:动态加载dll 一.使用限制 现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的,有了这个就可以实现代码更新,不过实际上,在unity里要用上动态加载dll是有很多限制的(不了解的话就是坑). 限制1:在Android手机里动态加载dll不能使用Assembly.LoadFile(string path),只能使用Assembly.Load(byte

【转】Unity3D AssetBundles 动态加载游戏资源

AssetBundles are files which you can export from Unity to contain assets of your choice. These files use a proprietary compressed format and can be loaded on demand in your application. This allows you to stream content like models, textures, audio c

Unity3D的坑系列:动态加载dll

Unity3D的坑系列:动态加载dll 我现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的,有了这个就可以实现代码更新,不过实际上,在unity里要用上动态加载dll是有很多限制的(不了解的话就是坑). 限制1:在Android手机里动态加载dll不能使用Assembly.LoadFile(string path),只能使用Assembly.Load(byte[] raw

unity3D中动态加载物体的常用的方法

1.用Resources.Load():参数为路径,需要在Assets文件夹中创建Resources文件夹,通过路径去查找,实例化并加入到内存中去,通过Instantiate动态加载的方法来实现物体场景的加载: 2.使用AssetBundle打包预设或者场景可以将与其相关的所有资源打包,这样很好地解决资源的依赖问题 要先打包资源: using UnityEngine;using System.Collections;using UnityEditor;using System.IO;public