Unity热更新(八)uLua示例Demo简析及SimpleFramework工作流程
我们在进行热更新的完整案例之前。有必要分析一下uLua的实例代码,了解一下SimpleFramework工作流程,已便于在我们制作实例时更容易理解。
1、LuaState
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class HelloWorld : MonoBehaviour {
- // Use this for initialization
- void Start () {
- LuaState l = new LuaState();
- string str = "print('hello world 世界')";
- l.DoString(str);
- }
- // Update is called once per frame
- void Update () {
- }
- }
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class CreateGameObject02 : MonoBehaviour {
- private string script = @"
- luanet.load_assembly('UnityEngine')
- GameObject = UnityEngine.GameObject
- ParticleSystem = UnityEngine.ParticleSystem
- local newGameObj = GameObject('NewObj')
- newGameObj:AddComponent(ParticleSystem.GetClassType())
- ";
- //非反射调用
- void Start () {
- LuaScriptMgr lua = new LuaScriptMgr();
- lua.Start();
- lua.DoString(script);
- }
- // Update is called once per frame
- void Update () {
- }
- }
这个示例代码中就使用了LuaScriptMgr。
上面的两个示例中,要注意一下的代码:
- private string script = @"
- luanet.load_assembly('UnityEngine')
- GameObject = UnityEngine.GameObject
- ParticleSystem = UnityEngine.ParticleSystem
- local newGameObj = GameObject('NewObj')
- newGameObj:AddComponent(ParticleSystem.GetClassType())
- ";
大概工作流程就是先加载UnityEngine,然后创建一个GameObject变量,再调用AddComponent方法。其实就和Unity里面使用的差不多。很简单。不多说。
3、在Unity中访问Lua中的变量
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class AccessingLuaVariables01 : MonoBehaviour {
- private string script = @"
- luanet.load_assembly('UnityEngine')
- GameObject = luanet.import_type('UnityEngine.GameObject')
- ParticleSystem = luanet.import_type('UnityEngine.ParticleSystem')
- particles = {}
- for i = 1, Objs2Spawn, 1 do
- local newGameObj = GameObject('NewObj')
- local ps = newGameObj:AddComponent(luanet.ctype(ParticleSystem))
- ps:Stop()
- table.insert(particles, ps)
- end
- var2read = 42
- ";
- void Start () {
- LuaState l = new LuaState();
- l["Objs2Spawn"] = 5; //就是这里
- l.DoString(script);
- print("Read from lua: " l["var2read"].ToString());
- LuaTable particles = (LuaTable)l["particles"];
- foreach( ParticleSystem ps in particles.Values )
- {
- ps.Play();
- }
- }
- void Update () {
- }
- }
4、执行Lua脚本文件,调用Lua方法,在Lua中使用协程
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class ScriptsFromFile_01 : MonoBehaviour
- {
- public TextAsset scriptFile;
- // Use this for initialization
- void Start()
- {
- LuaState l = new LuaState();
- l.DoString(scriptFile.text);
- }
- // Update is called once per frame
- void Update()
- {
- }
- }
而我们的File文件就是在同目录下的与类同名的txt文件。
注意,我们这里定义的File文件,是TextAsset类型。
- public TextAsset scriptFile;
接下来就是调用Lua的方法。也很简单。
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class CallLuaFunction_01 : MonoBehaviour {
- private string script = @"
- function luaFunc(message)
- print(message)
- return 42
- end
- ";
- void Start () {
- LuaState l = new LuaState();
- l.DoString(script);
- LuaFunction f = l.GetFunction("luaFunc");
- object[] r = f.Call("I called a lua function!");
- print(r[0]);
- }
- void Update () {
- }
- }
协程的使用:
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- public class LuaCoroutines : MonoBehaviour
- {
- private string script = @"
- function fib(n)
- local a, b = 0, 1
- while n > 0 do
- a, b = b, a b
- n = n - 1
- end
- return a
- end
- function CoFunc()
- print('Coroutine started')
- local i = 0
- for i = 0, 10, 1 do
- print(fib(i))
- coroutine.wait(1)
- end
- print('Coroutine ended')
- end
- function myFunc()
- coroutine.start(CoFunc)
- end
- ";
- private LuaScriptMgr lua = null;
- void Awake ()
- {
- lua = new LuaScriptMgr();
- lua.Start();
- lua.DoString(script);
- LuaFunction f = lua.GetLuaFunction("myFunc");
- f.Call();
- f.Release();
- }
- void Update ()
- {
- lua.Update();
- }
- void LateUpdate()
- {
- lua.LateUpate();
- }
- void FixedUpdate()
- {
- lua.FixedUpdate();
- }
- }
好了。uLua的示例代码我们就分析到这里了。接下来的可以自己琢磨,对于我们的案例来说已经够了。下面我们来分析一下自带热更新的案例的工作流程。也就是我们的SimpleFramework的工作流程。
5、框架启动GlobalGenerator,生成appview和gamemanager
在这里我们首先要看一下框架的目录。
整个SimpleFramework采用的是PureMVC的框架。看下图。
GlobalGenerator.cs
- using UnityEngine;
- using System.Collections;
- namespace SimpleFramework {
- /// <summary>
- /// 全局构造器,每个场景里都有,所以每个场景都会初始化一遍,也会初始化游戏管理器一次
- /// 如果游戏管理器已经存在了,就跳过了,否则创建游戏管理器,来保证游戏里只有一个GameManager
- /// </summary>
- public class GlobalGenerator : MonoBehaviour {
- void Awake() {
- InitGameMangager();
- }
- /// <summary>
- /// 实例化游戏管理器
- /// </summary>
- public void InitGameMangager() {
- string name = "GameManager";
- gameObject.AddComponent<AppView>();
- GameObject manager = GameObject.Find(name);
- if (manager == null) {
- manager = new GameObject(name);
- manager.name = name;
- AppFacade.Instance.StartUp(); //启动游戏
- }
- }
- }
- }
这段代码就是我们热更新的入口。
这里GlobalGenerator的任务就完成了。
appview和gamemanager都是框架相关的东西。这里我们就只对GameManager进行分析。
6、GameManager中对资源的更新处理及处理Lua的View的加载和初始化
GameManager的功能就如这个标题所说。
代码中也有详细注释。
我们就不贴代码了。
我们关注一下GameManager组件下的脚本。
然后。我们要重点分析Lua文件夹下的Lua脚本。
GameManager.cs:
①加载所有Lua脚本
- void Init() {
- DontDestroyOnLoad(gameObject); //防止销毁自己
- CheckExtractResource(); //释放资源
- Screen.sleepTimeout = SleepTimeout.NeverSleep;
- Application.targetFrameRate = AppConst.GameFrameRate;
- }
- using UnityEngine;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- namespace SimpleFramework {
- public class AppConst {
- public const bool DebugMode = false; //调试模式-用于内部测试
- /// <summary>
- /// 如果想删掉框架自带的例子,那这个例子模式必须要
- /// 关闭,否则会出现一些错误。
- /// </summary>
- public const bool ExampleMode = true; //例子模式
- /// <summary>
- /// 如果开启更新模式,前提必须启动框架自带服务器端。
- /// 否则就需要自己将StreamingAssets里面的所有内容
- /// 复制到自己的Webserver上面,并修改下面的WebUrl。
- /// </summary>
- public const bool UpdateMode = false; //更新模式-默认关闭
- public const int TimerInterval = 1;
- public const int GameFrameRate = 30; //游戏帧频
- public const bool UsePbc = true; //PBC
- public const bool UseLpeg = true; //LPEG
- public const bool UsePbLua = true; //Protobuff-lua-gen
- public const bool UseCJson = true; //CJson
- public const bool UseSproto = true; //Sproto
- public const bool LuaEncode = false; //使用LUA编码
- public const string AppName = "SimpleFramework"; //应用程序名称
- public const string AppPrefix = AppName "_"; //应用程序前缀
- public const string ExtName = ".assetbundle"; //素材扩展名
- public const string AssetDirname = "StreamingAssets"; //素材目录
- public const string WebUrl = "http://localhost:6688/"; //测试更新地址
- public static string UserId = string.Empty; //用户ID
- public static int SocketPort = 0; //Socket服务器端口
- public static string SocketAddress = string.Empty; //Socket服务器地址
- }
- }
④资源初始化结束
- public void OnResourceInited() {
- LuaManager.Start();
- LuaManager.DoFile("Logic/Network"); //加载游戏
- LuaManager.DoFile("Logic/GameManager"); //加载网络
- initialize = true;
- NetManager.OnInit(); //初始化网络
- object[] panels = CallMethod("LuaScriptPanel");
- //---------------------Lua面板---------------------------
- foreach (object o in panels) {
- string name = o.ToString().Trim();
- if (string.IsNullOrEmpty(name)) continue;
- name = "Panel"; //添加
- LuaManager.DoFile("View/" name);
- Debug.LogWarning("LoadLua---->>>>" name ".lua");
- }
- //------------------------------------------------------------
- CallMethod("OnInitOK"); //初始化完成
- }
我们看到熟悉的字眼了。
- LuaManager.DoFile("Logic/Network"); //加载游戏
- LuaManager.DoFile("Logic/GameManager"); //加载网络
这正是我们Lua文件夹Logic文件夹下的脚本,我们来看看这两个脚本。
Network.lua文件主要是网络相关的文件,我们就不分析了,重点看GameManager.lua。它相当于Lua脚本文件的起点。
- require "3rd/pblua/login_pb"
- require "3rd/pbc/protobuf"
- local lpeg = require "lpeg"
- local json = require "cjson"
- local util = require "3rd/cjson.util"
- local sproto = require "3rd/sproto/sproto"
- local core = require "sproto.core"
- local print_r = require "3rd/sproto/print_r"
- require "Logic/luaclass"
- require "Logic/CtrlManager"
- require "Common/functions"
- require "Controller/PromptCtrl"
文件相关。不解释。
- GameManager = {};
- local this = GameManager;
单例。你懂的。
- function GameManager.LuaScriptPanel()
- return 'Prompt', 'Message';
- end
返回我们的UI相关类。
我们结合两个文件一起看。
- function GameManager.LuaScriptPanel()
- return 'Prompt', 'Message';
- end
- object[] panels = CallMethod("LuaScriptPanel");
- //---------------------Lua面板---------------------------
- foreach (object o in panels) {
- string name = o.ToString().Trim();
- if (string.IsNullOrEmpty(name)) continue;
- name = "Panel"; //添加
- LuaManager.DoFile("View/" name);
- Debug.LogWarning("LoadLua---->>>>" name ".lua");
- }
这两段代码是相关联的。
- local transform;
- local gameObject;
- PromptPanel = {};
- local this = PromptPanel;
- --启动事件--
- function PromptPanel.Awake(obj)
- gameObject = obj;
- transform = obj.transform;
- this.InitPanel();
- warn("Awake lua--->>"..gameObject.name);
- end
- --初始化面板--
- function PromptPanel.InitPanel()
- this.btnOpen = transform:FindChild("Open").gameObject;
- this.gridParent = transform:FindChild('ScrollView/Grid');
- end
- --单击事件--
- function PromptPanel.OnDestroy()
- warn("OnDestroy---->>>");
- end
- local transform;
- local gameObject;
- MessagePanel = {};
- local this = MessagePanel;
- --启动事件--
- function MessagePanel.Awake(obj)
- gameObject = obj;
- transform = obj.transform;
- this.InitPanel();
- warn("Awake lua--->>"..gameObject.name);
- end
- --初始化面板--
- function MessagePanel.InitPanel()
- this.btnClose = transform:FindChild("Button").gameObject;
- end
- --单击事件--
- function MessagePanel.OnDestroy()
- warn("OnDestroy---->>>");
- end
这两段代码得以执行。初始化UI界面。
这里有个疑问?初始化界面是怎么得以实现的?
其实也很简单,结合工程来看:
这是初始化后得到的界面。
其实我们早就把界面打包成Prefab了。
对,就是这么来的,每个prefab对应生成AssetBundle。达到热的目的。
好多啊。写得挺乱的。将就着看吧。