[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)

2021年11月21日 阅读数:9
这篇文章主要向大家介绍[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于),主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------html

       在前一篇文章《Unity3D游戏开发之Lua与游戏的不解java

之缘(上)》中,博主带领你们初步探索了Lua语言与游戏开发领域之间的紧密联系,今天让咱们来继续将Lua语言进行究竟吧!经过前面的学习,咱们知道设计Lua语言的目的是为了将Lua嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua语言自己没有像其余语言提供丰富的类库,所以Lua语言必须依赖于其余语言来完毕功能上的扩展(可是正是在功能上牺牲才换来了Lua精简而稳定的核心)。假设咱们要深刻了解Lua语言的话,就必需要了解Lua语言与其余语言的交互接口,因为这将是咱们使用Lua语言的基础。那么。今天就让博主来带领你们一块儿学习Lua语言与其余语言的交互吧!ios


      1、Lua堆栈git

    假设咱们想要理解Lua语言与其余语言交互的实质,咱们首先就要理解Lua堆栈。github

简单来讲。Lua语言之因此能和C/C++进行交互。主要是因为存在这样一个无处不在的虚拟栈。c#

栈的特色是先进后出,在Lua语言中,Lua堆栈是一种索引可以是正数或者负数的结构,并规定正数1永远表示栈底。负数-1永远表示栈顶。数据结构

换句话说呢,在不知道栈大小的状况下。咱们可以经过索引-1取得栈底元素、经过索引1取得栈顶元素。如下呢,咱们经过一个实例来加深咱们对于这段话的理解:编辑器

#include <iostream>

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

using namespace std;

int main()
{
//建立Lua环境
lua_State* L=lua_open();
//打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
//luaopen_os、luaopen_string、luaopen_math、luaopen_debug
luaL_openlibs(L);
//压入一个数字20
lua_pushnumber(L,20);
//压入一个数字15
lua_pushnumber(L,15);
//压入一个字符串Lua
lua_pushstring(L,"Lua");
//压入一个字符串C
lua_pushstring(L,"C");
//获取栈元素个数
int n=lua_gettop(L);
//遍历栈中每个元素
for(int i=1;i<=n;i++)
{
cout << lua_tostring(L ,i) << endl;
}
return 0;
}


在上面的这段代码中,咱们可以可以看到咱们首先建立了一个lua_State类型的变量L,咱们可以将它理解成一个Lua运行环境的上下文(Context),这里咱们在Lua堆栈中压入了四个元素:20、1五、"Lua"、"C"而后将其输出,假设你们理解了Lua堆栈中的索引,那么终于输出的结果应该是:20、1五、"Lua"、"C"。因为索引1始终指向栈底,最早入栈的元素会处于栈底。ide

所以当咱们依照递增的索引顺序来输出栈中的元素的话,其实是自下而上输出,这样咱们就能获得这种结果了。函数

       好了。假设这段代码没有什么问题的话。接下来咱们来解说Lua为C/C++提供的接口,它们均被定义在lua.h文件里。Lua提供的C/C++接口大部分与栈操做有关,所以深刻理解Lua堆栈是学习Lua语言的重点和难点。经过数据结构的知识,咱们可以知道栈有出栈和入栈两种基本操做,Lua提供的C API中入栈可以经过push系列的方法来实现。例如如下图所看到的:

[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)_c#

而出栈或者说查询的方法则可以经过to系列的方法来实现,例如如下图:

[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)_入栈_02

这两部分是学习Lua语言必定要去了解的内容,因为之后假设需要咱们将Lua整合到其余项目中这些内容,这些东西可以说是原理性、核心性的东西。

好了,如下咱们利用这里的API对一个演示样例代码进行改造,这里添加了对栈中元素类型的推断:

#include <iostream>

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

using namespace std;

