一、什么情況下需要使用C++
1.大量的復(fù)雜運算克懊,C++比C#效率高忱辅。
2.大多數(shù)語言都有調(diào)用C++ DLL的途徑,若項目中某個模塊客戶端和服務(wù)器都需要使用谭溉,可以考慮用C++實現(xiàn)該模塊墙懂,這樣客戶端和服務(wù)器就不需要重復(fù)編寫該模塊,只需寫一些膠水代碼即可扮念。
二损搬、基本概念
1.托管(Managed)和非托管(Unmanaged):.Net的運行環(huán)境是CLR(Common Language Runtime),運行在CLR上的代碼成為托管代碼(Managed Code)柜与,CLR提供了自動的垃圾回收機制(GC)巧勤。而C++是編譯后直接由操作系統(tǒng)執(zhí)行的代碼,不運行在CLR上弄匕,所以C++屬于非托管代碼(Unmanaged Code)颅悉。(注:另有托管C++,本質(zhì)上也屬于.Net范疇迁匠,不在討論范圍內(nèi))
2.P/Invoke:P/Invoke(Platform Invoke剩瓶,平臺調(diào)用)使得我們可以在托管代碼中調(diào)用非托管函數(shù),Unity與C++的交互都是通過P/Invoke實現(xiàn)城丧。
三儒搭、Demo
1.創(chuàng)建C++ DLL
在VS中新建C++項目,這里理應(yīng)選擇DLL芙贫,但是建議先選成控制臺搂鲫,等我們編寫的函數(shù)先在控制臺調(diào)試沒問題后可以在項目屬性中改為DLL。
2.新建一個類Bridge.h和Bridge.cpp
//Bridge.h
#ifdef WIN32
#ifdef UNITY_CPP_INTEROP_DLL_BRIDGE
#define UNITY_CPP_INTEROP_DLL_BRIDGE __declspec(dllexport)
#else
#define UNITY_CPP_INTEROP_DLL_BRIDGE __declspec(dllimport)
#endif
#else
// Linux
#define UNITY_CPP_INTEROP_DLL_BRIDGE
#endif
extern "C"
{
UNITY_CPP_INTEROP_DLL_BRIDGE int Internal_Add(int a, int b);
}
//Bridge.cpp
#include "Bridge.h"
extern "C"
{
int Internal_Add(int a, int b)
{
return a + b;
}
}
3.拷貝DLL
右鍵項目生成DLL后磺平,將DLL拷貝到Unity項目中魂仍。
4.在C#中調(diào)用DLL
在Unity中新建腳本填入如下內(nèi)容。
// Use this for initialization
void Start()
{
int a = 5, b = 6;
Debug.LogError(string.Format("Internal_Add(): {0} + {1} = {2}", a, b, Internal_Add(a, b)));
Debug.LogError(string.Format("Add(): {0} + {1} = {2}", a, b, Add(a, b)));
}
[DllImport("UnityCppInterop")]
private static extern int Internal_Add(int a, int b);
[DllImport("UnityCppInterop", EntryPoint = "Internal_Add")]
private static extern int Add(int a, int b);
其中由兩個extern修飾的函數(shù)與C++中Internal_Add()函數(shù)對應(yīng)拣挪,二者的區(qū)別在于是否指定了EntryPoint(入口)擦酌,EntryPoint參數(shù)指明了從UnityCppInterop.dll中調(diào)用的函數(shù)名,如果未指定菠劝,則會調(diào)用與C#中函數(shù)名相同的C++函數(shù)赊舶。本例中,二者調(diào)用的是C++中的同一個函數(shù),輸出如下:
如果將Add()的EntryPoint刪除笼平,會報EntryPointNotFoundException異常:
四园骆、Android Studio中編譯so文件
上面只是介紹了在Windows平臺Unity與C++交互的過程,但發(fā)布到Android平臺后DLL是無法使用的寓调,我們需要將C++源碼編譯成Android平臺可用的庫文件xxx.so锌唾。
1.新建Android工程
勾選Include C++ support,一路下一步即可夺英。
創(chuàng)建好后檢查工程屬性中是否指定了ndk路徑:
修改app的build.gradle內(nèi)容(供參考)如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.3"
defaultConfig {
applicationId "com.zqj.unitycppinterop"
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk{
moduleName "native-lib"
abiFilters "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:26.+'
}
編譯so可以使用CMake或Android.mk晌涕,本文介紹CMake,如果沒有安裝痛悯,可在Android Studio的SDK Tools中下載余黎。將之前編寫的C++文件拷貝到cpp目錄下,最好將頭文件和源文件分類载萌,native-lib.cpp是新建工程時自動創(chuàng)建的惧财,可以刪掉:
CMakeList.txt
cmake_minimum_required(VERSION 3.4.1)
#設(shè)置頭文件目錄
set(INCLUDE_DIR
"src/main/cpp/include/"
)
include_directories(${INCLUDE_DIR})
#需要編譯的源文件
file(GLOB_RECURSE SRC_FILE
"src/main/cpp/src/*.cpp"
)
#################################
add_library( # 最后生成的庫名稱
UnityCppInterop
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/native-lib.cpp
${SRC_FILE} )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
Make Project成功后,生成的libUnityCppInterop.so位于app/build/intermediates/cmake/debug中炒考,把armeabi-v7a下的so文件拷貝到Unity工程Plugins/Android/目錄下。這里的so文件名比DLL多了lib前綴霎迫,不需要修改斋枢,Unity會自動識別。
總結(jié)
本文主要介紹Unity與C++交互的基礎(chǔ)知識知给,Demo只演示了最基本類型int型數(shù)據(jù)的交互瓤帚,而實際項目中肯定會涉及到string, struct, class, 數(shù)組等復(fù)雜類型數(shù)據(jù)的傳遞,這其中有很多需要注意的地方涩赢,下一篇會針對這些進行介紹戈次。