Unity3D 序列化与动态加载

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

  技术分享

 

实际效果:

  有更新:

    技术分享

  无更新:

     技术分享

 

以上是关于Unity3D 序列化与动态加载的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D的坑系列:动态加载dll

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

Unity3D序列帧动画制作方法---实现加载进度条

unity3d 资源加载与释放的内存管理

unity3d脚本不能加载

在android中动态创建选项卡并使用传入的参数加载片段