以下所有內(nèi)容均為個(gè)人觀點(diǎn),轉(zhuǎn)載請(qǐng)注明出處<簡(jiǎn)書(shū)--小蝸牛吱呀之悠悠 >,謝謝谁帕!
最近需要在原生項(xiàng)目集成unity導(dǎo)出的工程,并作為子模塊存在冯袍,網(wǎng)上教程不少雇卷,大多數(shù)都嘗試了一遍,但都不能用,最終关划,還是總結(jié)出一套最為簡(jiǎn)單、快速翘瓮、有效的方法贮折。
一、背景
unity導(dǎo)出的工程资盅,不僅可以以APP的形式獨(dú)立上線调榄,同時(shí)也可以將其囊括成framework的形式,集成進(jìn)入我們已有的原生工程中呵扛,下面?zhèn)€將介紹已有原生上線項(xiàng)目每庆,如何集成unity。
unity的集成有三種方式:
1今穿、直接拖拽導(dǎo)入
這種方式網(wǎng)上資料很多缤灵,但我嘗試后都失敗了,也許與我這邊使用的unity版本有關(guān)蓝晒,此處不再贅述
2腮出、將unity作為一個(gè)target導(dǎo)入,并建立關(guān)聯(lián)
此方法也資料很多芝薇,仍然沒(méi)有成功
上述兩種方法我這邊都失敗了胚嘲,目前初步猜測(cè)是與使用的unity版本有關(guān),如有大神知道原因洛二,歡迎留言~
3馋劈、將unity囊括成framework,并將unity的內(nèi)容作為子項(xiàng)目導(dǎo)入到原生的workspace中晾嘶,并建立兩者之間的關(guān)聯(lián)妓雾。
二、準(zhǔn)備工作
1变擒、確認(rèn)當(dāng)前使用的unity版本是否高于2019.3.a2君珠,如果低于此版本,本文將不適用娇斑,建議使用上述方式1策添、2。Xcode版本需要大于9.4毫缆,作者使用的是11.5版本唯竹。
2、unity導(dǎo)出環(huán)境配置
a. 首先在Unity編輯器打開(kāi)UnityProject項(xiàng)目苦丁,選擇Menu -> Window -> Package Manager浸颓,因?yàn)?.0.8版本不兼容使用Unity作為庫(kù),所以要移除Ads資源包,或更新Ads資源包到v 3.*版本产上。
b. 配置Bundle Identification和Signing Team ID棵磷,此步驟非必須,可以在導(dǎo)出后再配置晋涣,但作者是統(tǒng)一配置的仪媒,所以也一并提一下。
選擇Menu -> Edit -> Player Settings -> Player -> iOS設(shè)置標(biāo)簽頁(yè) -> Identification Section
c.導(dǎo)出unity項(xiàng)目時(shí)谢鹊,要注意區(qū)分是否支持模擬器算吩,此處特別重要,如果弄錯(cuò)了佃扼,將導(dǎo)致后續(xù)集成失敗偎巢,如果你的原生工程是真機(jī)調(diào)試,那直接導(dǎo)出真機(jī)的工程即可兼耀。
正確導(dǎo)出unity工程后压昼,就可以開(kāi)始進(jìn)行集成了
三、集成
集成分為兩個(gè)步驟:workspace配置 翠订、代碼配置
如果你的原生項(xiàng)目使用的是cocopods巢音,直接跳過(guò)此1.1步驟,從1.2開(kāi)始尽超。
1.1workspace配置
此步驟為沒(méi)有使用cocopods的項(xiàng)目集成用官撼,將原生工程和unity導(dǎo)出的工程放在同一個(gè)文件夾中,如下圖
打開(kāi)原生項(xiàng)目似谁,左上角點(diǎn)擊File->New->workspace傲绣,并建立的workspace保存在上圖中統(tǒng)一文件加下
此時(shí),關(guān)閉原生工程巩踏,打開(kāi)新建的workspace秃诵,點(diǎn)擊Xcode左下角的"+"號(hào),將原生工程塞琼、unity導(dǎo)出的工程添加進(jìn)來(lái)
導(dǎo)入完成后菠净,請(qǐng)從步驟2繼續(xù)集成
1.2原生項(xiàng)目有使用cocopods
將unity導(dǎo)出的工程拷貝到原生工程文件夾中,得到如下圖結(jié)構(gòu)
打開(kāi)workspace彪杉,點(diǎn)擊Xcode左下角的"+"號(hào)毅往,將unity導(dǎo)出的工程添加進(jìn)來(lái)
2.將unity工程集成為UnityFramework.framework
a.展開(kāi)unity原生工程,在products文件夾下找到UnityFramework.framework派近,右擊show in finder
b.在workspace中選中nativeiOS工程文件攀唯,點(diǎn)擊下圖的“+”號(hào)
此操作比較關(guān)鍵,很多人找不到UnityFramework.framework渴丸。打開(kāi)剛才UnityFramework.framework的路徑文件夾侯嘀,直接將文件夾拖拽進(jìn)剛才的路徑查找器中
添加完成以后另凌,注意檢查一下下圖項(xiàng)
此時(shí),你的原生空間下的Frameworks下將會(huì)出現(xiàn)UnityFramework.framework戒幔,且?guī)в姓归_(kāi)箭頭吠谢,否則就是錯(cuò)誤的
3.配置UnityFramework.framework和橋接文件
a.選中unity工程Data文件夾,按下圖配置
b.選中unity工程下的NativeCallProxy.h文件溪食,按下圖配置囊卜,注意,需要public
好多同學(xué)說(shuō)NativeCallProxy.h文件找不到错沃,這里特別說(shuō)明一下:這個(gè)文件是需要在導(dǎo)出unity工程之前,將NativeCallProxy.h文件導(dǎo)入雀瓢,然后再導(dǎo)出unity工程枢析;并且這個(gè)文件是不可以在unity工程導(dǎo)出后添加的,因?yàn)閡nity導(dǎo)出過(guò)程刃麸,會(huì)建立NativeCallProxy.h與unity工程的關(guān)聯(lián)醒叁,后期添加則沒(méi)有這個(gè)關(guān)聯(lián),編譯不通過(guò)
c.build一下UnityFramework.framework泊业,這一步一定要把沼,否則容易出現(xiàn)文件找不到的問(wèn)題
到這里為止,整個(gè)工程的配置就結(jié)束了吁伺,build一下你的原生工程饮睬,正常情況下是OK的,接下來(lái)就是代碼的配置
四篮奄、背景
1捆愁、新建一個(gè)繼承于NSObject的單例類(lèi),并添加以下兩個(gè)屬性
@property (nonatomic, assign) int gArgc;
@property (nonatomic, assign) char** gArgv;
2窟却、打開(kāi)main.m文件昼丑,在main函數(shù)中添加下面代碼,ConfigObj.h為剛才新建的單例類(lèi)
[ConfigObj shareInstance].gArgc = argc;
[ConfigObj shareInstance].gArgv = argv;
3夸赫、打開(kāi)AppDelegate.h文件菩帝,添加以下代碼
#import <UIKit/UIKit.h>
#include <UnityFramework/UnityFramework.h>
#include <UnityFramework/NativeCallProxy.h>
#import "ConfigObj.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate,UnityFrameworkListener, NativeCallsProtocol>
@property (strong, nonatomic) UIWindow *window;
@end
打開(kāi)AppDelegate.m文件,添加以下代碼
#import "AppDelegate.h"
UnityFramework* UnityFrameworkLoad()
{
NSString* bundlePath = nil;
bundlePath = [[NSBundle mainBundle] bundlePath];
bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"];
NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
if ([bundle isLoaded] == false) [bundle load];
UnityFramework* ufw = [bundle.principalClass getInstance];
if (![ufw appController])
{
// unity is not initialized
[ufw setExecuteHeader: &_mh_execute_header];
}
return ufw;
}
@interface AppDelegate ()
@property (nonatomic, strong) UnityFramework *ufw;
@end
@implementation AppDelegate
- (bool)unityIsInitialized { return [self ufw] && [[self ufw] appController]; }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self initUnityWithOptions:launchOptions];
});
return YES;
}
- (void)initUnityWithOptions:(NSDictionary *)launchOptions
{
[self setUfw: UnityFrameworkLoad()];
// Set UnityFramework target for Unity-iPhone/Data folder to make Data part of a UnityFramework.framework and uncomment call to setDataBundleId
// ODR is not supported in this case, ( if you need embedded and ODR you need to copy data )
[[self ufw] setDataBundleId: "com.unity3d.framework"];
[[self ufw] registerFrameworkListener: self];
[NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
[[self ufw] runEmbeddedWithArgc: [ConfigObj shareInstance].gArgc argv: [ConfigObj shareInstance].gArgv appLaunchOpts: launchOptions];
UIView *view = [[[self ufw] appController] rootView];
}
- (void)applicationWillResignActive:(UIApplication *)application { [[[self ufw] appController] applicationWillResignActive: application]; }
- (void)applicationDidEnterBackground:(UIApplication *)application { [[[self ufw] appController] applicationDidEnterBackground: application]; }
- (void)applicationWillEnterForeground:(UIApplication *)application { [[[self ufw] appController] applicationWillEnterForeground: application]; }
- (void)applicationDidBecomeActive:(UIApplication *)application { [[[self ufw] appController] applicationDidBecomeActive: application]; }
- (void)applicationWillTerminate:(UIApplication *)application { [[[self ufw] appController] applicationWillTerminate: application]; }
@end
集成后茬腿,你可能會(huì)發(fā)現(xiàn)呼奢,將unity作為你的子模塊啟動(dòng)時(shí),在加載unity的啟動(dòng)頁(yè)之前滓彰,會(huì)先加載一次原生的啟動(dòng)頁(yè)控妻,可以將SplashScreen.mm文件下ShowSplashScreen函數(shù)里下面的代碼注釋
UIStoryboard *storyboard = [UIStoryboard storyboardWithName: launchScreen bundle: [NSBundle mainBundle]];
// as we still support xcode pre-11 we must do this weird dance of checking for both sdk and runtime version
// otherwise it fails to compile (due to unknown selector)
#if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
if (@available(iOS 13.0, tvOS 13.0, *))
{
_controller = [storyboard instantiateInitialViewControllerWithCreator:^(NSCoder *coder) {
return [[UnityViewControllerStoryboard alloc] initWithCoder: coder];
}];
}
else
#endif
{
_controller = [storyboard instantiateInitialViewController];
}