• 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

      

    实际效果:

      有更新:

        

      无更新:

         

  • 相关阅读:
    jqueryeasyui 使用笔记
    怎么查看端口占用情况?
    Ie6 Ie7 双倍padding
    Javascript获取URL
    fckeditor,用p替代div标签设置对齐方式
    ZenCart安全建站的几个措施和步骤
    复选框二选一 javascript
    dedecms从php设置里面去掉index.html
    ajax调用webservice返回DataTable "序列化类型为“System.Reflection.Module”的对象时检测到循环引用
    无题
  • 原文地址:https://www.cnblogs.com/wangsta/p/5517527.html
Copyright © 2020-2023  润新知