最近接到一个需求,要接入AR,给了个Vuforia官网地址,还有AR开发部们同事给的demo(需求以demo为主),就直接开干,查了iOS原生接入Unity的资料,还是遇到了很多问题,在此总结记录一下。
先看效果:目录
配置依赖
Demo封装
问题汇总
配置依赖
首先,我们拿到的demo结构是这样的:
-
将 Classes 和 Libraries 文件夹,以及Framework文件夹中的 Vuforia.framework 拖入工程,按下图方式:
-------------------------------------------------->>>
-
将 Classes 和 Libraries 文件夹,以及Framework文件夹中的 Vuforia.framework 拖入工程,按下图方式:
-
将 Data 文件夹拖入工程,按下图方式:
-------------------------------------------------->>>
-
将 Data 文件夹拖入工程,按下图方式:
-
结果是这样的(注意文件夹的路径,导入 Header Search Paths按此参照):
-
结果是这样的(注意文件夹的路径,导入 Header Search Paths按此参照):
注:需将 Data / Raw 文件夹内的Vuforia文件夹按步骤二添加到如下位置,否则会扫描无反应:
-------------------------------------------------->>>
- 4.将 Classes 文件夹 main.mm 内容复制到自己项目(ARTest)中的 main.m 中 ,然后ARTest的 main.m 后缀改为 main.mm,删除 Classes 文件夹中的 main.mm (Move To Trash)有报错先忽略, 代码如下图:
#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"
(不同项目需要按照文件在工程中的路径来确定,本次路径参照步骤三图片所示路径)
如图所示:-------------------------------------------------->>>
- 在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 做如下操作:
加入如下参数:
GCC_THUMB_SUPPORT:NO
GCC_USE_INDIRECT_FUNCTION_CALLS:NO
UNITY_RUNTIME_VERSION:2018.2.3f1 <--这个值可能不一样,根据自己unity-demo来
UNITY_SCRIPTING_BACKEND: il2cpp
结果:- 10 build Setting --> bitcode关闭
-
11 添加依赖库,根据demo 来:
Demo封装(以上编译成功再做如下操作)
- 1.借鉴了iOS项目集成Unity详细教程 ,由于会在 UnityInitRuntime (0, NULL) 处奔溃,所以做了下调整。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 内容:
#import "UnityAppController.h"
@interface DTPUnityController : UnityAppController
+ (instancetype)instance;
- (void)initUnity;
- (void)pauseUnity;
- (void)startUnity1;
- (BOOL)isPaused;
@end
.m 内容:
#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", ""); // 调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
- 调整demo代码,扫描页面添加返回按钮callback(UnityView.h):
UnityView.mm 添加返回按钮UI, addBackBtn 方法在initWithFrame中调用:
- (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];
}
以上就是全部过程了。
问题汇总
- 第一次运行demo 扫描后发现可以播放,但没有声音,音量也开到最大,以为是bug,找了好久,后面同事提示是否手机开了静音,煞笔了。。。。
- 当配置环境时,出现各种头文件引用报错,看是否引用了C C++代码 是的话看下文件是否.mm 结尾,不是的话要相应做修改 。另外,多数情况是Header Search Paths 路径有误,pch 文件合并,还有frameWork路径带来的报错,当然还有build setting 中的 C Language 设置。
- 有些报错还是无法解决,只能重新弄个工程集成了,xcode有时会抽风。
- 初始化在main 函数里面进行了,然后没找到返回按钮的退出api,只能暂停,然后调用unity 的移除模型api (若没有需要与unity 工程师沟通)。
- 最大的坑就是好不容易集成了,扫描无反应,扫描的图片都是需要在官网先注册的,然后有对应的.dat .xml 文件,扫描不了就是这两个文件路径有问题,具体路径设置代码在哪,还希望广大高手指出,非常感激。
- 项目多Target时,Target名不能为中文,否则会 EXC_BAD_ACCESS 闪退,原因尚且不知。
---------------------------------后续补充内容---------------------------------------------------
- 当xcode集成的unity工程有改动需要替换时,只需将新导出的unity包内的Data与Classes中的Native 2个文件夹替换即可。
- Xcode 11 中修改app名,需在info.plist中修改 bundle Display Name,直接在General 中修改Display Name,并且如果带有中文的话,进入unity 会在"initQCARiOS" 处出现EXC_BAD_ACCESS 的错误。原因是Xcode 11 在G general 中修改app名,会连同Product Name 一起修改。此外,引入Vuforia.framework及VuforiaDL.framework时,应设置为Embed & Sign。
- AR 扫一扫出现黑屏,有可能是vuforia 购买的key 过期了。另外,替换新key 后,还要检查unity 中的设置: 1.去掉metal 只保留openGLES 2.0,2.ARCamera 中不勾选Allow MSAA,勾选Allow HDR.
- 以上集成,可能也会出现报错,可以试着调整build-setting 中的 C Language Dialect 选项,一般为GNU99,来回切换下,可能会有效。
- 集成unity 后,打包出来的ipa 包普遍很大,只保留arm64 架构可以减少很多。
- 编译出现prinft_console 报错,找到对应代码,注释即可。