Unity热更新(八)uLua示例Demo简析及SimpleFramework工作流程

发表于2017-10-26
评论0 5.1k浏览
我们在进行热更新的完整案例之前。有必要分析一下uLua的实例代码,了解一下SimpleFramework工作流程,已便于在我们制作实例时更容易理解。

1、LuaState

  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class HelloWorld : MonoBehaviour {  
  6.   
  7.     // Use this for initialization  
  8.     void Start () {  
  9.         LuaState l = new LuaState();  
  10.         string str = "print('hello world 世界')";  
  11.         l.DoString(str);  
  12.     }  
  13.       
  14.     // Update is called once per frame  
  15.     void Update () {  
  16.       
  17.     }  
  18. }  
这个示例代码中就使用了LuaState。

2、LuaScriptMgr

  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class CreateGameObject02 : MonoBehaviour {  
  6.   
  7.     private string script = @"  
  8.             luanet.load_assembly('UnityEngine')  
  9.             GameObject = UnityEngine.GameObject  
  10.             ParticleSystem = UnityEngine.ParticleSystem  
  11.             local newGameObj = GameObject('NewObj')  
  12.             newGameObj:AddComponent(ParticleSystem.GetClassType())  
  13.         ";  
  14.   
  15.     //非反射调用  
  16.     void Start () {  
  17.         LuaScriptMgr lua = new LuaScriptMgr();  
  18.         lua.Start();  
  19.         lua.DoString(script);  
  20.     }  
  21.       
  22.     // Update is called once per frame  
  23.     void Update () {  
  24.       
  25.     }  
  26. }  

这个示例代码中就使用了LuaScriptMgr。

上面的两个示例中,要注意一下的代码:
  1. private string script = @"  
  2.             luanet.load_assembly('UnityEngine')  
  3.             GameObject = UnityEngine.GameObject  
  4.             ParticleSystem = UnityEngine.ParticleSystem  
  5.             local newGameObj = GameObject('NewObj')  
  6.             newGameObj:AddComponent(ParticleSystem.GetClassType())  
  7.         ";  

大概工作流程就是先加载UnityEngine,然后创建一个GameObject变量,再调用AddComponent方法。其实就和Unity里面使用的差不多。很简单。不多说。

3、在Unity中访问Lua中的变量

  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class AccessingLuaVariables01 : MonoBehaviour {  
  6.   
  7.     private string script = @"  
  8.             luanet.load_assembly('UnityEngine')  
  9.             GameObject = luanet.import_type('UnityEngine.GameObject')  
  10.             ParticleSystem = luanet.import_type('UnityEngine.ParticleSystem')  
  11.             particles = {}  
  12.   
  13.             for i = 1, Objs2Spawn, 1 do  
  14.                 local newGameObj = GameObject('NewObj')  
  15.             local ps = newGameObj:AddComponent(luanet.ctype(ParticleSystem))  
  16.                 ps:Stop()  
  17.   
  18.                 table.insert(particles, ps)  
  19.             end  
  20.   
  21.             var2read = 42  
  22.         ";  
  23.   
  24.     void Start () {  
  25.         LuaState l = new LuaState();  
  26.         l["Objs2Spawn"] = 5;            //就是这里  
  27.         l.DoString(script);  
  28.   
  29.         print("Read from lua: "   l["var2read"].ToString());  
  30.   
  31.         LuaTable particles = (LuaTable)l["particles"];  
  32.   
  33.         foreach( ParticleSystem ps in particles.Values )  
  34.         {  
  35.             ps.Play();  
  36.         }  
  37.     }  
  38.     void Update () {  
  39.       
  40.     }  
  41. }  

4、执行Lua脚本文件,调用Lua方法,在Lua中使用协程

  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class ScriptsFromFile_01 : MonoBehaviour  
  6. {  
  7.   
  8.     public TextAsset scriptFile;  
  9.   
  10.     // Use this for initialization  
  11.     void Start()  
  12.     {  
  13.         LuaState l = new LuaState();  
  14.         l.DoString(scriptFile.text);  
  15.     }  
  16.   
  17.     // Update is called once per frame  
  18.     void Update()  
  19.     {  
  20.   
  21.     }  
  22. }  