int main()
{
//建立Lua环境
lua_State* L=lua_open();
//打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
//luaopen_os、luaopen_string、luaopen_math、luaopen_debug
luaL_openlibs(L);
//压入一个数字20
lua_pushnumber(L,20);
//压入一个字符串15
lua_pushnumber(L,15);
//压入一个字符串Lua
lua_pushstring(L,"Lua");
//压入一个字符串C
lua_pushstring(L,"C");
//获取栈中元素个数
int n=lua_gettop(L);
//遍历栈中每个元素
for(int i=1;i<=n;i++)
{
//类型推断
switch(lua_type(L,i))
{
case LUA_TSTRING:
cout << "This value's type is string" << endl;
break;
case LUA_TNUMBER:
cout << "This value's type is number" << endl;
break;
}
//输出值
cout << lua_tostring(L ,i) << endl;
}

//释放Lua
lua_close(L);
}


    2、Lua与C++交互

   Lua与C++的交互从宿主语言的选择划分上可以分为C++调用Lua和Lua调用C++两中类型:

   一、C++调用Lua

    使用C++调用Lua时咱们可以直接利用C++中的Lua环境来直接Lua脚本,好比咱们在外部定义了一个lua脚本文件。咱们现在需要使用C++来訪问这个脚本该怎么作呢?在这里咱们可以使用luaL_loadfile()、luaL_dofile()这两个方法个方法来实现。其差异是前者仅载入脚本文件然后者会在载入的同一时候调用脚本文件。咱们一块儿来看如下的代码:

#include <iostream>

using namespace std;

#include <iostream>

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

using namespace std;

int main()
{
//建立Lua环境
lua_State* L=luaL_newstate();
//打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
//luaopen_os、luaopen_string、luaopen_math、luaopen_debug
luaL_openlibs(L);

//如下的代码可以用luaL_dofile()来取代
//载入Lua脚本
luaL_loadfile(L,"script.lua");
//运行Lua脚本
lua_pcall(L,0,0,0);

//将变量arg1压入栈顶
lua_getglobal(L,"arg1");
//将变量arg2压入栈顶
lua_getglobal(L,"arg2");

//读取arg一、arg2的值
int arg1=lua_tonumber(L,-1);
int arg2=lua_tonumber(L,-2);

//输出Lua脚本中的两个变量
cout <<"arg1="<<arg1<<endl;
cout <<"arg2="<<arg2<<endl;

//将函数printf压入栈顶
lua_getglobal(L,"printf");
//调用printf()方法
lua_pcall(L,0,0,0);

//将函数sum压入栈顶
lua_getglobal(L,"sum");
//传入參数
lua_pushinteger(L,15);
lua_pushinteger(L,25);
//调用printf()方法
lua_pcall(L,2,1,0);//这里有2个參数、1个返回值
//输出求和结果
cout <<"sum="<<lua_tonumber(L,-1)<<endl;

//将表table压入栈顶
lua_getglobal(L,"table");
//获取表
lua_gettable(L,-1);
//输出表中第一个元素
cout <<"table.a="<<lua_tonumber(L,-2)<<endl;

}

在这段代码中咱们调用了一个外部的文件script.lua。这是一个Lua脚本文件,在调试阶段,咱们需要将其放置在和C++项目源文件同级的文件夹下,而在正式运行阶段,咱们仅仅需要将其和终于的可运行文件放在同一个文件夹下就行了。如下是脚本代码:

--在Lua中定义两个变量
arg1=15
arg2=20

--在Lua中定义一个表
table=
{
a=25,
b=30
}

--在Lua中定义一个求和的方法
function sum(a,b)
return a+b
end

--在Lua中定义一个输出的方法
function printf()
print("This is a function declared in Lua")
end

咱们注意到在脚本文件里咱们定义了一些变量和方法,在C++代码中咱们首先用lua_getglobal()方法来说Lua脚本中的变量或函数压入栈顶,这样咱们就可以使用相关的to系列方法去获取它们,因为每次运行lua_getglobal()都是在栈顶。因为咱们使用索引值-1来获取栈顶的元素。C++可以调用Lua中的方法,第一步和普通的变量一样。是将Lua中定义的方法压入栈顶。因为仅仅有压入栈中。咱们才干够使用这种方法,接下来,咱们需要经过push系列的方法为栈中的方法传入參数,在完毕參数传入后,咱们可以使用一个lua_pcall()的方法来运行栈中的方法,它有四个參数。第一个參数是Lua环境状态Lua_State。第二个參数是要传入的參数个数,第三个參数是要返回的值的数目。第四个參数通常默认为0。

