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 () { }}这个示例代码中就使用了LuaState。
2、LuaScriptMgr
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脚本
public LuaScriptMgr uluaMgr; private List<string> downloadFiles = new List<string>();
②初始化
void Init() { DontDestroyOnLoad(gameObject); //防止销毁自己 CheckExtractResource(); //释放资源 Screen.sleepTimeout = SleepTimeout.NeverSleep; Application.targetFrameRate = AppConst.GameFrameRate; }我们可以看看AppConst.cs文件,里面主要是一些配置。
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。达到热的目的。
好多啊。写得挺乱的。将就着看吧。
===================================================================================
结束。
版权声明:本文为博主原创文章,未经博主允许,随便转载。