Unity项目接入xLua的一种流程

news2025/2/11 9:49:11

1. 导入xlua

首先导入xlua,这个不用多说
在这里插入图片描述

2. 编写C#和Lua交互脚本

基础版本,即xlua自带的版本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System;
using System.IO;

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

/// <summary>
/// XLua的原生版本
/// </summary>
public class LuaBehaviour : MonoBehaviour
{
    public TextAsset luaScript;
    public Injection[] injections;
    
    /// <summary>
    /// 虚拟机唯一
    /// </summary>
    internal static LuaEnv luaEnv = new LuaEnv();
    
    /// <summary>
    /// 上一次的GC时间
    /// </summary>
    internal static float lastGCTime = 0; 
    
    /// <summary>
    /// GC间隔
    /// </summary>
    internal const float GCInterval = 1f;

    private Action luaStart;
    private Action luaUpdate;
    private Action luaOnDestroy;
    
    private LuaTable scriptScopeTable;
    
    
    private void Awake()
    {
        SetPackagePath();
        
        scriptScopeTable = luaEnv.NewTable();
        
        //给scriptScopeTable设置__index元表,使其能够访问全局
        using (LuaTable meta = luaEnv.NewTable())
        {
            meta.Set("__index", luaEnv.Global);
            scriptScopeTable.SetMetaTable(meta);
        }
        
        scriptScopeTable.Set("self", this);
        foreach (var injection in injections)
        {
            scriptScopeTable.Set(injection.name, injection.value);
        }
        
        //执行脚本
        luaEnv.DoString(luaScript.text, luaScript.name, scriptScopeTable);
        
        //获得生命周期绑定,这里直接使用scriptScopeTable,代表是使用这个脚本的全局去设置的
        Action luaAwake = scriptScopeTable.Get<Action>("Awake");
        scriptScopeTable.Get("Start", out luaStart);
        scriptScopeTable.Get("Update", out luaUpdate);
        scriptScopeTable.Get("OnDestroy", out luaOnDestroy);

        if (luaAwake != null)
        {
            luaAwake();
        }
    }

    private void Start()
    {
        if (luaStart != null)
        {
            luaStart();
        }
    }

    private void Update()
    {
        if (luaUpdate != null)
        {
            luaUpdate();
        }

        if (Time.time - lastGCTime > GCInterval)
        {
            luaEnv.Tick();
            lastGCTime = Time.time;
        }
    }

    private void OnDestroy()
    {
        if (luaOnDestroy != null)
        {
            luaOnDestroy();
        }
        
        scriptScopeTable.Dispose();
        luaStart = null;
        luaUpdate = null;
        luaOnDestroy = null;
        injections = null;
    }
    
    /// <summary>
    /// 设置xlua的加载路径
    /// </summary>
    private void SetPackagePath()
    {
        //在Unity项目的“Assets”文件夹(或指定的Application.dataPath路径)及其所有子目录中,查找名为“LuaScripts”的目录,并返回一个包含这些目录路径的字符串数组
        foreach (string dir in Directory.GetDirectories(Application.dataPath,"LuaScripts", SearchOption.AllDirectories))
        {
            luaEnv.AddLoader(new FileLoader(dir, ".lua"));
            luaEnv.AddLoader(new FileLoader(dir, ".lua.txt"));
        }
    }
}

Loxodon的版本

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
using Object = UnityEngine.Object;


/// <summary>
/// Loxodon的版本
/// </summary>
[LuaCallCSharp]
public class LoxodonLuaBehaviour : MonoBehaviour
{
    public ScriptReference script;
    public VariableArray variables;

    protected LuaTable scriptEnv;
    protected LuaTable metatable;
    protected Action<MonoBehaviour> onAwake;
    protected Action<MonoBehaviour> onEnable;
    protected Action<MonoBehaviour> onDisable;
    protected Action<MonoBehaviour> onUpdate;
    protected Action<MonoBehaviour> onDestroy;
    protected Action<MonoBehaviour> onStart;


    public LuaTable GetMetatable()
    {
        return metatable;
    }

