写点什么

元宇宙链游系统开发搭建技术

作者:Congge420
  • 2023-05-19
    广东
  • 本文字数:2836 字

    阅读完需:约 9 分钟

META FORCE 系统属于我们的社区,开发需求及分析:Congge420 并且是完全的去中心化,这意味着它是透明的,安全的,技术 Congge420 整理发布,并且能够抵抗外部影响。

三个步骤:

 

  加载 lua 代码到 vm 中,对应 api-luaL_loadbuffer

 

  luaL_loadbuffer 会同时在栈上压入代码块的指针

 

  执行 lua 代码,对应 api-lua_pcall

 

  lua_pcall 会从栈上依次弹出{nargs}个数据作为函数参数,再弹出函数进行执行,并将结果压入栈

 

  如果 lua 代码有返回值,那么通过 lua_toXXX 相关 api 从栈上获取结果

 

  完整的代码如下:

 

  private bool DoLuaCode(System.IntPtr L,string luaCode){

  //加载 lua 代码

 

  if(Lua.luaL_loadbuffer(L,luaCode,"")==0){

  //执行栈顶的函数

 

  if(Lua.lua_pcall(L,0,1,0)==0){

  //函数执行完成后,返回值会依次依次押入栈

 

  return true;

 

  }else{关于区块链技术开发唯:Congge420

  Debug.LogError("pcall failed!");

 

  return false;

 

  }

 

  }else{

  Debug.LogError("load buffer failed");

 

  return false;

 

  }

 

  }

 

  假如我们有一段 lua 代码:

 

  return'hello,i am from lua'

 

  这段 lua 仅仅返回一段字符串,那么利用 DoLuaCode 去执行就是:

 

  //lua 代码

 

  string luaCode="return'hello,i am from lua'";

 

  if(DoLuaCode(L,luaCode)){

  Debug.Log(Lua.lua_tostring(L,-1));

 

  //lua_toXXX 不会出栈,需要 lua_pop 才能出栈

 

  Lua.lua_pop(L,1);

 

  }

 

  由于此处 lua 代码返回的是字符串,因此使用 lua_tostring(L,-1)来将栈顶的元素转为字符串并返回,相应的我们还能看到有 lua_tonumber,lua_toboolean 等等.

 

  4.c#调用 lua 全局函数

 

  接下来的例子将说明一下 c#端如何执行 lua 中的全局函数。

 

  假设现在我们有一段 lua 代码如下:

 

  function addSub(a,b)

 

  return a+b,a-b;

 

  end

 

  通过 DoLuaCode 来运行以上的 lua 代码,就得到了一个全局的 addSub 函数,这个函数会返回 a,b 相加和相减的结果。

 

  为了在 c#端执行以上的 lua 函数,需要按以下步骤进行:

 

  将全局函数压入栈中,对应 api-lua_getglobal

 

  将函数所需的参数依次压入栈中,对应 api-lua_pushnumber

 

  执行栈中函数,对应 api-lua_pcall

 

  获取函数返回结果,对应 api-lua_tonumber

 

  完整 c#代码如下:

 

  //从全局表里读取 addSub 函数,并压入栈

 

  Lua.lua_getglobal(L,"addSub");

 

  //压入参数 a

 

  Lua.lua_pushnumber(L,101);

 

  //压入参数 b

 

  Lua.lua_pushnumber(L,202);

 

  //2 个参数,2 个返回值

 

  Lua.lua_pcall(L,2,2,0);

 

  //pcall 会让参数和函数指针都出栈

 

  //pcall 执行完毕后,会将结果压入栈

 

  Debug.Log(Lua.lua_tonumber(L,-2));

 

  Debug.Log(Lua.lua_tonumber(L,-1));

 

  Lua.lua_pop(L,2);

 

  5.lua 注册并调用 c#静态函数

 

  首先,想要被 Lua 调用的 c#函数,都必须满足以下的格式:

 

  public delegate int LuaCSFunction(System.IntPtr luaState);

 

  同时需要加上特性:

 

  MonoPInvokeCallback(typeof(LuaCSFunction))

 

  我们可以通过以下方式,将一个 LuaCSFunction 注册到 lua 中:

 

  static void RegisterCSFunctionGlobal(System.IntPtr L,string funcName,LuaCSFunction func){

  //将 LuaCSFunction 压入栈中

 

  Lua.lua_pushcfunction(L,func);

 

  //lua_setglobal 会弹出栈顶元素,并按给定的名字作为 key 将其加入到全局表

 

  Lua.lua_setglobal(L,funcName);

 

  }

 

  那么,当我们在 lua 中执行 c#注册的函数时,其交互过程如下:

 

  LuaVM 会临时分配一个局部栈结构(这里要区分开始通过 luaL_newstate 创建的全局栈,两者是独立的)

 

  LuaVM 会将 lua 侧的函数参数压入这个临时栈,然后将栈指针传给 LuaCSFunction

 

  LuaCSFunction 在实现上需要从这个栈中读取 lua 侧压入的参数,然后执行真正的相关逻辑,并将最终结果压入栈中

 

  LuaCSFunction 需要返回一个 int 值,表示往栈中压入了多少个返回值

 

  Lua 从栈中获取 C#侧压入的 0/1/多个返回值

 

  官方说明文档可以参考-Calling C from Lua

 

  接下来要将演示如何将一个 c#静态函数 Print 注入到 lua 中,实现 lua 中调用 c#端的日志输出功能。

 

  我们定义一个 c#静态函数如下:

 

  [MonoPInvokeCallback(typeof(LuaCSFunction))]

 

  private static int Print(System.IntPtr localL){

  //获取栈中元素个数

 

  var count=Lua.lua_gettop(localL);

 

  System.Text.StringBuilder s=new System.Text.StringBuilder();

 

  for(var i=1;i<=count;i++){

  //依次读取 print 的每个参数,合并成一个 string

 

  s.Append(Lua.lua_tostring(localL,i));

 

  s.Append('');

 

  }

 

  Debug.Log(s);

 

  //print 函数没有返回值

 

  return 0;

 

  }

 

  lua_gettop 可以获取栈中的元素个数,此处代表了 lua 端压入栈中的函数参数个数

 

  然后我们通过以下方式将这个 c#侧的 Print 注册到 lua 中,命名为 print。

 

  //将 LuaCSFunction 压入栈中

 

  Lua.lua_pushcfunction(L,Print);

 

  //lua_setglobal 会弹出栈顶元素,并按给定的名字作为 key 将其加入到全局表

 

  Lua.lua_setglobal(L,"print");

 

  接下来我们执行以下的 lua 代码:

 

  print('hello','csharp')

 

  就能看到编辑器中输出

 

  hello csharp

 

  6.lua 注册 c#类型

 

  通常我们使用 lua 中的 table 来模拟 c#中的类。一般类的注册思路如下:

 

  在 lua 中创建一个与 c#类同名的表

 

  将 c#类的静态函数都注册到 lua 的这个同名表里

 

  下面演示一下如何将 Unity 中的 Debug 类注册到 lua 中:

 

  Lua.lua_createtable(L,0,1);

 

  Lua.lua_setglobal(L,"Debug");

 

  其实很简单:

 

  lua_createtable 会创建一个 table,压入栈顶

 

  lua_setglobal 会弹出栈顶元素,并将其加到全局表里

 

  这样我们在 lua 里就有了一个名为 Debug 的表可供全局访问。但目前这个表是空空如也的,我们还需要为其添加静态函数。(tips:实际上完整的设计中,还需要为 class table 设置 metatable,增加一些限制性,但这里先不表)

 

  6.1 注入类的静态函数

 

  首先我们定义一个符合 LuaCSFunction 形式的 c#函数如下:

 

  [MonoPInvokeCallback(typeof(LuaCSFunction))]

 

  private static int Debug_Log(System.IntPtr L){

  string msg=Lua.lua_tostring(L,1);

 

  Debug.Log(msg);

 

  return 0;

 

  }

 

  这个 c#函数是对 Debug.Log 的一个封装。

 

  然后可以通过以下方式将这个 c#函数注册到 lua 中的 Debug 表中:

 

  Lua.lua_createtable(L,0,1);

 

  //往栈中压入字符串'Log'

 

  Lua.lua_pushstring(L,"Log");

 

  //往栈中压入函数 Debug_Log

 

  Lua.lua_pushcfunction(L,Debug_Log);

 

  //从栈中弹出一个元素作为 key,再弹出一个元素作为 value,作为 pair 赋值到 index 指定的 table

 

  Lua.lua_settable(L,1);

 

  Lua.lua_setglobal(L,"Debug");

 

用户头像

Congge420

关注

还未添加个人签名 2023-05-12 加入

还未添加个人简介

评论

发布
暂无评论
元宇宙链游系统开发搭建技术_区块链_Congge420_InfoQ写作社区