Unity与C++交互入门(1)

一、什么情况下需要使用C++

1.大量的复杂运算,C++比C#效率高。

2.大多数语言都有调用C++ DLL的途径,若项目中某个模块客户端和服务器都需要使用,可以考虑用C++实现该模块,这样客户端和服务器就不需要重复编写该模块,只需写一些胶水代码即可。

二、基本概念

1.托管(Managed)和非托管(Unmanaged):.Net的运行环境是CLR(Common Language Runtime),运行在CLR上的代码成为托管代码(Managed Code),CLR提供了自动的垃圾回收机制(GC)。而C++是编译后直接由操作系统执行的代码,不运行在CLR上,所以C++属于非托管代码(Unmanaged Code)。(注:另有托管C++,本质上也属于.Net范畴,不在讨论范围内)

2.P/Invoke:P/Invoke(Platform Invoke,平台调用)使得我们可以在托管代码中调用非托管函数,Unity与C++的交互都是通过P/Invoke实现。

三、Demo

1.创建C++ DLL

在VS中新建C++项目,这里理应选择DLL,但是建议先选成控制台,等我们编写的函数先在控制台调试没问题后可以在项目属性中改为DLL。

新建C++项目
修改项目属性

2.新建一个类Bridge.h和Bridge.cpp

新建Bridge类
//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项目中。

拷贝DLL

4.在C#中调用DLL

在Unity中新建脚本填入如下内容。

    // 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修饰的函数与C++中Internal_Add()函数对应,二者的区别在于是否指定了EntryPoint(入口),EntryPoint参数指明了从UnityCppInterop.dll中调用的函数名,如果未指定,则会调用与C#中函数名相同的C++函数。本例中,二者调用的是C++中的同一个函数,输出如下:

Demo输出

如果将Add()EntryPoint删除,会报EntryPointNotFoundException异常:

去掉EntryPoint的输出

四、Android Studio中编译so文件

上面只是介绍了在Windows平台Unity与C++交互的过程,但发布到Android平台后DLL是无法使用的,我们需要将C++源码编译成Android平台可用的库文件xxx.so。

1.新建Android工程

勾选Include C++ support,一路下一步即可。

新建Android工程

创建好后检查工程属性中是否指定了ndk路径:

检查NDK

修改app的build.gradle内容(供参考)如下:

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可以使用CMakeAndroid.mk,本文介绍CMake,如果没有安装,可在Android Studio的SDK Tools中下载。将之前编写的C++文件拷贝到cpp目录下,最好将头文件和源文件分类,native-lib.cpp是新建工程时自动创建的,可以删掉:

项目结构

CMakeList.txt


cmake_minimum_required(VERSION 3.4.1)

#设置头文件目录
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会自动识别。

总结

本文主要介绍Unity与C++交互的基础知识,Demo只演示了最基本类型int型数据的交互,而实际项目中肯定会涉及到string, struct, class, 数组等复杂类型数据的传递,这其中有很多需要注意的地方,下一篇会针对这些进行介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容