创建一个事件监听类,并把它添加到wrap中,让lua可以访问到它。
using UnityEngine;
using System;
using System.Collections;
using LuaInterface;
public sealed class TestEventListener : MonoBehaviour
{
public delegate void VoidDelegate(GameObject go);
public delegate void OnClick(GameObject go);
public OnClick onClick = delegate { };
public event OnClick onClickEvent = delegate { };
[NoToLuaAttribute]
public void OnClickEvent(GameObject go)
{
onClickEvent(go);
}
}在lua中对onClick和onClickEvent进行添加和删除事件
--listener为上面的那个类
function DoClick1(go)
print('click1 gameObject is '..go.name)
end
function AddClick1(listener) --给委托添加事件
if listener.onClick then
listener.onClick = listener.onClick + DoClick1
else
listener.onClick = DoClick1
end
end
function RemoveClick1(listener) --给委托移除事件
if listener.onClick then
listener.onClick = listener.onClick - DoClick1
else
print('empty delegate')
end
end
function TestEvent()
print('this is a event')
end
function AddEvent(listener)--事件添加事件
listener.onClickEvent = listener.onClickEvent + TestEvent
end
function RemoveEvent(listener)--事件移除事件
listener.onClickEvent = listener.onClickEvent - TestEvent
end
local t = {name = 'byself'}
function t:TestSelffunc()
print('callback with self: '..self.name)
end
function AddSelfClick(listener)
if listener.onClick then
listener.onClick = listener.onClick + TestEventListener.OnClick(t.TestSelffunc, t)
else
listener.onClick = TestEventListener.OnClick(t.TestSelffunc, t)
end
end
function RemoveSelfClick(listener)
if listener.onClick then
listener.onClick = listener.onClick - TestEventListener.OnClick(t.TestSelffunc, t)
else
print('empty delegate')
end
endc#中获取lua方法为委托添加或删除事件
LuaFunction func = state.GetFunction("DoClick1");
TestEventListener.OnClick onClick = (TestEventListener.OnClick)DelegateTraits<TestEventListener.OnClick>.Create(func);
listener.onClick += onClick;
LuaFunction func = state.GetFunction("DoClick1");
listener.onClick = (TestEventListener.OnClick)DelegateFactory.RemoveDelegate(listener.onClick, func);
func.Dispose();
func = null;
在c#中可以通过强转,将枚举和整形之间互相转换。那么在Lua中怎么转换呢?
space = UnityEngine.Space.World --Space是一个枚举 space:ToInt() --转换为数值 UnityEngine.Space.IntToEnum(0) --数值转换为枚举
using UnityEngine;
using System.Collections.Generic;
using LuaInterface;
public class AccessingLuaVariables : MonoBehaviour
{
private string luaScript =
@"
tab = {1,2,3,4,5}
tab.name = 'tab'
tab.map = {}
tab.map.name = 'map'
meta = {name = 'meta'}
setmetatable(tab,meta)
";
void Start ()
{
new LuaResLoader();
LuaState lua = new LuaState();
lua.Start();
lua.DoString(luaScript);
LuaTable tab = lua.GetTable("tab");
Debug.LogError(tab[1]); //1
Debug.LogError(tab["name"]); //tab
LuaTable map = tab["map"] as LuaTable;
Debug.LogError(map["name"]); //map
LuaTable meta = tab.GetMetaTable();
Debug.LogError(meta["name"]); //meta
table.Dispose();
lua.CheckTop();
lua.Dispose();
}
}
在编辑器中,实例化LuaState的时候就会将LuaConst.cs中定义的luaDir和toluaDir添加到lua脚本搜索目录中,所以这两个脚本一定得正确,在编辑器中,运行lua脚本就会使用这两个目录下的,也方便修改后立即看到效果。
void InitLuaPath()//LuaState构造方法中调用的其中一个方法
{
InitPackagePath();
if (!LuaFileUtils.Instance.beZip)
{
#if UNITY_EDITOR
if (!Directory.Exists(LuaConst.luaDir))
{
string msg = string.Format("luaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.luaDir);
throw new LuaException(msg);
}
if (!Directory.Exists(LuaConst.toluaDir))
{
string msg = string.Format("toluaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.toluaDir);
throw new LuaException(msg);
}
AddSearchPath(LuaConst.toluaDir);
AddSearchPath(LuaConst.luaDir);
#endif
if (LuaFileUtils.Instance.GetType() == typeof(LuaFileUtils))
{
AddSearchPath(LuaConst.luaResDir);
}
}
}当在手机上时,lua后缀极其的被排斥,放在哪里都不太行,所以需要打包成assetbundle,从而能够真正的热更新。由于unity中不认.lua文件,所以在打包的时候复制lua脚本到一个临时目录并添加后缀.bytes,然后根据文件夹名字标志assetbundle的名字,tolua中已经有了实现,我删除了打包的步骤,只用来标志名字了,因为我项目已经有了打包的方法了。
//ToLuaMenu.cs
[MenuItem("Lua/Build bundle files not jit", false, 55)]
public static void BuildNotJitBundles()
{
ClearAllLuaFiles();
CreateStreamDir(GetOS());
#if UNITY_4_6 || UNITY_4_7
string tempDir = CreateStreamDir("Lua");
#else
string tempDir = Application.dataPath + "/temp/Lua";
if (!File.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
#endif
CopyLuaBytesFiles(LuaConst.luaDir, tempDir);
CopyLuaBytesFiles(LuaConst.toluaDir, tempDir);
AssetDatabase.Refresh();
List<string> dirs = new List<string>();
GetAllDirs(tempDir, dirs);
for (int i = 0; i < dirs.Count; i++)
{
string str = dirs[i].Remove(0, tempDir.Length);
BuildLuaBundle(str.Replace('\\', '/'), "Assets/temp/Lua");
}
BuildLuaBundle(null, "Assets/temp/Lua");
AssetDatabase.SaveAssets();
//只用来标志BUndle的名字就行了
//string output = string.Format("{0}/{1}", Application.streamingAssetsPath, GetOS());
//BuildPipeline.BuildAssetBundles(output, BuildAssetBundleOptions.DeterministicAssetBundle, EditorUserBuildSettings.activeBuildTarget);
//Directory.Delete(Application.dataPath + "/temp/", true);
AssetDatabase.Refresh();
}
static void CopyLuaBytesFiles(string sourceDir, string destDir, bool appendext = true, string searchPattern = "*.lua", SearchOption option = SearchOption.AllDirectories)
{
if (!Directory.Exists(sourceDir))
{
return;
}
string[] files = Directory.GetFiles(sourceDir, searchPattern, option);
int len = sourceDir.Length;
if (sourceDir[len - 1] == '/' || sourceDir[len - 1] == '\\')
{
--len;
}
for (int i = 0; i < files.Length; i++)
{
string str = files[i].Remove(0, len);
string dest = destDir + "/" + str;
if (appendext) dest += ".bytes";
string dir = Path.GetDirectoryName(dest);
Directory.CreateDirectory(dir);
File.Copy(files[i], dest, true);
}
}
static void GetAllDirs(string dir, List<string> list)
{
string[] dirs = Directory.GetDirectories(dir);
list.AddRange(dirs);
for (int i = 0; i < dirs.Length; i++)
{
GetAllDirs(dirs[i], list);
}
}
static void BuildLuaBundle(string subDir, string sourceDir)
{
string[] files = Directory.GetFiles(sourceDir + subDir, "*.bytes");
string bundleName = subDir == null ? "lua": "lua" + subDir.Replace('/', '_') ;
bundleName = "scripts/" + bundleName.ToLower();
#if UNITY_4_6 || UNITY_4_7
List<Object> list = new List<Object>();
for (int i = 0; i < files.Length; i++)
{
Object obj = AssetDatabase.LoadMainAssetAtPath(files[i]);
list.Add(obj);
}
BuildAssetBundleOptions options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle;
if (files.Length > 0)
{
string output = string.Format("{0}/{1}/" + bundleName, Application.streamingAssetsPath, GetOS());
File.Delete(output);
BuildPipeline.BuildAssetBundle(null, list.ToArray(), output, options, EditorUserBuildSettings.activeBuildTarget);
}
#else
string bundleVariant = com.Bowen.Game.Lobby.GlobalConst.Res.Phone_BundleFileExt.Replace(".", "");
for (int i = 0; i < files.Length; i++)
{
AssetImporter importer = AssetImporter.GetAtPath(files[i]);
if (importer)
{
importer.assetBundleName = bundleName;
importer.assetBundleVariant = bundleVariant;
}
}
#endif
}好了,现在lua脚本都复制到了temp/Lua下面了,并且lua脚本根据所在的子文件夹标志了一个包名字,只需要调用Unity提供的打包方法就可以了,写入自己框架中的资源文件中。
public static bool BuildAssetBundle(string path, BuildTarget target, List<GameConfig> games, ref List<AssetBundleInfo> abList)
{
try
{
var manifest = BuildAssetBundle(path, target, games);
if (manifest == null)
{
Debug.Log("null manifest");
return true;
}
string[] assetBundles = manifest.GetAllAssetBundles();
foreach (var ab in assetBundles)
{
string name = ab.Replace(Path.GetExtension(ab), "");
var data_type = new AssetBundleInfo();
//data_type.ID = "";
data_type.Name = name;
data_type.FileName = ab;
data_type.Hash = MD5Util.GetFileMD5(path + ab);
data_type.Version = "0";
var oldItem = abList.Find(item => { return string.Compare(item.Name, name, true) == 0; });
if (oldItem != null)
{
abList.Remove(oldItem);
if (string.Compare(oldItem.Hash, data_type.Hash, true) != 0)
{
data_type.UpdateVersion();
}
}
Debug.Log("add builded ab:" + data_type + " name:" + name);
abList.Add(data_type);
}
Debug.Log("assetbundle build finish");
return true;
}
catch (Exception e)
{
Debug.LogException(e);
return false;
}
}
private static AssetBundleManifest BuildAssetBundle(string path, BuildTarget target, List<GameConfig> games)
{
var builds = new List<AssetBundleBuild>();
var abs = AssetDatabase.GetAllAssetBundleNames();
foreach (var ab in abs)
{
var build = new AssetBundleBuild();
build.assetBundleName = Path.GetDirectoryName(ab) + "/" + Path.GetFileNameWithoutExtension(ab);
build.assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(ab);
build.assetBundleVariant = Path.GetExtension(ab).Replace(".", "");
builds.Add(build);
Debug.Log("name:" + build.assetBundleName + " variant:" + build.assetBundleVariant + " ab:" + ab);
}
Debug.Log("Build scene for target:" + target + " by path:" + path);
if (EditorUserBuildSettings.activeBuildTarget != target)
{
EditorUserBuildSettings.SwitchActiveBuildTarget(target);
}
return BuildPipeline.BuildAssetBundles(
path,
builds.ToArray(),
BuildAssetBundleOptions.DeterministicAssetBundle/* |
BuildAssetBundleOptions.ForceRebuildAssetBundle*/,
target
);
}在框架下载后,读取assetbundle包中的lua脚本
//LuaManager.cs 在资源下载完成后,调用初始化方法
public void InitStart()
{
InitLuaBundle();
this.luaState.Start(); //启动LUAVM
//this.StartMain();
this.StartLooper();
}
/// <summary>
/// 初始化LuaBundle,加载下载的Bundle到一个字典中缓存起来
/// </summary>
void InitLuaBundle()
{
if (loader.beZip)
{
loader.AddBundle("lua.mp");
loader.AddBundle("lua_math.mp");
loader.AddBundle("lua_system.mp");
loader.AddBundle("lua_system_reflection.mp");
loader.AddBundle("lua_unityengine.mp");
loader.AddBundle("lua_common.mp");
loader.AddBundle("lua_logic.mp");
loader.AddBundle("lua_view.mp");
loader.AddBundle("lua_controller.mp");
loader.AddBundle("lua_misc.mp");
loader.AddBundle("lua_protobuf.mp");
loader.AddBundle("lua_3rd_cjson.mp");
loader.AddBundle("lua_3rd_luabitop.mp");
loader.AddBundle("lua_3rd_pbc.mp");
loader.AddBundle("lua_3rd_pblua.mp");
loader.AddBundle("lua_3rd_sproto.mp");
}
}
//这里是loader的类 LuaLoader.cs
using UnityEngine;
using System.Collections;
using System.IO;
using LuaInterface;
/// <summary>
/// 集成自LuaFileUtils,重写里面的ReadFile,继承LuaFileUtils
/// </summary>
public class LuaLoader : LuaFileUtils {
// Use this for initialization
public LuaLoader(bool isZip = false) {
instance = this;
beZip = isZip;
}
/// <summary>
/// 添加打入Lua代码的AssetBundle
/// </summary>
/// <param name="bundle"></param>
public void AddBundle(string bundleName) {
string url = "";
#if !UNITY_EDITOR
//lua bundle资源下载的目录
url = LuaConst.luaResDir + "/" + bundleName.ToLower();
#else
url = LuaConst.luaWinTestResDir + "/" + bundleName.ToLower();
#endif
if (File.Exists(url)) {
AssetBundle bundle = AssetBundle.LoadFromFile(url);
if (bundle != null)
{
bundleName = bundleName.Replace("lua/", "").Replace(".mp", "");
base.AddSearchBundle(bundleName.ToLower(), bundle);
}
}
}
/// <summary>
/// 当LuaVM加载Lua文件的时候,这里就会被调用,
/// 用户可以自定义加载行为,只要返回byte[]即可。
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public override byte[] ReadFile(string fileName) {
return base.ReadFile(fileName);
}
}好了LuaFileUtils里面有个zipMao字典有了所有的lua的AssetBundle包,当我们想调用lua脚本的时候,我们可以自由的去读取了,因为我们有做映射,就是不知道哪个lua脚本在哪个包中,所以我就直接遍历所有的包了,先实现再优化了。
byte[] ReadZipFile(string fileName)
{
byte[] buffer = null;
string zipName = null;
using (CString.Block())
{
CString sb = CString.Alloc(256);
sb.Append("lua");
int pos = fileName.LastIndexOf('/');
if (pos > 0)
{
sb.Append("_");
sb.Append(fileName, 0, pos).ToLower().Replace('/', '_');
fileName = fileName.Substring(pos + 1);
}
if (!fileName.EndsWith(".lua"))
{
fileName += ".lua";
}
#if UNITY_5 || UNITY_5_3_OR_NEWER
fileName += ".bytes";
#endif
zipName = sb.ToString();
//这里要优化的 现在遍历查找
foreach (AssetBundle bundle in zipMap.Values)
{
#if UNITY_4_6 || UNITY_4_7
TextAsset luaCode = bundle.Load(fileName, typeof(TextAsset)) as TextAsset;
#else
TextAsset luaCode = bundle.LoadAsset<TextAsset>(fileName);
#endif
if (luaCode != null)
{
buffer = luaCode.bytes;
Resources.UnloadAsset(luaCode);
break;
}
}
}
return buffer;
}区分是否读取包的lua还是LuaConst.cs中那两个目录的lua,是LuaFileUtils.cs中一个叫beZip的参数,设置为true表示加载的是资源包的。
UNity5.6+
官方的,在这里下载https://github.com/Unity-Technologies/AssetBundles-Browser
导入到Editor文件夹就行了,使用方法:Window->AssetBundle Browser,其他摸索
IEnumerator Start () {
UnityWebRequest request = UnityWebRequest.GetAssetBundle (baseUrl + "AssetBundle",0);
yield return request.SendWebRequest ();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent (request);
AssetBundleManifest abm = ab.LoadAsset<AssetBundleManifest> ("AssetBundleManifest");
foreach (var a in abm.GetAllDependencies("cub")) {
using (UnityWebRequest r = UnityWebRequest.GetAssetBundle (baseUrl + a, 0)) {
yield return r.SendWebRequest ();
DownloadHandlerAssetBundle.GetContent (r);//加载到依赖包到内存中
}
}
//依赖包加载完成加载完成后,实例化Cube才不会丢失材质和贴图
using (UnityWebRequest r = UnityWebRequest.GetAssetBundle (baseUrl + "cub")) {
yield return r.SendWebRequest ();
AssetBundle assb = DownloadHandlerAssetBundle.GetContent (r);
GameObject go = assb.LoadAsset<GameObject> ("Cube");
Instantiate (go);
}
}还记得那个怎么都有的AssetBundle文件吗,没错,这里记载了所有你打包出来的AssetBundle的依赖
加载名字叫做那个就是那个AssetBundle的文件,获取Manifest,通过GetAllDependencies()获取cub的依赖(字符串,包名),因为等下我要加载cub里面的Cube
在使用BuildPipeline.BuildAssetBundles()的第二个参数可以指定构建的压缩选项
BuildAssetBundleOptions.None ->使用的是LZMA算法,压缩包更小,但是加载时需要整体解压。下载后,就会使用LZ4重新压缩。
BuildAssetBundleOptions.ChunkBasedCompression ->使用的是LZ4的压缩方法,包较小且有较快的加载速度
BuildAssetBundleOptions.UncompressedAssetBundle ->不压缩,包大,加载速度最快
AssetBundle的打包会自动识别依赖,所以可以将一些公用的资源(贴图,材质等)打包到一个包上(共享包),其他的资源在分类打包,避免贴图等共享(多次使用的资源)被打包了多次,增加总体包的大小
在指定资源的assetbundle的名字的时候,可以进行目录分类,只需要使用路径的形式来命名,例如"scene/texture",这样texture这个AssetBundle就在scene的目录下
在lua代码中,肯定要使用到Unity中内置的类和自己自定义的类,在Assets/Editor/Custom目录下有一个CustomSettings.cs这里配置了lua中要使用到的类,也只有在这里配置了之后,ToLua才会把这些类映射到lua中,这样才可以在lua中使用这些类。
例如:
_GT(typeof(Text)), //UGUI的Text
TextUI = TextUI:GetComponent(typeof(UnityEngine.UI.Text)) --加上命名空间