最近接到一個需求,要接入AR,給了個Vuforia官網(wǎng)地址阵难,還有AR開發(fā)部們同事給的demo(需求以demo為主)试溯,就直接開干,查了iOS原生接入Unity的資料传于,還是遇到了很多問題,在此總結(jié)記錄一下。
先看效果:目錄
配置依賴
Demo封裝
問題匯總
配置依賴
首先展箱,我們拿到的demo結(jié)構(gòu)是這樣的:
-
將 Classes 和 Libraries 文件夾,以及Framework文件夾中的 Vuforia.framework 拖入工程蹬昌,按下圖方式:
-------------------------------------------------->>>
-
將 Classes 和 Libraries 文件夾,以及Framework文件夾中的 Vuforia.framework 拖入工程蹬昌,按下圖方式:
-
將 Data 文件夾拖入工程,按下圖方式:
-------------------------------------------------->>>
-
將 Data 文件夾拖入工程,按下圖方式:
-
結(jié)果是這樣的(注意文件夾的路徑皂贩,導(dǎo)入 Header Search Paths按此參照):
-
結(jié)果是這樣的(注意文件夾的路徑皂贩,導(dǎo)入 Header Search Paths按此參照):
注:需將 Data / Raw 文件夾內(nèi)的Vuforia文件夾按步驟二添加到如下位置栖榨,否則會掃描無反應(yīng):
-------------------------------------------------->>>
- 4.將 Classes 文件夾 main.mm 內(nèi)容復(fù)制到自己項(xiàng)目(ARTest)中的 main.m 中 ,然后ARTest的 main.m 后綴改為 main.mm明刷,刪除 Classes 文件夾中的 main.mm (Move To Trash)有報(bào)錯先忽略, 代碼如下圖:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
static const int constsection = 0;
void UnityInitTrampoline();
const char* AppControllerClassName = "UnityAppController";
int main(int argc, char * argv[]) {
@autoreleasepool {
UnityInitStartupTime();
UnityInitTrampoline();
UnityInitRuntime(argc,argv);
RegisterMonoModules();
RegisterFeatures();
std::signal(SIGPIPE, SIG_IGN);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
-------------------------------------------------->>>
- 在build Setting --> other Link Flags 下做如下修改:
-weak_framework CoreMotion -weak-lSystem
-------------------------------------------------->>>
- 在build Setting --> Header Search Paths 下做如下修改:
"$(SRCROOT)/ARTest/Unity"
"$(SRCROOT)/ARTest/Unity/Classes"
"$(SRCROOT)/ARTest/Unity/Classes/Native"
"$(SRCROOT)/ARTest/Unity/Libraries"
"$(SRCROOT)/ARTest/Unity/Libraries/libil2cpp/include"
(不同項(xiàng)目需要按照文件在工程中的路徑來確定婴栽,本次路徑參照步驟三圖片所示路徑)
如圖所示:-------------------------------------------------->>>
- 在build Setting --> Other C Flags 做如下修改:
-DINIT_SCRIPTING_BACKEND=1 -fno-strict-overflow -DRUNTIME_IL2CPP=1 -DNET_4_0
如圖所示:-------------------------------------------------->>>
-
8.在build Setting --> Precompile Prefix Header做如下修改:如圖所示:
-------------------------------------------------->>>
-
9.在build Setting 做如下操作:
加入如下參數(shù):
GCC_THUMB_SUPPORT:NO
GCC_USE_INDIRECT_FUNCTION_CALLS:NO
UNITY_RUNTIME_VERSION:2018.2.3f1 <--這個值可能不一樣本冲,根據(jù)自己unity-demo來
UNITY_SCRIPTING_BACKEND: il2cpp
結(jié)果:- 10 build Setting --> bitcode關(guān)閉
-
11 添加依賴庫准脂,根據(jù)demo 來:
Demo封裝(以上編譯成功再做如下操作)
- 1.借鑒了iOS項(xiàng)目集成Unity詳細(xì)教程 檬洞,由于會在 UnityInitRuntime (0, NULL) 處奔潰狸膏,所以做了下調(diào)整。AppDelegate.h:
#import <UIKit/UIKit.h>
@class DTPUnityController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic , strong)UIWindow *unityWindow;
@property (nonatomic, strong) DTPUnityController *unityController;
- (void)showUnityWindow;
- (void)hideUnityWindow;
@end
AppDelegate.mm:
#import "AppDelegate.h"
#import "DTPUnityController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_unityController = [[DTPUnityController alloc]init];
return YES;
}
#pragma mark ---------------unity開啟與隱藏
- (UIWindow *)unityWindow
{
if (!_unityWindow) {
if (!UnityGetMainWindow()) {
_unityWindow = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
}else{
_unityWindow = UnityGetMainWindow();
}
}
return _unityWindow;
}
- (void)showUnityWindow
{
[self.unityWindow makeKeyAndVisible];
}
- (void)hideUnityWindow
{
[self.window makeKeyAndVisible];
[self.unityController pauseUnity];
}
@end
- 2.新建一個類 DTPUnityController 繼承自 UnityAppController 以下是 .h 內(nèi)容:
#import "UnityAppController.h"
@interface DTPUnityController : UnityAppController
+ (instancetype)instance;
- (void)initUnity;
- (void)pauseUnity;
- (void)startUnity1;
- (BOOL)isPaused;
@end
.m 內(nèi)容:
#import "DTPUnityController.h"
#import "UnityAppController.h"
#import "UnityAppController+ViewHandling.h"
#import "UnityAppController+Rendering.h"
#import "DisplayManager.h"
#import "UnityView.h"
#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
#import "AppDelegate.h"
@interface DTPUnityController()
@property (nonatomic, assign) BOOL isInitUnity;
@end
@implementation DTPUnityController
+ (instancetype)instance {
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
return delegate.unityController;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.isInitUnity = NO;
// 注冊Unity的事件
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
return self;
}
void UnityInitTrampoline();
- (void)initUnity {
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (!self.isInitUnity) {
[super applicationDidBecomeActive:[UIApplication sharedApplication]];
UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]);
[self selectRenderingAPI];
[UnityRenderingView InitializeForAPI: self.renderingAPI];
_window = delegate.unityWindow;
_unityView = [self createUnityView];
[DisplayManager Initialize];
_mainDisplay = [DisplayManager Instance].mainDisplay;
[_mainDisplay createWithWindow: _window andView: _unityView];
[self createUI];
[self preStartUnity];
self.isInitUnity = YES;
_unityView.back = ^{
[delegate hideUnityWindow];
};
}else{
[self startUnity1];
}
[delegate showUnityWindow];
}
- (void)pauseUnity {
UnitySendMessage("ARCamera", "Exit", ""); // 調(diào)Unity方法 退出模型 (與unity交互)
UnityPause(1);
}
- (void)startUnity1 {
UnityPause(0);
}
- (BOOL)isPaused {
if (UnityIsPaused() == 1) {
return YES;
}
else {
return NO;
}
}
-(void)applicationDidFinishLaunching:(UIApplication *)application{
}
- (void)appWillEnterForeground:(NSNotification *)notification {
[super applicationWillEnterForeground:[UIApplication sharedApplication]];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
if (nil == self.unityView) {
return;
}
[super applicationDidBecomeActive:[UIApplication sharedApplication]];
}
- (void)appWillResignActive:(NSNotification *)notification {
[super applicationWillResignActive:[UIApplication sharedApplication]];
}
- (void)appWillTerminate:(NSNotification *)notification {
[super applicationWillTerminate:[UIApplication sharedApplication]];
}
- (void)appDidReceiveMemoryWarning:(NSNotification *)notification {
[super applicationDidReceiveMemoryWarning:[UIApplication sharedApplication]];
}
@end
- 調(diào)整demo代碼添怔,掃描頁面添加返回按鈕callback(UnityView.h):
UnityView.mm 添加返回按鈕UI湾戳, addBackBtn 方法在initWithFrame中調(diào)用:
- (void)addBackBtn{
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
backBtn.frame = CGRectMake(20, 20, 70, 40);
[self addSubview:backBtn];
[backBtn setTitle:@"< 返回" forState:UIControlStateNormal];
[backBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[backBtn addTarget:self action:@selector(touchEvents) forControlEvents:UIControlEventTouchUpInside];
}
- (void)touchEvents{
if (self.back) {
self.back();
}
}
------------------------------------->>
-
4.在UnityAppController.h 中按如圖修改:
- 5.在ViewController中添加開啟AR代碼:
- (IBAction)startAR:(id)sender {
DTPUnityController *vc = [DTPUnityController instance];
[vc initUnity];
}
以上就是全部過程了。
問題匯總
- 第一次運(yùn)行demo 掃描后發(fā)現(xiàn)可以播放广料,但沒有聲音砾脑,音量也開到最大,以為是bug,找了好久艾杏,后面同事提示是否手機(jī)開了靜音韧衣,煞筆了。。畅铭。氏淑。
- 當(dāng)配置環(huán)境時,出現(xiàn)各種頭文件引用報(bào)錯硕噩,看是否引用了C C++代碼 是的話看下文件是否.mm 結(jié)尾假残,不是的話要相應(yīng)做修改 。另外炉擅,多數(shù)情況是Header Search Paths 路徑有誤辉懒,pch 文件合并,還有frameWork路徑帶來的報(bào)錯谍失,當(dāng)然還有build setting 中的 C Language 設(shè)置眶俩。
- 有些報(bào)錯還是無法解決,只能重新弄個工程集成了袱贮,xcode有時會抽風(fēng)仿便。
- 初始化在main 函數(shù)里面進(jìn)行了,然后沒找到返回按鈕的退出api攒巍,只能暫停嗽仪,然后調(diào)用unity 的移除模型api (若沒有需要與unity 工程師溝通)。
- 最大的坑就是好不容易集成了柒莉,掃描無反應(yīng)闻坚,掃描的圖片都是需要在官網(wǎng)先注冊的,然后有對應(yīng)的.dat .xml 文件兢孝,掃描不了就是這兩個文件路徑有問題窿凤,具體路徑設(shè)置代碼在哪,還希望廣大高手指出跨蟹,非常感激雳殊。
- 項(xiàng)目多Target時,Target名不能為中文窗轩,否則會 EXC_BAD_ACCESS 閃退夯秃,原因尚且不知。
---------------------------------后續(xù)補(bǔ)充內(nèi)容---------------------------------------------------
- 當(dāng)xcode集成的unity工程有改動需要替換時痢艺,只需將新導(dǎo)出的unity包內(nèi)的Data與Classes中的Native 2個文件夾替換即可仓洼。
- Xcode 11 中修改app名,需在info.plist中修改 bundle Display Name,直接在General 中修改Display Name,并且如果帶有中文的話堤舒,進(jìn)入unity 會在"initQCARiOS" 處出現(xiàn)EXC_BAD_ACCESS 的錯誤色建。原因是Xcode 11 在G general 中修改app名,會連同Product Name 一起修改舌缤。此外箕戳,引入Vuforia.framework及VuforiaDL.framework時某残,應(yīng)設(shè)置為Embed & Sign。
- AR 掃一掃出現(xiàn)黑屏漂羊,有可能是vuforia 購買的key 過期了驾锰。另外卸留,替換新key 后走越,還要檢查unity 中的設(shè)置: 1.去掉metal 只保留openGLES 2.0,2.ARCamera 中不勾選Allow MSAA耻瑟,勾選Allow HDR.
- 以上集成旨指,可能也會出現(xiàn)報(bào)錯,可以試著調(diào)整build-setting 中的 C Language Dialect 選項(xiàng)喳整,一般為GNU99谆构,來回切換下,可能會有效框都。
- 集成unity 后搬素,打包出來的ipa 包普遍很大,只保留arm64 架構(gòu)可以減少很多魏保。
- 編譯出現(xiàn)prinft_console 報(bào)錯熬尺,找到對應(yīng)代碼,注釋即可谓罗。