在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理

在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理,第1张

概述点击打开链接 前言:为什么要用Lua 首先要说,所有编程语言里面,我最喜欢的还是C#,VisualStudio+C#,只能说太舒服了。所以说,为什么非要在Unity里面用Lua呢?可能主要是闲的蛋疼。。。。。另外还有一些次要原因: 方便做功能的热更新; Lua语言的深度和广度都不大,易学易用,可以降低项目成本。 C#与Lua互相调用的方案 坦白来将,我并没有对现在C#与Lua互相调用的所有库进行一 点击打开链接
前言:为什么要用Lua 首先要说,所有编程语言里面,我最喜欢的还是C#,VisualStudio+C#,只能说太舒服了。所以说,为什么非要在Unity里面用Lua呢?可能主要是闲的蛋疼。。。。。另外还有一些次要原因: 方便做功能的热更新; Lua语言的深度和广度都不大,易学易用,可以降低项目成本。 C#与Lua互相调用的方案 坦白来将,我并没有对现在C#与Lua互相调用的所有库进行一个仔细的调研,大概搜了一下,找到这样几个: slua:https://github.com/pangweiwei/slua Nlua:http://nlua.org/ UniLua:https://github.com/xebecnan/UniLua ulua插件 以上这些方案的具体内容,不是本文的重点,这里就不说了,感兴趣的同学,点开自己去看就行了。
最后我选用了ulua,主要原因是:ulua方案比较成熟,它并没有太多自己的代码,主要是把LuaInterface和Lua解释器整合了一下,都是比较成熟的代码,相对会稳定一些。另外,个人很欣赏LuaInterface这个库。接下来我们就看一下ulua。:) ulua的基本使用 ulua插件的使用非常简单,基本上看一下他自带的几个例子就明白了。
游戏逻辑粘合层设计 ulua插件解决了语言层面的问题:C#与LUA两种语言代码互相调用,以及参数传递等相关的一系列底层问题。而我们游戏逻辑开发中,到底如何使用LUA是上层的一个问题。下面给出我摸索的一个方案,个人认为:够简单,够清晰,是很薄很薄的一层,不可能更薄了。

使用几个LuaState? 曾经看过一个网友的方案,每次运行脚本就new一个LuaState,个人认为这种方案十分不妥。整个游戏的Lua代码应该运行在一个LuaState之上,原因有二:
运行在同一LuaState的Lua代码才能互相调用啊。相信一个游戏总会有一定的代码量的,如果不同的lua文件之中的代码,完全独立运行,不能互相调用或者互相调用很麻烦,则游戏逻辑组织平添很多障碍; 混合语言编程中原则之一就是:尽量减少代码执行的语言环境切换,因为这个的代价往往比代码字面上看上去要高很多。我的目标是:既然用了Lua,就尽量把UI事件响应等游戏上层逻辑放到Lua代码中编写。 基于以上原因,我觉得游戏的Lua代码全都跑在一个LuaState之上。这也是本文方案的基础。

实现LuaComponent 首先说一下我的目标:
既然C#对于Unity来说是脚本层了,那么Lua应该和C#脚本代码具有相同的逻辑地位; Lua整合的代码应该很少,应尽量保持简单; 基于以上的目标,我实现了LuaComponet类,它的实现类似MonoBehavior,只不过我们没有C++源代码,只能由C#层的MonoBehavior来转发一下调用。这样,我们的Lua代码的实现方式就是写和写一个C#脚本组件完全一致了,可以说达到了和引擎天衣无缝的整合。:)OK,先上代码!
[csharp]  view plain copy

