Unity + XLua 简单框架搭建

Unity + XLua 简单框架结构

Xlua环境搭建教程入口.

一.unity内创建lua脚本

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
using UnityEngine;

public class GenLua
{
    [MenuItem("Assets/Create/Lua Script", false, 80)]
    public static void CreatNewLua()
    {
        ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0,
        ScriptableObject.CreateInstance<MyDoCreateScriptAsset>(),
        GetSelectedPathOrFallback() + "/New Lua.lua",
        null,
       "Assets/Editor/Template/NewLua.txt");
    }



    public static string GetSelectedPathOrFallback()
    {
        string path = "Assets";
        foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
        {
            path = AssetDatabase.GetAssetPath(obj);
            if (!string.IsNullOrEmpty(path) && File.Exists(path))
            {
                path = Path.GetDirectoryName(path);
                break;
            }
        }
        return path;
    }
}

class MyDoCreateScriptAsset : EndNameEditAction
{
    public override void Action(int instanceId, string pathName, string resourceFile)
    {
        UnityEngine.Object o = CreateScriptAssetFromTemplate(pathName, resourceFile);
        ProjectWindowUtil.ShowCreatedAsset(o);
    }

    internal static UnityEngine.Object CreateScriptAssetFromTemplate(string pathName, string resourceFile)
    {
        string fullPath = Path.GetFullPath(pathName);
        StreamReader streamReader = new StreamReader(resourceFile);
        string text = streamReader.ReadToEnd();
        streamReader.Close();
        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(pathName);
        text = Regex.Replace(text, "#NAME#", fileNameWithoutExtension);
        bool encoderShouldEmitUTF8Identifier = true;
        bool throwOnInvalidBytes = false;
        UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier, throwOnInvalidBytes);
        bool append = false;
        StreamWriter streamWriter = new StreamWriter(fullPath, append, encoding);
        streamWriter.Write(text);
        streamWriter.Close();
        AssetDatabase.ImportAsset(pathName);
        return AssetDatabase.LoadAssetAtPath(pathName, typeof(UnityEngine.Object));
    }

}

代码转载自雨松MoMo
将此段代码放入unity的Editor目录下,然后在相应目录下创建NewLua.txt模板,即可像创建C#脚本那样创建lua脚本

NewLua.txt 模板

local #NAME# = class()
-- 自定义数据
-- 构造函数
function #NAME#:Ctor(params)
end
-- 开始函数
function #NAME#:Start()
end
return #NAME#

在unity中新建一个LuaScript文件夹,用于保存lua脚本

二.创建 LuaManager 脚本

可以创建lua脚本后,直接进入正题。在C#中执行Lua脚本,我们可以new一个LuaEnv环境变量来使用Dostring方法来执行lua脚本。LuaManager用于更方便的执行lua脚本。代码如下

public class ST_LuaMannager : MonoBehaviour
{
    private static readonly object padlock = new object();
    private static ST_LuaMannager instance;
    //统一管理Update
    public event Action luaUpdates;
    //单例类
    public static ST_LuaMannager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        GameObject go = new GameObject("LuaManager");
                        DontDestroyOnLoad(go);
                        instance = go.AddComponent<ST_LuaMannager>();
                    }
                }

            }
            return instance;
        }
    }
    LuaEnv luaEnv;
    float lastGcTime = 0;
    //luaGC回收时间见隔
    const float GCInterval = 1;
    //全局唯一lua环境
    public LuaEnv MyLuaEnv
    {
        get
        {
            if (luaEnv == null)
            {
                luaEnv = new LuaEnv();
                luaEnv.AddLoader(LuaLoader);
                //加载lua全局变量
                luaEnv.DoString("require 'ST/ST_LuaEnv'");
                //luaEnv.DoString("require 'TestClass'");
            }
            return luaEnv;
        }
    }
    //自定义Loader
    public static byte[] LuaLoader(ref string filename)
    {
        byte[] byArrayReturn = null;
        string luaPath = Application.dataPath + "/LuaScripts/" + filename.Replace(".","/") + ".lua";
        string strLuaConent = File.ReadAllText(luaPath);
        byArrayReturn = System.Text.Encoding.UTF8.GetBytes(strLuaConent);
        return byArrayReturn;
    }
	
	//封装dostring 方法
    public object[] Dostring(string str, string chunkName = "chunk")
    {
        var ret = MyLuaEnv.DoString(str, chunkName);
        return ret;
    }


    private void Update()
    {
    	//定时GC
        if (Time.time - lastGcTime > GCInterval)
        {
            MyLuaEnv.Tick();
            lastGcTime = Time.time;
        }
        //管理luaUpdate
        if(luaUpdates != null)
        {
            luaUpdates();
        }
    }
}

这里采用自定义Loader的方法加载Lua,并使用全局唯一的lua环境,ST_LuaEnv 里面主要包含lua环境的公共变量和函数。Update方法里实现lua定时垃圾回收,执行绑定lua 脚本的Update方法。

测试

分别创建ST_LuaEnv.lua,Tests.lua,TestEnv.cs脚本
ST_LuaEnv

TestValue = 2
function Add(a,b)
    return a + b
end

Tests.lua

print(Add(TestValue,12))

TestEnv.cs

public class TestEnv : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        ST_LuaMannager.Instance.Dostring("require 'Tests'");
    }
}