而我们的File文件就是在同目录下的与类同名的txt文件。


注意,我们这里定义的File文件,是TextAsset类型。
  1. public TextAsset scriptFile;  

接下来就是调用Lua的方法。也很简单。
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class CallLuaFunction_01 : MonoBehaviour {  
  6.   
  7.     private string script = @"  
  8.             function luaFunc(message)  
  9.                 print(message)  
  10.                 return 42  
  11.             end  
  12.         ";  
  13.   
  14.     void Start () {  
  15.         LuaState l = new LuaState();  
  16.         l.DoString(script);  
  17.         LuaFunction f = l.GetFunction("luaFunc");  
  18.         object[] r = f.Call("I called a lua function!");  
  19.         print(r[0]);  
  20.     }  
  21.     void Update () {  
  22.       
  23.     }  
  24. }  

协程的使用:
  1. using UnityEngine;  
  2. using System.Collections;  
  3. using LuaInterface;  
  4.   
  5. public class LuaCoroutines : MonoBehaviour   
  6. {  
  7.     private string script = @"                                     
  8.             function fib(n)  
  9.                 local a, b = 0, 1  
  10.                 while n > 0 do  
  11.                     a, b = b, a   b  
  12.                     n = n - 1  
  13.                 end  
  14.   
  15.                 return a  
  16.             end  
  17.   
  18.             function CoFunc()  
  19.                 print('Coroutine started')  
  20.                 local i = 0  
  21.                 for i = 0, 10, 1 do  
  22.                     print(fib(i))                      
  23.                     coroutine.wait(1)  
  24.                 end  
  25.                 print('Coroutine ended')  
  26.             end  
  27.   
  28.             function myFunc()  
  29.                 coroutine.start(CoFunc)  
  30.             end  
  31.         ";  
  32.   
  33.     private LuaScriptMgr lua = null;  
  34.   
  35.     void Awake ()   
  36.     {  
  37.         lua  = new LuaScriptMgr();  
  38.         lua.Start();  
  39.         lua.DoString(script);          
  40.         LuaFunction f = lua.GetLuaFunction("myFunc");  
  41.         f.Call();  
  42.         f.Release();  
  43.     }  
  44.       
  45.     void Update ()   
  46.     {          
  47.         lua.Update();  
  48.     }  
  49.   
  50.     void LateUpdate()  
  51.     {  
  52.         lua.LateUpate();  
  53.     }  
  54.   
  55.     void FixedUpdate()  
  56.     {  
  57.         lua.FixedUpdate();  
  58.     }  
  59. }  

好了。uLua的示例代码我们就分析到这里了。接下来的可以自己琢磨,对于我们的案例来说已经够了。下面我们来分析一下自带热更新的案例的工作流程。也就是我们的SimpleFramework的工作流程。


5、框架启动GlobalGenerator,生成appview和gamemanager

在这里我们首先要看一下框架的目录。
整个SimpleFramework采用的是PureMVC的框架。看下图。


GlobalGenerator.cs
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. namespace SimpleFramework {  
  5.     /// <summary>  
  6.     /// 全局构造器,每个场景里都有,所以每个场景都会初始化一遍,也会初始化游戏管理器一次  
  7.     /// 如果游戏管理器已经存在了,就跳过了,否则创建游戏管理器,来保证游戏里只有一个GameManager  
  8.     /// </summary>  
  9.     public class GlobalGenerator : MonoBehaviour {  
  10.   
  11.         void Awake() {  
  12.             InitGameMangager();  
  13.         }  
  14.   
  15.         /// <summary>  
  16.         /// 实例化游戏管理器  
  17.         /// </summary>  
  18.         public void InitGameMangager() {  
  19.             string name = "GameManager";  
  20.             gameObject.AddComponent<AppView>();  
  21.   
  22.             GameObject manager = GameObject.Find(name);  
  23.             if (manager == null) {  
  24.                 manager = new GameObject(name);  
  25.                 manager.name = name;  
  26.   
  27.                 AppFacade.Instance.StartUp();   //启动游戏  
  28.             }  
  29.         }  
  30.     }  
  31. }  

