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

喜欢我的博客请记住我的名字:秦元培。我的博客地址是blog.csdn.net/qinyuanpei。

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39910099

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

大家好,我是秦元培,欢迎大家关注我的博客。我的博客地址是blog.csdn.net/qinyuanpei。在前一篇文章《Unity3D游戏开发之Lua与游戏的不解

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

一、Lua堆栈

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

简单来说。Lua语言之所以能和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_debugluaL_openlibs(L);//压入一个数字20lua_pushnumber(L,20);//压入一个数字15lua_pushnumber(L,15);//压入一个字符串Lualua_pushstring(L,"Lua");//压入一个字符串Clua_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、15、"Lua"、"C"然后将其输出,假设大家理解了Lua堆栈中的索引,那么终于输出的结果应该是:20、15、"Lua"、"C"。由于索引1始终指向栈底,最先入栈的元素会处于栈底。

因此当我们依照递增的索引顺序来输出栈中的元素的话,实际上是自下而上输出,这样我们就能得到这种结果了。

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

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

这两部分是学习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_debugluaL_openlibs(L);//压入一个数字20lua_pushnumber(L,20);//压入一个字符串15lua_pushnumber(L,15);//压入一个字符串Lualua_pushstring(L,"Lua");//压入一个字符串Clua_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;}//释放Lualua_close(L);
}

二、Lua与C++交互

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

1、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_debugluaL_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");//读取arg1、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的详细实现了。

2、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);//返回值的个数,这里为2return 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中绑定会有更好的认识吧!

三、Lua与C#交互

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

1、https://github.com/Jakosa/LuaInterface

2、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){//实例化LuaSxriptLuaScript mLua=new LuaScript();//设置LuaFilemLua.LuaFile="D:\\test.lua";//调用字符串中定义的Lua方法mLua.DoString();//为美观考虑添加一个空行Console.WriteLine();//运行Lua文件里定义的脚本mLua.DoFile();//调用C#中定义的方法mLua.Invoke();}}
}

好了。C#与Lua的交互攻克了,很多其他的内容期待着大家自行到该项目源码中去寻找。好了,先这样吧!

四、Lua与Java交互

和C#相似的一点是在Java中我们能够使用JNI来调用C++代码。因此理论上Lua和Java应该是能够通过JNI来交互的。这块博主眼下没有展开研究。这里仅仅给大家推荐以下工具库:

1、LuaJava

2、luaj

五、结语

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

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

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

喜欢我的博客请记住我的名字:秦元培。我的博客地址是blog.csdn.net/qinyuanpei。

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39910099

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

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

转载于:https://www.cnblogs.com/yxwkf/p/4843763.html

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

  1. [Unity3D]Unity3D游戏开发Lua随着游戏的债券(在)

    ---------------------------------------------------------------------------------------------------- ...

  2. libGDX游戏开发之打包游戏(十二)

    libGDX游戏开发之打包游戏(十二) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和goog ...

  3. libGDX游戏开发之修改游戏帧数FPS(十三)

    libGDX游戏开发之修改游戏帧数FPS(十三) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网 ...

  4. 基于cocos2d-x的快速的游戏开发--回合制游戏

    2019独角兽企业重金招聘Python工程师标准>>> #基于cocos2d-x的快速的游戏开发--回合制游戏 开发时间:3天 开发工具:cocos2d-x和cocostudio 开 ...

  5. 微信小游戏开发教程-2D游戏原理讲解

    微信小游戏开发教程-2D游戏原理讲解 原理 为了更加形象的描述,这里先上一张图: 背景 a. 首先,我们看到背景好像是一张无限长的图片在向下移动.实际则不然,这是一张顶部和底部刚好重叠的图片.这是一种 ...

  6. Unity 2D游戏开发教程之游戏中精灵的跳跃状态

    Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...

  7. 【源码+教程】Java课设项目_12款最热最新Java游戏项目_Java游戏开发_Java小游戏_飞翔的小鸟_王者荣耀_超级玛丽_推箱子_黄金矿工_贪吃蛇

    马上就要期末了,同学们课设做的如何了呢?本篇为大家带来了12款热门Java小游戏项目的源码和教程,助力大家顺利迎接暑假![源码+教程]Java课设项目_12款最热最新Java游戏项目_Java游戏开发 ...

  8. 小游戏开发怎么选游戏引擎

    前言 小游戏开发怎么选游戏引擎 微信小游戏现在非常的火,当我们下定决心做微信小游戏开发的时候, 面临我们的第一个问题是怎么选一个H5的游戏引擎, 那么今天本尊就来给大家分析一下目前能开发各大平台H5小 ...

  9. H5游戏开发包括哪些游戏类型

    H5在微信还没有诞生的时候,就已经在各大手机应用方面展露头角.最早的H5小游戏,例如4399公司开发的,开始了新一轮的热潮.随着微信的推出,很多H5游戏开始嵌入到微信公众号.微信群.朋友圈. H5因其 ...

最新文章

  1. mysql+dump+选项_mysqldump 备份常用选项以及备份脚本
  2. 线段树区间扫描线超详解,一篇文章搞懂扫描线
  3. Windows恶意软件API调用特征分析
  4. 皮一皮:20岁和30岁的你的区别...
  5. ssh secure shell
  6. 青春环游记 | 雪中悍刀行之卡尔曼的鱼【回顾 2021,展望 2022】
  7. GRUB 启动 WIN PE 镜像(ISO)
  8. Change Fiori launchpad logo
  9. linux磁盘冗余阵列
  10. 前端学习(3315):UI提取
  11. 嵌入式操作系统内核原理和开发
  12. dojo调用php,dojo学习第一天 Tab选项卡 实现_dojo
  13. TiDB 源码阅读系列文章(九)Hash Join
  14. Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解
  15. php实现微信小程序登录(根据微信小程序接口更新最新教程)
  16. 华为二层网络隔离与互通(vlan隔离与端口隔离)
  17. QQ空间迁移_【小米摄像头跨局域网NAS存储】
  18. 给大家分享一个QQ资料查询接口(等级,活跃,年龄,性别,身份卡)
  19. 大连计算机学校附近的逸林,逸林街附近学校
  20. 软件测试基础篇(1)

热门文章

  1. 图表达相关书书籍调研
  2. 操作系统 真象还原 读书笔记
  3. NeHe OpenGL课程 网址整理
  4. 机器学习(三十五)——Actor-Critic, Integrating Learning and Planning(1)
  5. 深度学习(三十五)——Style Transfer(2), YOLOv3, Tiny-YOLO, One-stage vs. Two-stage
  6. 机器学习(十五)——loss function比较、独立成分分析
  7. idea 关于高亮显示与选中字符串相同的内容
  8. linux快速复制大量小文件方法
  9. Chrome 或将于2018年正式弃用 HPKP 公钥固定标准
  10. 在windows中手动安装第三方模块