-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei。
转载请注明出处,本文作者:秦元培。 本文出处:http://blog.csdn.net/qinyuanpei/article/details/40050225
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
各位朋友。大家好。我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。在前两篇文章中,博主和大家一起学习了Lua在游戏开发领域中应用。今天我们继续来学习Lua语言在游戏开发中的应用。今天我们将视角转换到我们熟悉的Unity平台上来。那么我们为什么要将Lua语言引入Unity平台呢?这是我们今天要思考的第一个问题。传统的单机游戏通常以游戏光盘的形式出售给游戏玩家,玩家在购买游戏后无法获得很多其它的游戏内容,玩家仅仅能在一张容量有限的游戏光盘里不断地反复寻找着游戏的乐趣。毋庸置疑。这样的模式不利于游戏开发方为游戏添加新的内容。但是在互联网技术逐步成熟的今天。玩家在购买一款实体游戏后。通常能够通过购买DLC来体验更加丰富的游戏内容,而游戏制作方则能够通过DLC向玩家传达游戏正传中没有表达出来的内容。我们知道DLC是一般是指游戏的资料片,它是对游戏内容的一种补充。从技术层面上来讲。假设我们採用编译型的语言来做一款游戏,那么我们根本无法实现对游戏内容的扩充,由于我们须要对整个项目进行又一次编译然后打包成游戏光盘再出售给玩家。
这样无疑会添加游戏制作方的制作成本,而更重要的是玩家不会为了新的游戏内容而再次购买游戏,显然这样的方式是不合理的。那么,此时像Lua这样的脚本语言就能够发挥出巨大的作用,由于脚本语言通常不会占用太多资源,也许我们仅仅须要一个游戏脚本就能够利用游戏中现有的场景和人物开辟出新的游戏剧情。
所以,经过一番分析。大家能够总结出脚本语言在游戏开发中一个关键的数据就是更新。由于脚本语言通常都是纯文本文件。我们仅仅须要改变某些參数而不必又一次编译整个项目,这正是我们希望看到的。
好了,以下我们就来一起学习在Unity3D项目中怎样使用Lua语言吧,Unity3D基于Mono虚拟机,所以理论上.NET的类库是能够直接在Unity3D中使用的。但是考虑到Unity3D跨平台的须要,我们选择的工具必须在各个平台获得良好的支持。在前文中提到的LuaInterface理论上是能够在Unity3D中使用的。但是由于IOS不支持反射机制,所以这个类库我们无法直接在Unity3D中使用的。在开源社区中。博主发现了云风团队的阿楠开发的UniLua,云风团队的风云是一个在国内游戏开发领域比較著名的人物,那么我们今天就来选择UniLua来作为我们的一个工具库吧,该项目是一个开源项目,參考了LuaInterface项目,只是在处理反射这个问题上使用了新的方法。所以眼下能够完美地支持各个平台。相信大家有了前面两篇文章的基础。如今已经能够从容地面对Lua API了吧。
好了,我们。如今来创建一个简单地Unity项目:
第一步是下载UniLua:http://github.com/xebecnan/UniLua。将UniLua引用到项目中有两种方法,一种是将该项目中的UniLua编译成dll然后在Unity项目中使用,一种是将该项目中的UniLua直接拷贝到Unity 项目中,我们这里使用另外一种方法。由于博主比較懒,呵呵。将UniLua的命名空间加入到我们项目中,我们就能够開始动手敲代码了。只是这里,博主想说的是Mono可能会导致的一个错误,预计是阿楠在写这个项目的时候使用了.NET4.0以上的版本号。而在.NET4.0以上的版本号是支持默认參数的构造函数的。但是由于Mono默认使用的是.NET3.5,所以在编译项目的时候就会报错,我们能够通过Project->Assembly-CSharp->Build->General将.NET的目标框架设为4.0,这样就能够解决问题了。
好了,以下我们開始写代码啦,首先创建一个InvokeScript.cs的脚本:
using UnityEngine;
using System.Collections;
using UniLua; public class InvokeScript : MonoBehaviour { //Lua脚本文件,我们将在C#调用该脚本
public TextAsset LuaFile;
//Lua虚拟机
private ILuaState mLua; void Start()
{
//初始化Lua虚拟机
mLua=LuaAPI.NewState();
//载入Lua标准库
mLua.L_OpenLibs();
//引用一个静态地C#库
mLua.L_RequireF(CSharpLib.CLASSNAME,CSharpLib.InitLib,false); //运行Lua脚本
mLua.L_DoString(LuaFile.text);
} void OnGUI()
{
if(GUILayout.Button("调用Lua脚本",GUILayout.Height(30)))
{
InvokeLua();
}
if(GUILayout.Button("调用C#脚本",GUILayout.Height(30)))
{
InvokeCSharp();
}
} #region 调用C#脚本
void InvokeCSharp()
{
//获取方法并传入參数
mLua.GetGlobal("SumAndSub");
mLua.PushInteger(12);
mLua.PushInteger(8);
mLua.PCall(2,4,0);
}
#endregion #region 调用Lua脚本
void InvokeLua()
{
//获取Lua脚本中的arg1參数
mLua.GetGlobal("arg1");
//输出arg1
Debug.Log("Lua脚本中的变量arg1="+mLua.L_ToString(-1)); //获取Lua脚本中的arg2參数
mLua.GetGlobal("arg2");
//输出arg2
Debug.Log("Lua脚本中的变量arg2="+mLua.L_ToString(-1)); //获取Lua脚本中的Printf方法
mLua.GetGlobal("Printf");
//调用Lua脚本中的Printf方法
mLua.PCall(0,0,0); //获取Lua脚本中的Sum方法
mLua.GetGlobal("Sum");
//传入參数12和25
mLua.PushInteger(12);
mLua.PushInteger(25);
//调用此方法
mLua.PCall(2,3,0);
//获取传入的两个參数及求和结果
int a=mLua.ToInteger(-3);
int b=mLua.ToInteger(-2);
int sum=mLua.ToInteger(-1);
//输出
Debug.Log("调用Lua脚本中的Sum方法:"+a+"+"+b+"="+sum);
}
#endregion }
在这段脚本中。我们首先初始化了Lua环境,这一点和我们在C++中使用Lua是一样的,由于UniLua在设计API的时候在命名上和LuaAPI保持了高度的一致,假设你对Lua API足够熟悉的话,那么如今这一切对你而言应该会非常easy的。接下来,我们通过Require的形式引入了我们编写的一个C#库,它是一个静态库,目的是封装C#方法以便于Lua脚本来调用,这一部分我们稍后会讲到。接下来,我们通过Unity的AssetText载入了一个Lua脚本文件,该脚本的文件的扩展名是.txt,由于我们仅仅须要Lua脚本的内容。在脚本中我们定义了两个方法InvokeLua和InvokeSharp来分别调用Lua脚本和C#脚本。好了。接下来,我们重点来讲Lua调用C#脚本的这部分。由于UniLua在调用函数这块儿和LuaInterface不太一样,所以我们不能再用原来的先注冊C#方法然后再像Lua脚本方法一样。只是博主认为这里的原理是一样的,只是UniLua提供了更好的方法绑定机制,我们来看以下的脚本:
using UnityEngine;
using System.Collections;
using UniLua; public static class CSharpLib
{
//当前类文件名称称,我们将在Lua脚本中使用这个名称
public const string CLASSNAME="CSharpLib.cs"; //C#库初始化
public static int InitLib(ILuaState lua)
{
NameFuncPair[] define=new NameFuncPair[]
{
new NameFuncPair("SumAndSub",SumAndSub),
};
lua.L_NewLib(define); return 1;
} //我们在C#中定义一个求和差的方法
public static int SumAndSub(ILuaState lua)
{
//第一个參数
int a=lua.L_CheckInteger(1);
//第二个參数
int b=lua.L_CheckInteger(2);
//计算和
int c=a+b;
//计算差
int d=a-b; //将參数及计算结果压入栈
lua.PushInteger(a);
lua.PushInteger(b);
lua.PushInteger(c);
lua.PushInteger(d); //有四个返回值, 虽然在C#中不支持返回多个值,但是在Lua中这样是支持的
return 4;
}
}
大家一定注意到这里有个NameFuncPair类吧。这就是在UniLua中用来将一个C#方法和Lua方法进行绑定的方法。我们首先构造这样一个NameFuncPair数组,然后将其加入到lua_L_NewLib()的參数中。这样相当于是注冊了一个库,我认为应该就是注冊了一个方法集合吧.而CLASSNAME是一个表示当前类名称的常量,能够取随意字符。这里我们使用该类的文件名称我们将在Lua脚本是用这个值来查找当前类.接下来。我们能够看到博主构造了一个求和差的C#方法,这种方法和Lua API中定义的方法是一致的。即我们须要指定该方法会返回的值得数目.假设我们须要返回一个值。就要把它通过push系列的方法压入栈中.这里我们返回了四个值,大家一定会问好是C#还支持返回多个值啊,事实上呢,这是Lua语言提供给我们的一个福利啊,比方我们须要返回一个物体在3D世界里的坐标,通常情况下。我们须要用三个赋值语句才干获取吧,但是你用Lua的话,一行代码就能够搞定啦.好。如今我们回到InvokeScript脚本的Start方法中,大家能够注意到这里有一个L_RequireF()的方法,前面仅仅是轻描淡写地说它引入了一个库,那么如今我们看看它详细做了什么吧,第一个參数表示这个类的名字。指向我们定义好的CLASSNAME,第二个參数是这个类的初始化方法指向InitLib()方法。第三个參数是是否要在全局空间中使用这个库。这里我们选在false.好了,这样,我们就完毕了C#脚本的编写.好了,以下我们在项目中创建一个纯文本文件。我们输入例如以下代码:
local csharplib=require"CSharpLib.cs"
arg1="Unity3D"
arg2="Unreal"
arg3="Coco2dX" function Printf()
print("This is the methods invoked in Lua")
end function Sum(a,b)
return a,b,a+b
end function SumAndSub(a,b)
print(csharplib.SumAndSub(a,b))
end
第一行代码相同是一个require的方法,这是Lua脚本中引用一个库的方法,该方法能够引用Lua的标准库,相同能够引用我们定义的外部库。大家注意到这里的名字和我们之前定义的CLASSNAME是一样的,由于我们就是通过这个名字来查询这个库的。我们在Lua环境中注冊了这个库,所以如今才干够引用这个库.在这段脚本中我们定义了几个字符型的变量,两个Lua方法,一个用Lua包装的C#方法.好了。如今我们将这个文本文件指定到InvokeScript的LuaFile字段,我们通过LuaFille的text获取脚本内容,然后通过DoString()方法来运行脚本中的内容。注意这里要先对C#库进行注冊,然后再运行脚本中的内容,否则会出现错误.好了,最后,我们来一起看看运行效果吧:
大家能够看到C#调用的Lua脚本中我们获取了脚本中的两个变量arg1、arg2,调用了Lua中定义的两个方法。而最后一个方法,如我们所愿。它返回了四个值,这正是我们所希望的结果.这里顺便说一下啊,在Lua中的print方法和return在Call以后是能够直接在Debug中输出结果的。无需我们再去做Log。
好了,今天的内容就是这样啦。希望大家喜欢啊,欢迎大家关注我的博客,在下一篇文章中,博主将带领大家继续Lua究竟,请关注博主的下一篇文章《Unity3D游戏开发之Lua与游戏的不解之缘终结篇:UniLua热更新全然解读》。谢谢大家.关于UniLua调用非静态的类和方法。大家能够參考这篇文章:http://www.cnblogs.com/cqgreen/p/3483026.html。
每日箴言:合适的人生位置,既不靠近钱,也不靠近权。而是靠近灵魂。真正的幸福,既不是富贵,也不是凡事都对,而是问心无愧。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei。
转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/40050225
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------