创建空节点,运行Unity,结果如下
在这里插入图片描述

三.Lua面向对象

让Lua脚本具有面向对象特性可以使我们编程更加方便,在ST_LuaEnv里面添加面向对象方法代码如下

function clone(object)
    local lookup_table = {}
    local function copyObj(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end

        local new_table = {}
        lookup_table[object] = new_table
        for key, value in pairs(object) do
            new_table[copyObj(key)] = copyObj(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return copyObj(object)
end

local _class = {}
--[[
    @desc: class
    author:{}
    time:2022-01-24 17:47:45
    --@super: 父类
    @return:返回一个类
]]
function class(super)
    local class_type = {}
    class_type.Ctor = false
    class_type.super = super
    class_type.New = function(...)
        local obj = {}
        do
            local create
            create = function(c, ...)
                if c.super then
                    create(c.super, ...)
                end
                if c.Ctor then
                    c.Ctor(obj, ...)
                end
            end

            create(class_type, ...)
        end
        --setmetatable(obj,{ __index=_class[class_type] })(原)
        setmetatable(obj, {__index = clone(_class[class_type])})
        return obj
    end
    local vtbl = {}
    _class[class_type] = vtbl

    setmetatable(class_type, {
        __newindex = function(t, k, v)
            vtbl[k] = v
        end
    })

    if super then
        setmetatable(vtbl, {
            __index = function(t, k)
                local ret = _class[super][k]
                vtbl[k] = ret
                return ret
            end
        })
    end

    return class_type
end

这里的面向对象方法引用自云风的个人空间 并对代码做了一定的修改,满足我们的要求,测试代码如下

local TestClass = class()
-- 自定义数据
TestClass.ta = {x = 1}

local a1 = TestClass.New();
local s = a1.ta;
print(a1.ta.x)
s.x = 55
local a2 = TestClass.New();
print(a2.ta.x)

原代码输出如下(可以看到,修改a1实列的ta.x值后基类的ta.x值也一并修改了)
在这里插入图片描述
修改后
在这里插入图片描述

四.GameObject绑定lua脚本

创建ST_LuaBehaviours.cs 代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System;
using DG.Tweening;
[LuaCallCSharp]
public class ST_LuaBehaviours : MonoBehaviour
{
    //文件在LuaScript下的路径
    public string LuaFile;
    //需要绑定的对象数组
    public Injection[] injections;
    //绑定的luaTable
    LuaTable luaTable;
    //Lua调C#方法
    private Action<LuaTable> luaStart;
    private Action<LuaTable> luaUpdate;
    private Action<LuaTable> luaOnDestroy;
    //Update方法代理
    private Action luaUpadteDe;
    private void Awake()
    {
        object[] kRet = ST_LuaMannager.Instance.Dostring($"return require '{LuaFile}'.New()", LuaFile);
        if (null == kRet)
        {
            Debug.LogError("Luafile need return local table");
            return;
        }
        luaTable = kRet[0] as LuaTable;
        //设置绑定的对象
        foreach (var injection in injections)
        {
            luaTable.Set(injection.name, injection.value);
        }
        //设置luaCom
        luaTable.Set("luaCom", this);
        //设置gameObject
        luaTable.Set("gameObject", gameObject);
        //设置transform
        luaTable.Set("transform", transform);
        Action luaAwake = luaTable.Get<Action>("awake");
        //自动调用方法
        luaTable.Get("Start", out luaStart);
        luaTable.Get("Update", out luaUpdate);
        luaTable.Get("OnDestroy", out luaOnDestroy);
        //调用luaAwake方法
        if (luaAwake != null)
        {
            luaAwake();
        }

    }

    //脚本生效时注册Update
    private void OnEnable()
    {
        if (luaUpdate != null)
        {
            luaUpadteDe = () => { luaUpdate(luaTable); };
            ST_LuaMannager.Instance.luaUpdates += luaUpadteDe;
        }
    }

    //脚本失效时注销Update
    private void OnDisable()
    {
        if (luaUpadteDe != null)
        {
            ST_LuaMannager.Instance.luaUpdates -= luaUpadteDe;
        }
    }
    void Start()
    {
        //执行luaStart
        if (luaStart != null)
        {
            luaStart(luaTable);
        }
    }

    //对象销毁时
    private void OnDestroy()
    {
        //调用luaOnDestroy方法
        if (luaOnDestroy != null)
        {
            luaOnDestroy(luaTable);
        }
        luaOnDestroy = null;
        luaUpdate = null;
        luaStart = null;
        injections = null;
        luaUpadteDe = null;
    }
}


[Serializable]
public class Injection
{
    public string name;
    public GameObject value;
}

测试
创建TestMono.lua文件

local TestMono = class()
-- 自定义数据


-- 构造函数
function TestMono:Ctor(params)
end

-- 开始函数
function TestMono:Start()
end

function TestMono:Update()
    self.transform:Rotate(CS.UnityEngine.Vector3(0,1,0));
    self.capsule.transform:Rotate(CS.UnityEngine.Vector3(1,0,0))
end
return TestMono

在场景中创建一个Cube,和一个Capsule,并将ST_LuaBehaviours挂到Cube上,如图:
在这里插入图片描述

运行游戏,可以看到Cube,和Capsule旋转,那么测试通过。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值