这段代码就是我们热更新的入口。
这里GlobalGenerator的任务就完成了。
appview和gamemanager都是框架相关的东西。这里我们就只对GameManager进行分析。

6、GameManager中对资源的更新处理及处理Lua的View的加载和初始化

GameManager的功能就如这个标题所说。
代码中也有详细注释。
我们就不贴代码了。

我们关注一下GameManager组件下的脚本。


然后。我们要重点分析Lua文件夹下的Lua脚本。


GameManager.cs:

①加载所有Lua脚本

  1. public LuaScriptMgr uluaMgr;  
  2.         private List<string> downloadFiles = new List<string>();  

②初始化

  1. void Init() {  
  2.             DontDestroyOnLoad(gameObject);  //防止销毁自己  
  3.   
  4.             CheckExtractResource(); //释放资源  
  5.             Screen.sleepTimeout = SleepTimeout.NeverSleep;  
  6.             Application.targetFrameRate = AppConst.GameFrameRate;  
  7.         }  
我们可以看看AppConst.cs文件,里面主要是一些配置。
  1. using UnityEngine;  
  2. using System;  
  3. using System.Collections;  
  4. using System.Collections.Generic;  
  5.   
  6. namespace SimpleFramework {  
  7.     public class AppConst {  
  8.         public const bool DebugMode = false;                       //调试模式-用于内部测试  
  9.         /// <summary>  
  10.         /// 如果想删掉框架自带的例子,那这个例子模式必须要  
  11.         /// 关闭,否则会出现一些错误。  
  12.         /// </summary>  
  13.         public const bool ExampleMode = true;                       //例子模式   
  14.   
  15.         /// <summary>  
  16.         /// 如果开启更新模式,前提必须启动框架自带服务器端。  
  17.         /// 否则就需要自己将StreamingAssets里面的所有内容  
  18.         /// 复制到自己的Webserver上面,并修改下面的WebUrl。  
  19.         /// </summary>  
  20.         public const bool UpdateMode = false;                       //更新模式-默认关闭   
  21.   
  22.         public const int TimerInterval = 1;  
  23.         public const int GameFrameRate = 30;                       //游戏帧频  
  24.   
  25.         public const bool UsePbc = true;                           //PBC  
  26.         public const bool UseLpeg = true;                          //LPEG  
  27.         public const bool UsePbLua = true;                         //Protobuff-lua-gen  
  28.         public const bool UseCJson = true;                         //CJson  
  29.         public const bool UseSproto = true;                        //Sproto  
  30.         public const bool LuaEncode = false;                        //使用LUA编码  
  31.   
  32.         public const string AppName = "SimpleFramework";           //应用程序名称  
  33.         public const string AppPrefix = AppName   "_";             //应用程序前缀  
  34.         public const string ExtName = ".assetbundle";              //素材扩展名  
  35.         public const string AssetDirname = "StreamingAssets";      //素材目录   
  36.         public const string WebUrl = "http://localhost:6688/";      //测试更新地址  
  37.   
  38.         public static string UserId = string.Empty;                 //用户ID  
  39.         public static int SocketPort = 0;                           //Socket服务器端口  
  40.         public static string SocketAddress = string.Empty;          //Socket服务器地址  
  41.     }  
  42. }  

④资源初始化结束

  1. public void OnResourceInited() {  
  2.             LuaManager.Start();  
  3.             LuaManager.DoFile("Logic/Network");      //加载游戏  
  4.             LuaManager.DoFile("Logic/GameManager");   //加载网络  
  5.             initialize = true;    
  6.   
  7.             NetManager.OnInit();    //初始化网络  
  8.   
  9.             object[] panels = CallMethod("LuaScriptPanel");  
  10.             //---------------------Lua面板---------------------------  
  11.             foreach (object o in panels) {  
  12.                 string name = o.ToString().Trim();  
  13.                 if (string.IsNullOrEmpty(name)) continue;  
  14.                 name  = "Panel";    //添加  
  15.   
  16.                 LuaManager.DoFile("View/"   name);  
  17.                 Debug.LogWarning("LoadLua---->>>>"   name   ".lua");  
  18.             }  
  19.             //------------------------------------------------------------  
  20.             CallMethod("OnInitOK");   //初始化完成  
  21.         }  

