您的位置:首页 > 移动开发 > Unity3D

Unity3D在IOS、Android上使用C++并回调的究极解决方案

2017-11-17 18:43 525 查看
转载地址:
http://blog.csdn.net/IceTeaSet/article/details/53142929
各种调用C++的方法在各种平台下的总结如下(可能会有出入):

1、在VS中使用CLR C++工程,编译产生.dll,放入Plugins

✔ Android、Editor、Windows、OSX  

✘ IOS (报的错貌似与il2cpp有关)

2、直接在Plugins中放入.cpp文件(不支持子目录)

✔ IOS 

✘ Editor、Windows、OSX、Android

3、编译出目标移动平台的库(.a或.so等),放入Plugins

✔ IOS、Android 

✘ Editor、Windows、OSX

4、编译出PC平台的库,放入Plugins

✔ Windows(.dll)、Linux(.so)、OSX(Bundle) 

✘ iOS、Android 

? Editor

我的目标主要是IOS,所以下面以IOS、C++为例介绍一下各种解决方案,我的Unity版本是5.4.2f2。(后因为build时崩溃换成5.5.0f3)

无论用哪种方法,都需要在Unity工程中Assets中新建一个Plugins文件夹,再新建一个iOS文件夹。

第一种方案的话,CLR C++的语法与正常C++稍有不同,要改C++代码,而且还不能在iOS上使用,就直接否决了。第四种也不用说,应该行不通。

第二、三种方案根据回调方法的不同每种又可以分为两种方法:使用官方手册上的UnitySendMessage;使用MonoPInvokeCallBack。所以共四种方法。

直接看我将四种方法汇总在一块的代码吧:
//C#
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using AOT;

public class MonoCB : MonoBehaviour {

[StructLayout(LayoutKind.Sequential)]
struct Parameter {
public int a;
public int b;
}

delegate void CallBack(IntPtr param);

[DllImport ("__Internal")]
static extern void TestFunc(int a,int b,CallBack cb);

[DllImport ("__Internal")]
static extern void TestFunc2(int a,int b);

[DllImport ("__Internal")]
static extern void TestFunc3(int a,int b,CallBack cb);

[DllImport ("__Internal")]
static extern void TestFunc4(int a,int b);

const int CallBackByMonoPInvokeFromCpp = 1;
const int CallBackByUnitySendMessageFromCpp = 1;
const int CallBackByMonoPInvokeFromLib = 1;
const int CallBackByUnitySendMessageFromLib = 1;

// Use this for initialization
void Start () {
RuntimePlatform rp = Application.platform;
Debug.Log ("[DQC]:" + rp.ToString ());

if (rp == RuntimePlatform.IPhonePlayer) {
if (CallBackByMonoPInvokeFromCpp>0) {
Debug.Log ("[DQC]:start accessing cpp TestFunc. ");
TestFunc (1,0,CallBackFunc);
Debug.Log ("[DQC]:End accessing cpp TestFunc. ");
}
if (CallBackByUnitySendMessageFromCpp>0) {
Debug.Log ("[DQC]:start accessing cpp TestFunc2. ");
TestFunc2 (2,0);
Debug.Log ("[DQC]:End accessing cpp TestFunc2. ");
}
if (CallBackByMonoPInvokeFromLib>0) {
Debug.Log ("[DQC]:start accessing cpp TestFunc3. ");
TestFunc3 (3,0,CallBackFunc3);
Debug.Log ("[DQC]:End accessing cpp TestFunc3. ");
}
if (CallBackByUnitySendMessageFromLib>0) {
Debug.Log ("[DQC]:start accessing cpp TestFunc4. ");
TestFunc4 (4,0);
Debug.Log ("[DQC]:End accessing cpp TestFunc4. ");
}
}
else if (rp == RuntimePlatform.OSXEditor || rp == RuntimePlatform.OSXPlayer)
Debug.Log ("[DQC]:need to run in a real iphone to access cpp.");

}

// Update is called once per frame
void Update () {

}

[MonoPInvokeCallback(typeof(CallBack))]
static void CallBackFunc(IntPtr param) {
var p = (Parameter)Marshal.PtrToStructure(param, typeof(Parameter));
Debug.Log("[DQC]:CallBackFunc. a:" + p.a + " b:" + p.b);
}

void CallBackFunc2(string message)
{
Debug.Log("[DQC]:CallBackFunc2. Message is:"+ message);
}

[MonoPInvokeCallback(typeof(CallBack))]
static void CallBackFunc3(IntPtr param) {
var p = (Parameter)Marshal.PtrToStructure(param, typeof(Parameter));
Debug.Log("[DQC]:CallBackFunc3. a:" + p.a + " b:" + p.b);
}

void CallBackFunc4(string message)
{
Debug.Log("[DQC]:CallBackFunc4. Message is:"+ message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//C++ 直接用.cpp

#include <iostream>
using namespace std;

extern "C" {

typedef struct Parameter {
int a;
int b;
} Param;

typedef void(*CallBack)(Param* p);

void TestFunc(int a,int b,CallBack cb) {
cout<<"[DQC]:in TestFunc.a:"<<a<<" b:"<<b<<endl;
Param p;
p.a = a;
p.b = b;
cb(&p);
}

void TestFunc2(int a,int b)
{
cout<<"[DQC]:in TestFunc2.a:"<<a<<" b:"<<b<<endl;
UnitySendMessage("Main Camera","CallBackFunc2","[DQC]:success from TestFunc2");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//C++ 编译成.a  与上面几乎一样
#include <iostream>
using namespace std;

extern "C" {

typedef struct Parameter {
int a;
int b;
} Param;

typedef void(*CallBack)(Param* p);

namespace IvesTest{

void TestFunc3(int a,int b,CallBack cb) {
cout<<"[DQC]:in TestFunc3.a:"<<a<<" b:"<<b<<endl;
Param p;
p.a = a;
p.b = b;
cb(&p);
}
}

//注意这里
extern void UnitySendMessage(const char* obj, const char* method, const char* msg);

void TestFunc4(int a,int b)
{
cout<<"[DQC]:in TestFunc4.a:"<<a<<" b:"<<b<<endl;
UnitySendMessage("Main Camera","CallBackFunc4","[DQC]:success from TestFunc4");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

将最后一个代码片用CocoaTouchStaticLibrary工程编译成.a后,与第二个代码片的.cpp一起放到Plugins/iOS文件夹中。Unity在Build时会将Plugins复制到Xcode工程里的Libraries文件夹下。

几个注意事项: 

1、尽量不要在XCode里的Libraries/Plugins中直接修改.cpp,因为很容易就被Unity覆盖或者删除了……别问我怎么知道的。如果要在XCode中修改.cpp,那么要么干脆把Unity工程里的Plugins删除,要么可以放到XCode下的Classes文件夹中(官方文档推荐的,说是不会被覆盖)。

2、官方文档说UnitySendMessage是异步的,可能会慢一帧,MonoPInvokeCallBack未知。

3、如果是在Android,可能要将dllimport中的“__Internal”改成“库文件名”。

4、[重要]CocoaTouchStaticLibrary工程的IOS deployment target要设置成和Unity Xcode工程一样,不然链接时会报错。Unity生成的Xcode默认是7.0,郁闷的是现在最低只能选8.0了,只好都改成8.0或者更高。

5、用了extern “C”后namespace就失效了(如上例TestFunc3),注意不要重定义

要是官方文档能更详细一点就好了…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