因为Lua支持返回多个结果。所以,咱们可以充分利用Lua的这一特色来返回多个值。

运行该方法后。其结果会被压入栈顶,因此咱们可以索引值-1来获取函数的结果。假设函数有多个返回值。则依照函数中定义的return 顺序,依次入栈,索引值-1表明最后一个返回值。好了。这就是C++调用Lua的详细实现了。

     二、Lua调用C++

     首先咱们在C++中定义一个方法,该方法必须以Lua_State做为參数,返回值类型为int,表示要返回的值的数目。

static int AverageAndSum(lua_State *L)
{
//返回栈中元素的个数
int n = lua_gettop(L);
//存储各元素之和
double sum = 0;
for (int i = 1; i <= n; i++)
{
//參数类型处理
if (!lua_isnumber(L, i))
{
//传入错误信息
lua_pushstring(L, "Incorrect argument to 'average'");
lua_error(L);
}
sum += lua_tonumber(L, i);
}
//传入平均值
lua_pushnumber(L, sum / n);
//传入和
lua_pushnumber(L, sum);

//返回值的个数,这里为2
return 2;
}

接下来咱们在C++中使用lua_register()方法完毕对该方法的注冊

lua_register(L, "AverageAndSum", AverageAndSum);

这样咱们就可以在Lua环境中使用这种方法啦,前提是定义必须在运行代码以前完毕。咱们在Lua脚本文件下添加对该方法的调用:

--在Lua中调用C++中定义而且注冊的方法
average,sum=AverageAndSum(20,52,75,14)
print("Average=".average)
print("Sum=".sum)

假设咱们需要在C++中查看该方法调用的结果。那么这个在C++中调用Lua是同样的。

好了,C++和Lua的交互终于讲完了。被这块的代码纠结了好几天,这下总算是搞明确了。固然这仅仅是对原理的一种学习和理解啦。假设但愿更好的使用Lua调用C++,建议了解这几个项目:

​LuaPlus​​、​​LuaBind​​。这样相信你们对于C++中的方法如何在Lua中绑定会有更好的认识吧!



    3、Lua与C#交互

   既然咱们已经知道了C++是如何和Lua完毕交互的,理论上咱们可以经过编写dll的方式将前面完毕的工做继续在C#中运行,可是这样作咱们需要花费大量时间在三种语言之间纠结,因为这样会添加调试的难度。以前有个作coco2dx的朋友抱怨要在C++、Javascript、Lua之间来回跑,我当时没认为有什么,因为我最困难的时候就是C#和Java项目混合的情形,现在我算是深有体会了啊。这算是报应吗?哈哈。好了。不说这个了,好在C#与Lua的交互目方面前已经有了较好的解决方式。在开源社区咱们可以找到很是多的支持在C#中调用Lua的工具库,博主这里向你们推荐的是LuaInterface这个开源项目。这个开源项目我找到了两个地址:

​一、https://github.com/Jakosa/LuaInterface​

二、http://code.google.com/p/luainterface

博主我的感受这应该是同一个项目,因为两个项目的源码是同样的,只是从Github上下载的项目在使用的时候会报错。预计是我电脑里的Lua版本号和它项目里所用的Lua的版本号不一致形成的吧。如下的这个项目是可以使用的,博主这里写了一个简单的演示样例:

//------------------------------------------------------------------------------
// <summary>
// 这是一个用以演示LuaInterface的简单程序,经过LuaInterface咱们可以实现在C#与Lua的
// 的相互通讯。Lua是一个轻巧而高效的语言。它可以和不论什么语言混合使用。Lua语言最初并不是
// 为游戏开发而诞生,倒是因为游戏开发而成名。眼下。在世界上有大量的游戏使用了Lua做为它
// 的脚本语言。如图Unity使用了C#做为它的语言。Lua在游戏开发领域发挥着不可忽视的重要做
// 用。
使用LuaInterface的方法例如如下:
// 1.C#
// 注冊Lua中可调用方法:
//   mLua.RegisterFunction(Lua调用方法名, 类, 类.GetMethod(C#方法名));
//   注:C#不要用法级泛型。即 void Fun<T>(string str);,假设使用,系统本身主动断定T为第一个參数的类型。

