在Unity開發(fā)或者SDK開發(fā)經(jīng)常遇到Unity與移動端原生層之間進行通信,這里把它們之間通信做一個整理。
關(guān)于Unity與Android之間通信,參考【Unity3d】Unity與Android之間通信
Unity調(diào)用Objective-C
主要分三個步驟:
(一)、在xcode中定義要被unity調(diào)用的函數(shù)
新建一個類,名字可以任意,比如UnityBridge:
頭文件:UnityBridge.h
(頭文件中不需要字段和函數(shù)聲明)
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UnityBridge : NSObject
@end
NS_ASSUME_NONNULL_END
頭文件里沒什么代碼,主要代碼在實現(xiàn)文件中。
實現(xiàn)文件:UnityBridge.m
實現(xiàn)文件中需要用c語言定義函數(shù),這些函數(shù)定義了之后,就可以被unity調(diào)用:
#import "UnityBridge.h"
//如果c#調(diào)用oc函數(shù)時需要一個回調(diào),需要先聲明回調(diào)參數(shù)類型:
typedef void (*MyResultCallback) (int status,const char *result);
//在.m文件中以下4行可以不需要
#if defined (__cplusplus)
extern "C"
{
#endif
//這里寫被unity調(diào)用的函數(shù)
void test1(void){
//這里是函數(shù)實現(xiàn),支持oc語法
}
/**
* int類型參數(shù),返回int
*/
int test2(int params){
//這里是函數(shù)實現(xiàn),支持oc語法
}
/**
* 字符串類型參數(shù)
*/
void test3(const char *params){
//這里是函數(shù)實現(xiàn),支持oc語法
}
/**
* 返回字符串時,不能直接返回oc轉(zhuǎn)c的字符串,要調(diào)用strdup函數(shù)再返回
*/
const char* test4(void){
//返回字符串時,需要使用c分配內(nèi)存再返回,否則閃退
//return "abc" //錯誤,會閃退
//return [@"abc" UTF8String];//錯誤,會閃退
//return strdup("abc") //正確
//strdup函數(shù)需要引入#import <string.h>
return strdup([@"abc" UTF8String]) //要調(diào)用strdup()函數(shù)分配c內(nèi)存再返回給unity,不然閃退
}
/**
* 支持回調(diào)參數(shù)
*/
void test5(MyResultCallback callback){
//這里是函數(shù)實現(xiàn),支持oc語法
int code = 0;
NSString p = @"test";
const char *result = [p UTF8String];
//回調(diào)給c#
callback(code,result)
}
//在.m文件中以下3行可以不需要
#if defined (__cplusplus)
}
#endif
函數(shù)返回類型是字符串時,oc這邊不能直接返回oc轉(zhuǎn)c的char*字符,否則閃退,c++閃退代碼行:
…
// Marshaling cleanup of return value native representation
il2cpp_codegen_marshal_free(returnValue);
原因是oc使用了ARC自動釋放內(nèi)存,c層再去free內(nèi)存發(fā)現(xiàn)未分配就會報錯。
需要調(diào)用strdup()
函數(shù)再返回,函數(shù)所在頭文件:#import <string.h>
參見上面示例代碼的test4()
函數(shù)。
注意這些代碼不要寫在@implementation
中,它是c語言的函數(shù)。
C++支持函數(shù)重載,而C不支持,兩者的編譯規(guī)則也不一樣。函數(shù)被C++編譯后在符號庫中的名字與C語言的不同。例如,假設某個函數(shù)的原型為: void foo( int x, int y ); 該函數(shù)被C編譯器編譯后在符號庫中的名字可能為_foo,而C++編譯器則會產(chǎn)生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不 同,但是都采用了相同的機制,生成的新名字稱為“mangled name”)。_foo_int_int這樣的名字包含了函數(shù)名、函數(shù)參數(shù)數(shù)量及類型信息,C++就是靠這種機制來實現(xiàn)函數(shù)重載的。如果要在C++中使用C的函數(shù),需要加extern "C"修飾。
#if defined (__cplusplus)
這是一個宏判斷,c++中才有這個宏。extern "C"也是c++的關(guān)鍵字。因為oc是支持c++(在.mm文件中才支持),extern "C"
表示后面函數(shù)是c語言的,供外部調(diào)用的。如果在.m文件中,可以不需要extren “C”。
(二)、將oc代碼復制到unity工程中。
將oc代碼(包括.h和.m源文件)拷貝到Unity工程的Assets
目錄或子目錄中。
實際上將oc代碼放在unity工程的Assets
目錄下的任意位置都可以,oc代碼會自動被unity引擎識別。
(筆者使用的unity版本是2019.4,以前的版本不知道是否可以是任意位置。)
為了方便管理,oc代碼一般放在Assets/Plugins/iOS
中。
也可以將代碼打包成framework靜態(tài)庫,然后將其拷貝到unity工程的Assets目錄中,一般放在 Assets/Plugins/iOS
這個目錄中。Unity導出xcode會自動依賴這個framework。
(三)、在unity的c#腳本中聲明外部(oc)函數(shù)原型
using System.Runtime.InteropServices; //需要引入這個命名空間,會提示引入
using UnityEngine;
public class Test
{
private Test() {}
#if UNITY_IOS //加個宏比較好
//以下是外部函數(shù)聲明,函數(shù)簽名必須與oc函數(shù)保持一致,參數(shù)類型用各自語言的,類型映射見文末。
//聲明無參數(shù)無返回值的函數(shù)
[DllImport("__Internal")]
private static extern void test1();
//聲明有一個int參數(shù)和int返回值的函數(shù)
[DllImport("__Internal")]
private static extern int test2(int p);
//聲明一個string參數(shù)的函數(shù)
[DllImport("__Internal")]
private static extern void test3(string p);
//聲明返回string類型的函數(shù)
[DllImport("__Internal")]
private static extern string test4();
//如果需要oc的回調(diào),聲明一個回調(diào)函數(shù)類型
delegate void MyResultDelegate(int code,string result);
[DllImport("__Internal")]
private static extern void test5(MyResultDelegate resultDelegate);
/**
* 回調(diào)函數(shù)的實現(xiàn)(或者叫實例)
* 注意:必須是static類型的
*/
[AOT.MonoPInvokeCallback(typeof(MyResultDelegate))]
private static void MyResultDelegateInstance(int code, string result)
{
//這里寫接收到oc回調(diào)的代碼
}
#endif
public void CallOC()
{
#if UNITY_IOS
test1();
int result1 = test2(1);
test3("abc");
string result2 = test4();
test5(MyResultDelegateInstance);
#endif
}
}
在c#中調(diào)用oc中對應的方法,參見以上 CallOC()
。
c#調(diào)用oc注意事項:
1、數(shù)據(jù)類型需要使用各自語言的,兩者數(shù)據(jù)類型映射關(guān)系在文未。
2、c#中聲明的oc方法、回調(diào),都需要static修飾。
Objective-C調(diào)用Unity(c#)
oc調(diào)用c#比較簡單,一般使用以下這個方法:
UnitySendMessage("MyTestObject", "TestFunc", "msg");
UnitySendMessage
函數(shù)聲明在UnityFramework.framework
中UnityInterface.h
頭文件中:
void UnitySendMessage(const char* obj, const char* method, const char* msg);
第一個參數(shù)obj表示unity中物體GameObject的名字,注意不是c#腳本的名稱也不是類名。
如下圖:
第二個參數(shù)method表示這個物體掛載的c#腳本中方法的名字。
第三個參數(shù)表示msg表示這個方法接收的數(shù)據(jù)。
例如,以上物體MyTestObject
掛載了MyScript.c#
腳本(如上圖),MyScript.c#
中有TestFunc
方法:
using UnityEngine;
public class MyScript : MonoBehaviour
{
private void TestFunc(string content)
{
//這里是接收oc調(diào)用的實現(xiàn)
}
}
那么在oc中調(diào)用UnitySendMessage("MyTestObject", "TestFunc", "msg")
c#的TestFunc
方法就會執(zhí)行。
如果有多個參數(shù)需要發(fā)送,推薦使用json格式。
oc調(diào)用c#注意事項:
1、需要依賴UnityFramework.framework
框架。
2、unity工程導出的xcode工程默認已經(jīng)有UnityFramework.framework
。
3、如果是自己新建的xcode工程需要手動導入這個框架。文章來源:http://www.zghlxwxcb.cn/news/detail-789074.html
附:c#與oc數(shù)據(jù)類型映射:文章來源地址http://www.zghlxwxcb.cn/news/detail-789074.html
Unity(c#) | Objective-C |
---|---|
int | int |
float | float |
bool | bool |
string | const char * |
long | long long |
到了這里,關(guān)于【Unity3d】Unity與iOS之間通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!