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。达到热的目的。
好多啊。写得挺乱的。将就着看吧。
===================================================================================
结束。