// 载入Lua代码
// mLua.DoString(Lua代码);
//   mLua.DoFile(Lua文件绝对路径);
// 调用Lua方法
//   mLua.GetFunction(Lua方法).Call(參数);  注:此处參数不要传递dynamic类型的类。不然Lua中没法获取属性值
// 2.Lua
// 调用C#方法时需要先注冊注冊后依照Lua方法处理
// </summary>
//------------------------------------------------------------------------------
using System;
using LuaInterface;
namespace LuaExample
{
public class LuaScript
{
//定义LuaFile属性以便于从外部调用一个Lua脚本
private string mLuaFile;
public string LuaFile {
get {
return mLuaFile;
}
set {
mLuaFile = value;
}
}

//Lua虚拟机
private Lua mLua;

//构造函数
public LuaScript ()
{
//初始化Lua虚拟机
mLua=new Lua();
//注冊Printf方法
mLua.RegisterFunction("Printf",this,this.GetType().GetMethod("Printf"));
}

//定义一个C#方法供Lua使用
public void Printf(string str)
{
Console.WriteLine("This Method is Invoked by Lua:" + str);
}

//在C#中调用Lua方法
public void DoFile()
{
if(mLuaFile!="")
//运行Lua脚本中的代码
mLua.DoFile(mLuaFile);
}

//在C#中调用Lau方法
public void DoString()
{
//以字符串形式定义的Lua脚本
string mFuncString="function Add(a,b) io.write(a+b) end";
//在Lua中定义该方法
mLua.DoString(mFuncString);
//调用该方法
mLua.GetFunction("Add").Call(4,8);
}

//在Lua中调用C#脚本
public void Invoke()
{
//调用注冊的Printf方法
mLua.GetFunction("Printf").Call("Hello Lua");
}
}
}

接下来咱们编写一个主类来调用这个类:

using System;
using LuaInterface;

namespace LuaExample
{
class MainClass
{
public static void Main (string[] args)
{
//实例化LuaSxript
LuaScript mLua=new LuaScript();
//设置LuaFile
mLua.LuaFile="D:\\test.lua";
//调用字符串中定义的Lua方法
mLua.DoString();
//为美观考虑添加一个空行
Console.WriteLine();
//运行Lua文件里定义的脚本
mLua.DoFile();
//调用C#中定义的方法
mLua.Invoke();
}
}
}

好了。C#与Lua的交互攻克了,不少其余的内容期待着你们自行到该项目源码中去寻找。好了,先这样吧!


    4、Lua与Java交互

    和C#类似的一点是在Java中咱们可以使用JNI来调用C++代码。所以理论上Lua和Java应该是可以经过JNI来交互的。这块博主眼下没有展开研究。这里仅仅给你们推荐如下工具库:

​一、LuaJava​

​二、luaj​


    5、结语

    好吧。好了。好几天的时间来研究Lua语言的API,总算感受是收获多一点吧。因为C++方面研究的东西不是很是多,因此像编译C++项目、配置C++环境、引用C++库和头文件这些问题曾经都不大会,此次竟然一会儿都学会了,博主推荐你们使用CodeBlocks这个C/C++开发环境,它内置的gcc编译器我认为还不错啦,而且它跨平台啊,之后工做了说不定会在Linux和Mac下作开发,选择一个跨平台的编辑器或者是IDE,对于咱们来讲何尝不是一件好事啊。因为学习新东西老是要花必定成本的。好了,今天的内容就是这样啦,但愿你们喜欢啊,嘻嘻。忽然认为这篇文章好长啊。


每日箴言:别总因为迁就别人就委屈本身。这个世界没几我的值得你总弯腰。弯腰的时间久了。仅仅会让人习惯于你的低姿态。你的不重要。



[Unity3D]Unity3D游戏开发Lua随着游戏的债券(于)_c#_03


----------------------------------------------------------------------------------------------------------------------



版权声明:本文博主原创文章。博客,未经赞成不得转载。转载请注明做者和出处,谢谢。