    protected virtual void Initialize()
    {
        var luaEnv = LuaEnvironment.LuaEnv;
        scriptEnv = luaEnv.NewTable();

        LuaTable meta = luaEnv.NewTable();
        meta.Set("__index", luaEnv.Global);
        scriptEnv.SetMetaTable(meta);
        meta.Dispose();
        
        scriptEnv.Set("target", this);
        
        SetPackagePath(luaEnv);

        string scriptText = (script.Type == ScriptReferenceType.TextAsset)
            ? script.Text.text
            : string.Format(
                $"require(\"System\");local cls = require(\"{script.FileName}\");return extends(target,cls);");
        object[] result = luaEnv.DoString(scriptText, string.Format("{0}({1})", "LuaBehaviour", this.name), scriptEnv);
        
        if (result.Length != 1 || !(result[0] is LuaTable))
        {
            throw new Exception("");
        }
        
        metatable = (LuaTable)result[0];  //这里使用Lua脚本的返回值去设置,即脚本的返回值去绑定生命周期
        if (variables != null && variables.Variables != null)
        {
            foreach (var variable in variables.Variables)
            {
                var name = variable.Name.Trim();
                if (string.IsNullOrEmpty(name))
                {
                    continue;
                }
                
                metatable.Set(name, variable.GetValue());
            }
        }
        
        
        onAwake = metatable.Get<Action<MonoBehaviour>>("Awake");
        onEnable = metatable.Get<Action<MonoBehaviour>>("Enable");
        onDisable = metatable.Get<Action<MonoBehaviour>>("Disable");
        onStart = metatable.Get<Action<MonoBehaviour>>("Start");
        onUpdate = metatable.Get<Action<MonoBehaviour>>("Update");
        onDestroy = metatable.Get<Action<MonoBehaviour>>("Destroy");
    }

    protected virtual void Awake()
    {
        this.Initialize();
        if (this.onAwake != null)
        {
            this.onAwake(this);
        }
    }

    protected virtual void OnEnable()
    {
        if (this.onEnable != null)
        {
            this.onEnable(this);
        }
    }

    protected virtual void OnDisable()
    {
        if (this.onDisable != null)
        {
            this.onDisable(this);
        }
    }

    protected virtual void Start()
    {
        if (onStart != null)
        {
            onStart(this);
        }
    }

    protected virtual void Update()
    {
        if (onUpdate != null)
        {
            onUpdate(this);
        }
    }

    protected virtual void OnDestroy()
    {
        if (this.onDestroy != null)
        {
            this.onDestroy(this);
        }
        
        onDestroy = null;
        onUpdate = null;
        onStart = null;
        onEnable = null;
        onDisable = null;
        onAwake = null;

        if (metatable != null)
        {
            metatable.Dispose();
            metatable = null;
        }

        if (scriptEnv != null)
        {
            scriptEnv.Dispose();
            scriptEnv = null;
        }
    }
    
