Unity3D游戏开发之C++插件接入
各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://qinyuanpei.com。
虽然Unity3D引擎凭借强大的跨平台能力在高手林立的游戏引擎世界中占据一席之地,在使用Unity3D游戏引擎时,我们通常不会接触到底层内容。但在面对一些特殊需求时,就不得不考虑使用C++编写相关插件。例如,接入蓝牙手柄控制游戏、接入类似街机的设备控制游戏、将同一个游戏接入两个不同设备并响应不同控制等问题,目前在Unity3D引擎中可能找不到现成的解决方案,此时编写C++插件就成为了刚需,这也是我们今天要探讨的内容。
Unity3D主要使用C#进行开发,因此为Unity3D编写插件本质上就是让C#调用C++代码。目前主要有C++ CLR和C++ Native两种实现方法:
- C++ CLR:可理解为运行在.Net CLR(公共语言运行库)上的C++代码,是托管的C++代码,但未被C++标准承认,更像是C++和C#的混合代码。其优势在于可以像普通的.NET库一样被C#调用,考虑到Unity3D基于和.Net类似的Mono,这种方式是比较好的实践方案。
- C++ Native:指传统的C++动态链接库,通过DllImport在C#中进行包装后调用,调用的是非托管的C++代码。对于接触过Windows开发的朋友来说应该不陌生,这是一种更为普遍的方法。例如,接入苹果官方SDK时,需要对Object C的代码进行封装后交给C#调用,这里使用的就是DllImport方法。
下面我们来详细看看这两种方式的实现过程。博主使用的开发环境是Windows 8.1 32bit和Visual Studio 2012,Unity3D的版本为4.6。
C++ CLR
创建一个C++ CLR类库项目
首先按照以下步骤创建一个C++ CLR项目:
需要注意.Net版本问题,创建好项目后,打开项目属性窗口,设置【公共语言运行时支持】节点的值为【安全 MSIL 公共语言运行时支持(/clr:safe)】。接着找到CLR4Unity.h文件,添加ExampleClass声明:
/// <summary>
/// 一个简单的托管C++示例类
/// </summary>
public ref class ExampleClass
{
public:
/// <summary>
/// 产生一个介于min和max之间的整型随机数
/// <returns>整型随机数</returns>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// </summary>
static int Random(int min, int max)
{
//注意在托管的C++中使用gcnew来代替new
return (gcnew System::Random)->Next(min, max);
}
/// <summary>
/// 计算一个整数的平方
/// <returns>整型数值</returns>
/// <param name="a">需要平方的数值</param>
/// </summary>
static int Square(int a)
{
return a * a;
}
/// <summary>
/// 返回两个数中的最大值
/// <returns>整型数值</returns>
/// <param name="a">参数1</param>
/// <param name="b">参数2</param>
/// </summary>
static int Max(int a, int b)
{
if (a <= b)
{
return b;
}
else
{
return a;
}
}
};
这里定义了三个简单的方法,第一个方法Random依赖于System.Random类,在托管的C++中使用gcnew代替new关键字,体现了C#和C++的混搭语法风格。编译后可得到CLR4Unity.dll类库,将该文件复制到Unity3D项目中的Plugins目录下,并加入项目引用列表。需要注意的是,不能直接使用using CLR4Unity;,要先添加对CLR4Unity.dll的引用,再进行using操作。
在C#中添加引用及方法调用
在Unity3D中创建一个脚本PluginTest.cs,然后在OnGUI方法中增加以下代码:
//调用C++ CLR中的方法
if (GUILayout.Button("调用C++ CLR中的方法", GUILayout.Height(30)))
{
Debug.Log("调用C++ CLR中的方法Random(0,10):" + ExampleClass.Random(0, 10));
Debug.Log("调用C++ CLR中的方法Max(5,10):" + ExampleClass.Max(5, 10));
Debug.Log("调用C++ CLR中的方法Square(5):" + ExampleClass.Square(5));
}
需要注意的是,要先了解MonoBehaviour这个类,若添加代码后报错且没有进行using操作,请自行检查。
C++ Native
创建一个C++动态链接库项目
按照相应步骤创建一个C++ Win32项目,找到Native4Unity.cpp文件,写入以下代码:
// Native4Unity.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
//为了使用rand()函数引入C++标准库
#include "stdlib.h"
/// <summary>
/// 产生一个介于min和max之间的整型随机数
/// <returns>整型随机数</returns>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
/// </summary>
extern "C" __declspec(dllexport) int Random(int min, int max)
{
return rand() % (max - min + 1) + min;
}
/// <summary>
/// 返回两个数中的最大值
/// <returns>整型数值</returns>
/// <param name="a">参数1</param>
/// <param name="b">参数2</param>
/// </summary>
extern "C" __declspec(dllexport) int Max(int a, int b)
{
if (a <= b)
{
return b;
}
else
{
return a;
}
}
/// <summary>
/// 计算一个整数的平方
/// <returns>整型数值</returns>
/// <param name="a">需要平方的数值</param>
/// </summary>
extern "C" __declspec(dllexport) int Square(int a)
{
return a * a;
}
和C++ CLR类似,这里使用标准的C++语言实现了相同的功能。由于使用了rand()函数,所以在文件开头引入了stdlib.h头文件。需要注意的是,所有希望使用DllImport引入C#的C++方法都应在方法声明中增加__declspec(dllexport)关键字,除非在.def文件中对这些方法进行显式声明。关于.def文件的相关定义,可到MSDN上检索。
在C#中使用DllImport封装方法
将编译好的Native4Unity.dll复制到Plugins目录中后,在C#里对这些方法进行封装或声明:
[DllImport("Native4Unity")]
private extern static int Random(int min, int max);
[DllImport("Native4Unity")]
private extern static int Max(int a, int b);
[DllImport("Native4Unity")]
private extern static int Square(int a);
然后进行简单的调用:
//调用C++ Native中的方法
if (GUILayout.Button("调用C++ Native中的方法", GUILayout.Height(30)))
{
Debug.Log("调用C++ Native中的方法Random(0,10):" + Random(0, 10));
Debug.Log("调用C++ Native的方法Max(5,10):" + Max(5, 10));
Debug.Log("调用C++ Native中的方法Square(5):" + Square(5));
}
最终程序的运行效果如图所示,这个结果来之不易,希望大家珍惜。