using UnityEngine;   using System.Collections;   using LuaInterface;      /// <summary>   /// Lua组件 - 它调用的Lua脚本可以实现类似MonoBehavIoUr派生类的功能   /// </summary>   [AddComponentMenu("Lua/LuaComponent")]   public class LuaComponent : MonoBehavIoUr   {       private static LuaState s_luaState; // 全局的Lua虚拟机       [tooltip("绑定的LUA脚本路径")]       public TextAsset m_luaScript;      public Luatable LuaModule       {           get;           set;       }       LuaFunction m_luaUpdate;    // Lua实现的Update函数,可能为null            /// 找到游戏对象上绑定的LUA组件(Module对象)   static Luatable GetLuaComponent(GameObject go)           LuaComponent luaComp = go.GetComponent<luacomponent>();   if (luaComp == null)               return null;   return luaComp.LuaModule;   /// <summary>   /// 向一个GameObject添加一个LUA组件   /// </summary>   static Luatable AddLuaComponent(GameObject go, TextAsset luafile)       {           LuaComponent luaComp = go.AddComponent<luacomponent>();           luaComp.Initilize(luafile);  // 手动调用脚本运行,以取得Luatable返回值   /// 提供给外部手动执行LUA脚本的接口   voID Initilize(TextAsset luafile)           m_luaScript = luafile;           RunLuafile(luafile);           //-- 取得常用的函数回调   if (this.LuaModule !=          {               m_luaUpdate = this.LuaModule["Update"as LuaFunction;           }       }   /// 调用Lua虚拟机,执行一个脚本文件   voID RunLuafile(TextAsset luafile)   if (luafile == null || string.IsNullOrEmpty(luafile.text))               return;   if (s_luaState ==              s_luaState = new LuaState();   object[] luaRet = s_luaState.DoString(luafile.text, luafile.name, null);   if (luaRet != null && luaRet.Length >= 1)               // 约定:第一个返回的table对象作为Lua模块   this.LuaModule = luaRet[0] as Luatable;           }   else           {               DeBUG.LogError("Lua脚本没有返回table对象:" + luafile.name);   // MonoBehavIoUr callback   voID Awake()           RunLuafile(m_luaScript);           CallLuaFunction("Awake",153); Font-weight:bold; background-color:inherit">this.LuaModule,153); Font-weight:bold; background-color:inherit">this.gameObject);   // MonoBehavIoUr callback   voID Start()           CallLuaFunction("Start",153); Font-weight:bold; background-color:inherit">voID Update()   if (m_luaUpdate != null)               m_luaUpdate.Call(this.gameObject);   /// 调用一个Lua组件中的函数   voID CallLuaFunction(string funcname,153); Font-weight:bold; background-color:inherit">params object[] args)   this.LuaModule == return;           LuaFunction func = this.LuaModule[funcname] as LuaFunction;   if (func !=              func.Call(args);   }   </luacomponent></luacomponent>  
这段代码非常简单,实现以下几个功能点: 管理一个全局的LuaState; 负责将MonoBehavior的调用转发到相应的LUA函数; 提供了GetComponent()、AddComponent()对应的LUA脚本版本接口;这点非常重要。 LUA代码约定 为了很好的和LuaComponent协作,Lua脚本需要遵循一些约定: LUA脚本应该返回一个table,可以是LUA的Module,也可以是任何的table对象; 返回的table对象应该含有MonoBehavIoUr相应的回调函数; 例如: [plain] 

require "EngineMain"   local demoComponent = {}   function demoComponent:Awake( gameObject )       DeBUG.Log(gameObject.name.."Awake")   end   return demoComponent   LuaComponent回调函数中,主动将GameObject对象作为参数传递给Lua层,以方便其进行相应的处理。
Lua组件之间的互相调用(在Lua代码中)

基于以上结构,就很容易实现Lua组件之间的互相调用。在Demo工程中,有一个“Sphere”对象,绑定了如下脚本:

[plain]  view plain copy

require "EngineMain"      local sphereComponent = {}   sphereComponent.text = "Hello World"   function sphereComponent:Awake( gameObject )       DeBUG.Log(gameObject.name.."Awake")   end   return sphereComponent   还有另外一个“Cube”对象,绑定了如下脚本,用来演示调用上面这个Lua组件的成员:

local demoComponent = {}   function demoComponent:Awake( gameObject )   function demoComponent:Start( gameObject )       DeBUG.Log(gameObject.name.."Start")          --演示LuaComponent代码互相调用       local sphereGO = GameObject.Find("Sphere")       local sphereLuaComp = LuaComponent.GetLuaComponent(sphereGO)       DeBUG.log("Sphere.LuaDemoB:"..sphereLuaComp.text)   return demoComponent  

完整版DEMO下载地址:

百度网盘链接: http://pan.baidu.com/s/1nt1eGPV 密码: 3g7b


最后,顺带总结一下:在设计上次游戏逻辑框架时,比较好的思路是:在透彻的理解Unity自身架构的前提下,在其架构下进行下一层设计,而不是想一种新的框架。因为Unity本身就是一个框架。 总结

以上是内存溢出为你收集整理的在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理全部内容,希望文章能够帮你解决在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1246461.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存