以下使用Unity20174.10 + Vs2017 + XLua
在这之前,请阅读以下两篇文章
Unity加载AB:https://blog.csdn.net/MikuLingSSS/article/details/82838300
XLua基本使用:https://blog.csdn.net/MikuLingSSS/article/details/82873510
服务器地址可以填写127.0.0.1:端口号 // 但是请保证你们已经搭建了本地的FTP,并且可以使用网页和我的电脑访问
==================================================================================================
首先,创建两个场景,一个是加载XLua脚本,另一个是主场景
加载AssetBundle还是使用上篇博客的代码,进行了小范围修改
public class LoadWWW : MonoBehaviour
{
void Start()
{
StartCoroutine(LoadManifest("ftp://127.0.0.1:10091/StreamingAssets/PC/LuaScripts/", "LuaScripts", "ftp://127.0.0.1:10091/StreamingAssets/PC/LuaScripts/Version.txt"));
StartCoroutine(Loading("LuaRunningTest"));
}
private AsyncOperation async; // 新加了一个异步加载场景
private int ToJourney, YetJourney;
IEnumerator Loading(string scendID)
{
async = SceneManager.LoadSceneAsync(scendID);
async.allowSceneActivation = false;
while (async.progress < 0.9f)
{
ToJourney = (int)(async.progress * 100);
while (YetJourney < ToJourney)
{
YetJourney++;
yield return new WaitForEndOfFrame();
}
yield return new WaitForEndOfFrame();
}
while (true)
{
if (IsLoadScene) // 这是我们设立的条件 下面加载Lua脚本的地方会有介绍
{
ToJourney = 100;
while (YetJourney < ToJourney)
{
YetJourney++;
yield return new WaitForEndOfFrame();
}
async.allowSceneActivation = true;
yield return async;
}
yield return 0.1f;
}
}
// LoadManifest 修改了一下,让AB包版本从网络上获取
IEnumerator LoadManifest(string path, string filename, string versionPath)
{
int version = 1;
using (WWW versionW = new WWW(versionPath))
{
yield return versionW;
Debug.Log(versionW.text+"::" + versionW.ToString());
version = Convert.ToInt16(versionW.text);
}
///.... 中间代码没变 省略
}
这里说明一下思路,我们Lua脚本名称会和C#脚本名字一样,因为这样我们可以直接在C#里面使用键值对来直接获取脚本,而不需要在一个个配置
IEnumerator LoadPrefab(string path, string filename, int version, bool IsLoad = true)
{
... 上面代码没变 省略
foreach (var item in asbundle.GetAllAssetNames())
{
// 这个脚本只是修改了一下遍历里面
string scripts = asbundle.LoadAsset(item).text; // 加载网络上AB包的Lua脚本
string methodName = item.Replace(".lua.txt", ""); // 我们获取到的包是全称,所以要去掉一些东西 后续方便使用
methodName = methodName.Substring(methodName.LastIndexOf("/") + 1); // 获得名称
LuaManager.Instance.SetScripts(methodName, scripts); // 下面有介绍
}
}
}
-------------------------------------------------------------------------------------
这个脚本会在我们加载成功AB包中的Lua脚本时保存相应的Lua脚本,
并且以Lua脚本的名字作为自己的键,脚本代码作为值 ----
using ...;
public class LuaManager { // 单例,因为我们需要保证场景中只有一个实例
private static LuaManager instance;
public static LuaManager Instance
{
get
{
if (instance == null)
instance = new LuaManager();
return instance;
}
}
public Dictionary luaScripts; // 保存脚本信息
private LuaManager() { // 初始化
luaScripts = new Dictionary();
}
public string GetScripts(string methodName) // 获取脚本
{
if (!luaScripts.ContainsKey(methodName))
return null;
return luaScripts[methodName];
}
public void SetScripts(string methodName, string scripts) // 设置脚本,就是加载AB包额时候用到的
{
if (luaScripts.ContainsKey(methodName))
{
luaScripts[methodName] = scripts;
}
else
{
luaScripts.Add(methodName,scripts);
}
}
public static string RetType(object o) // 通过反射获取自己的类名,我们的类想要取出Lua脚本就必须要知道自己的类名
{
StackTrace trace = new StackTrace();
StackFrame frame = trace.GetFrame(1);
MethodBase method = frame.GetMethod();
string className = method.ReflectedType.Name;
return className;
}
}
FTP文档结构:Version.txt里面只有一个数字 这里暂时是1
然后,开始写主场景的脚本,这里就暂时只写一个脚本,写完记得点击 Xlua -> Hotfix Inject In Editor
// 这个写崩了 今天是没心情改了 各位大佬就当他不存在
[Hotfix]
public class LoadLuaS : MonoBehaviour {
protected LuaEnv luaenv = new LuaEnv();
protected LuaTable scriptEnv;
protected Action luaStart;
protected Action luaUpdate;
protected Action luaDestroy;
private void Awake()
{
OnAwake();
}
protected virtual void OnAwake()
{
}
protected virtual void OnDestroy()
{
if (luaDestroy != null)
luaDestroy();
luaenv = null;
scriptEnv = null;
}
}
----------------------------------------------------------------------------------------
// 因为我们的Lua脚本名字是PersonMove.lua.txt 所以这个类名会是PersonMove.cs
using ...;
[Hotfix]
public class PersonMove : LoadLuaS
{
// 上篇博客有介绍,这里不再多说
protected override void OnAwake()
{
scriptEnv = luaenv.NewTable();
using (LuaTable meta = luaenv.NewTable())
{
meta.Set("__index", luaenv.Global);
scriptEnv.SetMetaTable(meta);
}
//注意这两句=我们会通过反射获取到我们的类名,然后进行判断我这个脚本有没有对应的Lua脚本
string methodName = LuaManager.RetType(this);
//ToLower是转化为小写,因为我们解包之后所有的字符都是小写,故而我们保存到字典的键也是小写
//所以这里将其转为小写
string scripts = LuaManager.Instance.GetScripts(methodName.ToLower());
注意这两句====================================
if (scripts != null)
{
scriptEnv.Set("self", this);
luaenv.DoString(scripts, methodName, scriptEnv);
scriptEnv.Get("OnStart",out luaStart);
scriptEnv.Get("OnUpdate",out luaUpdate);
scriptEnv.Get("OnDestroy",out luaDestroy);
}
}
void Start()
{
if (luaStart != null)
luaStart();
}
void Update()
{
if (luaUpdate != null)
luaUpdate();
}
protected new void OnDestroy()
{
base.OnDestroy();
}
}
Lua代码
-------------------------------------Manager.lua.txt---------------------------------
Manager = {}
Manager.__index = Manager;
Manager.MoveSpeed = 10;
Manager.RotationSpeed = 90;
-------------------------------------PersonMove.lua.txt------------------------------
-- 一个非常简单的Move
require("Manager");
PersonMove = {}
setmetatable(PersonMove, Manager)
PersonMove.self = nil; -- 自身类
PersonMove.hero = nil; -- 英雄GameObject
PersonMove.camera = nil;
PersonMove.__index = Manager; --
local forward = 0; -- 移动增量
local right = 0; --
--------------方法体----------------
function OnStart()
print('Lua OnStart')
end
function Print()
print('This Is SiBaDa!!!')
end
function Move() -- 移动事件
position = self.transform.position;
position = position + self.transform.forward * CS.UnityEngine.Time.deltaTime * forward * Manager.MoveSpeed + self.transform.right * CS.UnityEngine.Time.deltaTime * right * Manager.MoveSpeed;
self.transform.position = position;
--颜色改变 这个是Demo代码,拷过来用一哈
self.transform:GetComponent(typeof(CS.UnityEngine.MeshRenderer)).material.color = CS.UnityEngine.Color(CS.UnityEngine.Mathf.Sin(CS.UnityEngine.Time.time) / 2 + 0.5, 0, 0, 1)
end
function OnUpdate() -- Update
forward = CS.UnityEngine.Input.GetAxis('Vertical');
right = CS.UnityEngine.Input.GetAxis('Horizontal');
forward = CS.UnityEngine.Mathf.Clamp(forward,-1,1);
right = CS.UnityEngine.Mathf.Clamp(right,-1,1);
Move();
end
xlua.hotfix(CS.XLuaLoadScripts,'HotfixTest',function(self)
print('This Is SiBaDa!!!');
end
)
function OnDestroy()
end
然后,打包一下,打包的结构和AssetBundle那篇博客一致,此时运行
说明一下:最先出现的那个反射调用是我写的一个例子,忘记注释了 = =
=============================================热更================================================
假设,我们现在需要一个跳跃的功能,那么修改一下Lua代码,并且将Version.txt中的版本+1(因为我们用的加载是WWW.LoadFromCacheOrDownload,他会根据版本查找自己的缓存,也就是说,如果没有新版本的话,他会使用原先也就是上次我们缓存的AB包)
Lua新增代码如下:
function Jump()
if(CS.UnityEngine.Input.GetKeyDown('space'))
then
velocity = 5;
end
velocity = velocity - gracity * CS.UnityEngine.Time.deltaTime;
velocity = CS.UnityEngine.Mathf.Clamp(velocity,-20,20);
self.transform:GetComponent(typeof(CS.UnityEngine.Rigidbody)).velocity = CS.UnityEngine.Vector3(0,velocity,0);
end
function OnUpdate() -- Update
forward = CS.UnityEngine.Input.GetAxis('Vertical');
right = CS.UnityEngine.Input.GetAxis('Horizontal');
forward = CS.UnityEngine.Mathf.Clamp(forward,-1,1);
right = CS.UnityEngine.Mathf.Clamp(right,-1,1);
Move();
Jump();
end
然后,进入Unity中打个AB包,将新包覆盖进入FTP中,运行一哈
注:由于我也是刚接触XLua不到三四天,所以做了很多的实验,很多地方都用到服务器,所以在这里无法上传完整项目,见谅
- -
代码下载:https://download.csdn.net/download/mikulingsss/10694584