我们看到熟悉的字眼了。
  1. LuaManager.DoFile("Logic/Network");      //加载游戏  
  2.             LuaManager.DoFile("Logic/GameManager");   //加载网络  

这正是我们Lua文件夹Logic文件夹下的脚本,我们来看看这两个脚本。

Network.lua文件主要是网络相关的文件,我们就不分析了,重点看GameManager.lua。它相当于Lua脚本文件的起点。
  1. require "3rd/pblua/login_pb"  
  2. require "3rd/pbc/protobuf"  
  3.   
  4. local lpeg = require "lpeg"  
  5.   
  6. local json = require "cjson"  
  7. local util = require "3rd/cjson.util"  
  8.   
  9. local sproto = require "3rd/sproto/sproto"  
  10. local core = require "sproto.core"  
  11. local print_r = require "3rd/sproto/print_r"  
  12.   
  13. require "Logic/luaclass"  
  14. require "Logic/CtrlManager"  
  15. require "Common/functions"  
  16. require "Controller/PromptCtrl"  

文件相关。不解释。
  1. GameManager = {};  
  2. local this = GameManager;  

单例。你懂的。
  1. function GameManager.LuaScriptPanel()  
  2.     return 'Prompt', 'Message';  
  3. end  

返回我们的UI相关类。

我们结合两个文件一起看。
  1. function GameManager.LuaScriptPanel()  
  2.     return 'Prompt''Message';  
  3. end   
  1. object[] panels = CallMethod("LuaScriptPanel");  
  2.             //---------------------Lua面板---------------------------  
  3.             foreach (object o in panels) {  
  4.                 string name = o.ToString().Trim();  
  5.                 if (string.IsNullOrEmpty(name)) continue;  
  6.                 name  = "Panel";    //添加  
  7.   
  8.                 LuaManager.DoFile("View/"   name);  
  9.                 Debug.LogWarning("LoadLua---->>>>"   name   ".lua");  
  10.             }  

这两段代码是相关联的。 
  1. local transform;  
  2. local gameObject;  
  3.   
  4. PromptPanel = {};  
  5. local this = PromptPanel;  
  6.   
  7. --启动事件--  
  8. function PromptPanel.Awake(obj)  
  9.     gameObject = obj;  
  10.     transform = obj.transform;  
  11.   
  12.     this.InitPanel();  
  13.     warn("Awake lua--->>"..gameObject.name);  
  14. end  
  15.   
  16. --初始化面板--  
  17. function PromptPanel.InitPanel()  
  18.     this.btnOpen = transform:FindChild("Open").gameObject;  
  19.     this.gridParent = transform:FindChild('ScrollView/Grid');  
  20. end  
  21.   
  22. --单击事件--  
  23. function PromptPanel.OnDestroy()  
  24.     warn("OnDestroy---->>>");  
  25. end  
  1. local transform;  
  2. local gameObject;  
  3.   
  4. MessagePanel = {};  
  5. local this = MessagePanel;  
  6.   
  7. --启动事件--  
  8. function MessagePanel.Awake(obj)  
  9.     gameObject = obj;  
  10.     transform = obj.transform;  
  11.   
  12.     this.InitPanel();  
  13.     warn("Awake lua--->>"..gameObject.name);  
  14. end  
  15.   
  16. --初始化面板--  
  17. function MessagePanel.InitPanel()  
  18.     this.btnClose = transform:FindChild("Button").gameObject;  
  19. end  
  20.   
  21. --单击事件--  
  22. function MessagePanel.OnDestroy()  
  23.     warn("OnDestroy---->>>");  
  24. end  

这两段代码得以执行。初始化UI界面。

这里有个疑问?初始化界面是怎么得以实现的?
其实也很简单,结合工程来看:
这是初始化后得到的界面。


其实我们早就把界面打包成Prefab了。


对,就是这么来的,每个prefab对应生成AssetBundle。达到热的目的。

好多啊。写得挺乱的。将就着看吧。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引