ToLua的lua脚本打包和加载

在编辑器中,实例化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表示加载的是资源包的。


首页 我的博客
粤ICP备17103704号