Unity项目接入xLua的一种流程

news/2025/2/9 8:08:19 标签: unity, 游戏引擎, lua

lua_0">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.niftyadmin.cn/n/5845806.html

相关文章

MariaDB *MaxScale*实现mysql8读写分离

1.MaxScale 是干什么的&#xff1f; MaxScale是maridb开发的一个mysql数据中间件&#xff0c;其配置简单&#xff0c;能够实现读写分离&#xff0c;并且可以根据主从状态实现写库的自动切换&#xff0c;对多个从服务器能实现负载均衡。 2.MaxScale 实验环境 中间件192.168.12…

使用opencv解析视频,通过图片比对,筛选出每一帧视频的变化

记录瞬间 最近碰到一个问题&#xff0c;在客户端上操作时&#xff0c;存在背景判断的情况&#xff0c;对自动化实现此操作增加难度。 所以考虑到实际的使用&#xff0c;将一些计算机视觉技术加入到实际的使用中&#xff0c;来解决此问题。 import os import cv2 import numpy#…

车机音频参数下发流程

比如以audioControlWrapper.setParametersToAmp(keyPairValues); 下发banlance为例&#xff0c;链路如下 hal层 1. AudioControl.cpp hardware\interfaces\automotive\audiocontrol\aidl\default\AudioControl.cpp ndk::ScopedAStatus AudioControl::setParametersToAmp(co…

算法与数据结构(字符串相乘)

题目 思路 这道题我们可以使用竖式乘法&#xff0c;从右往左遍历每个乘数&#xff0c;将其相乘&#xff0c;并且把乘完的数记录在nums数组中&#xff0c;然后再进行进位运算&#xff0c;将同一列的数进行相加&#xff0c;进位。 解题过程 首先求出两个数组的长度&#xff0c;…

【读书笔记·VLSI电路设计方法解密】问题46:什么是bug覆盖率

在IC设计项目的验证过程中&#xff0c;功能测试&#xff08;通过使用测试平台&#xff09;有助于定位设计错误或漏洞。这个验证过程有三个阶段&#xff1a;构建和启动测试平台、验证基本测试用例以及验证边界情况。 在前两个阶段&#xff0c;漏洞很容易被检测到&#xff0c;因…

Python实现GO鹅优化算法优化支持向量机SVM回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 在当今数据驱动的世界中&#xff0c;机器学习技术被广泛应用于各种领域&#xff0c;如金融、医疗、…

开发一款类似《王者荣耀》的游戏是一个复杂的系统工程,涉及多个领域的知识和技术。以下是从多个角度详细阐述如何开发的思维。

一、明确游戏定位与核心玩法 游戏类型 MOBA&#xff08;Multiplayer Online Battle Arena&#xff09;&#xff1a;强调团队合作、策略性和即时战斗。确定游戏模式&#xff08;如5v5、3v3等&#xff09;和地图设计。 核心玩法 角色设计&#xff1a;英雄技能、属性、成长曲线。…

Spring Boot 3.4 中 MockMvcTester 的新特性解析

引言 在 Spring Boot 3.4 版本中&#xff0c;引入了一个全新的 MockMvcTester 类&#xff0c;使 MockMvc 测试可以直接支持 AssertJ 断言。本文将深入探讨这一新特性&#xff0c;分析它如何优化 MockMvc 测试并提升测试的可读性。 Spring MVC 示例 为了演示 MockMvcTester 的…