    /// <summary>
    /// 修改lua的loader
    /// </summary>
    /// <param name="luaEnv"></param>
    private void SetPackagePath(LuaEnv luaEnv)
    {
        //在Unity项目的“Assets”文件夹(或指定的Application.dataPath路径)及其所有子目录中,查找名为“LuaScripts”的目录,并返回一个包含这些目录路径的字符串数组
        foreach (string dir in Directory.GetDirectories(Application.dataPath,"LuaScripts", SearchOption.AllDirectories))
        {
            luaEnv.AddLoader(new FileLoader(dir, ".lua"));
            luaEnv.AddLoader(new FileLoader(dir, ".lua.txt"));
        }
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;

public class LuaEnvironment
{
    private static float interval = 2;
    private static WaitForSecondsRealtime wait;
    private static LuaEnv luaEnv;
    //private static IAsyncResult result;

    public static float Interval
    {
        get => interval;
        set
        {
            if (interval <= 0)
            {
                return;
            }
            interval = value;
            wait = new WaitForSecondsRealtime(interval);
        }
    }

    public static LuaEnv LuaEnv
    {
        get
        {
            if (luaEnv == null)
            {
                luaEnv = new LuaEnv();
                // if (result != null)
                //     result.Cancel();
                wait = new WaitForSecondsRealtime(interval);
                //result = Executors.RunOnCoroutine(DoTick());
                luaEnv.Tick();
            }
            return luaEnv;
        }
    }

    public static void Dispose()
    {
        // if (result != null)
        // {
        //     result.Cancel();
        //     result = null;
        // }

        if (luaEnv != null)
        {
            luaEnv.Dispose();
            luaEnv = null;
        }
        wait = null;
    }

    private static IEnumerator DoTick()
    {
        while (true)
        {
            yield return wait;
            try
            {
                luaEnv.Tick();
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Object = UnityEngine.Object;

public enum ScriptReferenceType
{
    TextAsset,
    FileName
}

[Serializable]
public class ScriptReference : ISerializationCallbackReceiver
{
#if UNITY_EDITOR
    [SerializeField]
    private Object cachedAsset;
#endif
    [SerializeField]
    protected TextAsset text;
    [SerializeField]
    protected string fileName;
    [SerializeField]
    protected ScriptReferenceType type = ScriptReferenceType.TextAsset;
    
    public virtual ScriptReferenceType Type => type;
    
    public virtual TextAsset Text => text;
    
    public virtual string FileName => fileName;
    
    
    public void OnBeforeSerialize()
    {
        Clear();
    }

    public void OnAfterDeserialize()
    {
        Clear();
    }

    protected virtual void Clear()
    {
#if !UNITY_EDITOR
        switch (type)
        {
            case ScriptReferenceType.TextAsset:
                this.fileName = null;
                break;
            case ScriptReferenceType.FileName:
                this.text = null;
                break;
        }
#endif
    }
}

具体区别可以看两种不同的LuaBehaviour生命周期绑定

然后注意这里的lua文件读取路径设置
在这里插入图片描述
具体可以看xlua中自定义lua文件加载的一种方式
在这里插入图片描述

3.自定义属性面板

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. Lua脚本部分

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

因为提前设置了Lua文件的读取路径,都在LuaScripts文件夹下

运行
在这里插入图片描述

在这里插入图片描述
Lua脚本执行了对应的逻辑,将text的值变为了“你好”,同时打印了协程的输出

注意使用前让xlua生成代码
在这里插入图片描述

项目链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2295464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Deepseek 接入Word处理对话框(隐藏密钥)

硅基流动邀请码&#xff1a;1zNe93Cp 邀请链接&#xff1a;网页链接 亲测deepseek接入word&#xff0c;自由调用对话&#xff0c;看截图有兴趣的复用代码&#xff08;当然也可以自己向deepseek提问&#xff0c;帮助你完成接入&#xff0c;但是提问逻辑不一样给出的答案是千差万…

Office/WPS接入DS等多个AI工具,开启办公新模式!

在现代职场中&#xff0c;Office办公套件已成为工作和学习的必备工具&#xff0c;其功能强大但复杂&#xff0c;熟练掌握需要系统的学习。为了简化操作&#xff0c;使每个人都能轻松使用各种功能&#xff0c;市场上涌现出各类办公插件。这些插件不仅提升了用户体验&#xff0c;…

ximalaya(三) playUriList值解密--webpack

本文主要介绍解密音频播放url参数。 本文仅代表个人理解&#xff0c;如有其他建议可在评论区沟通。 声明 仅仅记录一下自己的学习方法&#xff0c;不作为其他参考、更不作为商业用途。如有侵犯请联系本人删除 目标地址&#xff1a;aHR0cHM6Ly93d3cueGltYWxheWEuY29tL3NvdW5k…

ASP.NET Core JWT

目录 Session的缺点 JWT&#xff08;Json Web Token&#xff09; 优点&#xff1a; 登录流程 JWT的基本使用 生成JWT 解码JWT 用JwtSecurityTokenHandler对JWT解码 注意 Session的缺点 对于分布式集群环境&#xff0c;Session数据保存在服务器内存中就不合适了&#…

光伏-报告显示,假期内,硅料端签单顺序发货相对稳定。若3月份下游存提产,则不排除硅料价格有上调预期。

据TrendForce集邦咨询报告显示&#xff0c;假期内&#xff0c;硅料端按照前期签单顺序发货&#xff0c;相对稳定。若3月份下游存提产&#xff0c;则不排除硅料价格有上调预期。 002306中科云网 旅游 | 公司为提供复合菜系特色餐饮的连锁企业&#xff0c;形成了以粤菜&#xff…

【信息系统项目管理师-案例真题】2017上半年案例分析答案和详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 试题一【问题1】8 分【问题2】4 分【问题3】8 分【问题4】5 分试题二【问题1】10 分【问题2】8 分【问题3】6 分【问题4】5 分试题三【问题1】5 分【问题2】7 分【问题3】6 分【问题4】3 分试题一 阅读下列说明…

滴水逆向_程序实现弹窗修改OEP

作业&#xff1a; 几个很重要的注意事项。 1 我们模拟的是内核如何将一个文件硬盘中拉伸到内存中&#xff0c;但是我们做的仅仅是 模拟拉伸过程。也就是说其中的属性字段是无差别的拷贝的。 但是加载exe的时候 &#xff0c;imagebase 是随机分配的。 我们打开内存中的exe&…

快速上手——.net封装使用DeekSeek-V3 模型

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,用爱发电,去丈量人心,是否能达到人机合一?开工大吉 新的一年就这么水灵灵的开始了,在这里,祝各位读者新春快乐,万事如意! 新年伊…

ReactNative进阶(五十九):存量 react-native 项目适配 HarmonyOS NEXT

文章目录 一、前言二、ohos_react_native2.1 Fabric2.2 TurboModule2.2.1 ArkTSTurboModule2.2.2 cxxTurboModule&#xff1a; 三、拓展阅读 一、前言 2024年10月22日19:00&#xff0c;华为在深圳举办“原生鸿蒙之夜暨华为全场景新品发布会”&#xff0c;主题为“星河璀璨&…

1-2 面向对象编程方法

1.0 面向对象编程思维 在面向对象风格中&#xff0c;结构体被看做数据&#xff08;data&#xff09;&#xff0c;而操作数据的函数称作方法&#xff08;method&#xff09;。目前函数 和数据是分离的&#xff0c;函数并不直接操作数据&#xff0c;我们需要拿到函数返回的结果&a…

CSS 组合选择符详解与实战示例

在 Web 开发过程中&#xff0c;CSS 用于定义页面元素的样式&#xff0c;而选择器则帮助我们精确定位需要添加样式的元素。今天我们主要来讲解 CSS 中的组合选择符&#xff0c;它们能够根据 DOM 结构中元素之间的关系来选中目标元素&#xff0c;从而写出结构清晰、易于维护的 CS…

html为<td>添加标注文本

样式说明&#xff1a; /*为td添加相对定位点*/ .td_text {position: relative; }/*为p添加绝对坐标(相对于父元素中的定位点)*/ .td_text p {position: absolute;top: 80%;font-size: 8px; }参考资料&#xff1a;

apachePoi中XSSFClientAnchor图片坐标简述;填充多张图片

概述 业务中经常会遇到在单元格内填充图片的需求&#xff0c;而且要求指定图片在单元格内的位置。 一般都是用的apache的poi&#xff0c;设置图片坐标。 HSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2)dx1 dy1 起始单元…

无界构建微前端?NO!NO!NO!多系统融合思路!

文章目录 微前端理解1、微前端概念2、微前端特性3、微前端方案a、iframeb、qiankun --> 使用比较复杂 --> 自己写对vite的插件c、micro-app --> 京东开发 --> 对vite支持更拉跨d、EMP 方案--> 必须使用 webpack5 --> 很多人感觉不是微前端 --> 去中心化方…

为飞牛OS基于FRP的内网穿透开启HTTPS加密

前言 玩NAS的朋友应该有比较多只是在家庭局域网使用&#xff0c;比如日常看看电影、备份手机照片什么的&#xff0c;这属于家庭局域网的使用场景。 当然了&#xff0c;如果你经常出差&#xff0c;或者过年回家不想把NAS也背回去&#xff0c;或者是想上班摸鱼&#xff0c;或者是…

《基于Python与DashScope的智能语音合成工具开发》

《基于Python与DashScope的智能语音合成工具开发》 在当今数字化时代&#xff0c;语音合成技术已经广泛应用于各种场景&#xff0c;如智能语音助手、有声读物、导航系统等。本文将介绍如何使用Python和DashScope平台开发一个简单而功能强大的文字转语音工具。通过这个工具&…

快速上手Vim的使用

Vim Linux编辑器-vim使用命令行模式下所有选项都可以带数字底行模式可视块模式&#xff08;ctrlV进入&#xff09; Linux编辑器-vim使用 Vim有多种模式的编辑器。能帮助我们很快的进行代码的编辑&#xff0c;甚至完成很多其他事情。 默认情况下我们打开vim在命令模式下&#x…

vue学习第四天 v-on事件绑定

v-on绑定事件如下&#xff0c;点击按钮会弹出alert v-on&#xff1a;事件具体名称“事件调用的函数名” 事件调用的函数写在methods里面 在methods属性的函数里&#xff0c;可以用this获取data的数据&#xff0c;this代表的就是整个vue实例 用this.age就可以拿到age&#xff0…

2.8寒假作业

web&#xff1a;[HNCTF 2022 Week1]2048 之前也做过类似的题目&#xff0c;之前的解法是直接get传参score20000&#xff0c;可以尝试 打开环境看源代码&#xff0c;直接改源代码显然是不行的&#xff0c;那么用一下上面的办法也不行&#xff0c;估计是要改其他方向的&#xff…

Formality:时序变换(五)(寄存器复制)

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 一、引言 时序变换在Design Compiler的首次综合和增量综合中都可能发生&#xff0c;它们包括&#xff1a;时钟门控(Clock Gating)、寄存器合并(Register Merging)、…