- 說明
- 參考網(wǎng)址unity導(dǎo)出工程導(dǎo)入到iOS原生工程中詳細(xì)步驟
- 我現(xiàn)在有兩個(gè)工程,一個(gè)是從unity中導(dǎo)出的iOS工程,一個(gè)是自己創(chuàng)建的unity工程,如下所示:

0.png
- 導(dǎo)入unity模塊
- unity中導(dǎo)出的iOS工程是這樣子的:

1.png
- 將Classes文件夾、Data文件夾、Libraries、MapFileParser、MapFileParser.sh這五個(gè)復(fù)制到自己的iOS工程中

2.png
- 將Data、QCAR(QCAR文件在Data->Raw里面)文件夾以以下方式添加

3.png
- 將Classes、Libraries以以下方式添加

4.png
- 添加引用庫文件
打開從unity中導(dǎo)出的iOS工程,查看其引用的庫文件,如圖所示

5.png
將上面的庫文件,添加到自己的工程中
- 關(guān)閉bitcode
在build settings中關(guān)閉bitcode -
在 other Linker Flags 添加
-weak_framework CoreMotion -weak-lSystem
14.png - 在Other C Flags中添加 -DINIT_SCRIPTING_BACKEND=1,如圖所示

6.png
- 添加pch文件

7.png
如果項(xiàng)目中有多個(gè)pch文件,請將其合并成一個(gè)pch文件,再添加
- 在Header Search Paths 添加頭文件引用
打開從unity導(dǎo)出的工程,查看Header Search Paths中添加的頭文件,如圖所示

8.png
將其添加到自己工程的Header Search Paths中
- 在Library Search Path 中添加庫引用
打開從unity導(dǎo)出的工程,查看ibrary Search Path中添加的頭文件,如圖所示

9.png
將其添加到自己工程的ibrary Search Path中
- 在user-Defined 添加
打開從unity導(dǎo)出的工程, 在build settings中查看user-Defined,如圖所示

10.png
并將其添加到自己項(xiàng)目的 user-Defined中

11.png
- 添加Run Script
打開從unity導(dǎo)出的工程, 在build phases中查看Run Script,如圖所示:

12.png
- 更改main.m文件為main.mm文件。將Classes中的main.mm中的內(nèi)容復(fù)制,粘貼到原來工程的main.mm中。然后刪除Classes中的main文件。
#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#import "AppDelegate.h"
#include <mach/mach_time.h>
#include <csignal>
// Hack to work around iOS SDK 4.3 linker problem
// we need at least one __TEXT, __const section entry in main application .o files
// to get this section emitted at right time and so avoid LC_ENCRYPTION_INFO size miscalculation
static const int constsection = 0;
void UnityInitTrampoline();
// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
const char* AppControllerClassName = "UnityAppController";
int main(int argc, char* argv[])
{
signed long long startTime = mach_absolute_time();
@autoreleasepool
{
UnitySetStartupTime(startTime);
UnityInitTrampoline();
UnityInitRuntime(argc, argv);
RegisterMonoModules();
NSLog(@"-> registered mono modules %p\n", &constsection);
RegisterFeatures();
// iOS terminates open sockets when an application enters background mode.
// The next write to any of such socket causes SIGPIPE signal being raised,
// even if the request has been done from scripting side. This disables the
// signal and allows Mono to throw a proper C# exception.
std::signal(SIGPIPE, SIG_IGN);
//UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
return 0;
}
#if TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR
#include <pthread.h>
extern "C" int pthread_cond_init$UNIX2003(pthread_cond_t *cond, const pthread_condattr_t *attr)
{ return pthread_cond_init(cond, attr); }
extern "C" int pthread_cond_destroy$UNIX2003(pthread_cond_t *cond)
{ return pthread_cond_destroy(cond); }
extern "C" int pthread_cond_wait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex)
{ return pthread_cond_wait(cond, mutex); }
extern "C" int pthread_cond_timedwait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{ return pthread_cond_timedwait(cond, mutex, abstime); }
#endif // TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR
并將main函數(shù)中的 UIApplicationMain方法修改為UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- 在Classes的UnityAppController.mm中的- (void)applicationDidBecomeActive:(UIApplication*)application方法中,注釋掉[self performSelector: @selector(startUnity:) withObject: application afterDelay: 0]方法,這樣做的目的,是為防止unity的內(nèi)容隨著應(yīng)用的啟動(dòng),而啟動(dòng);
- (void)applicationDidBecomeActive:(UIApplication*)application
{
::printf("-> applicationDidBecomeActive()\n");
[self removeSnapshotView];
if (_unityAppReady)
{
if (UnityIsPaused() && _wasPausedExternal == false)
{
UnityWillResume();
UnityPause(0);
}
UnitySetPlayerFocus(1);
}
else if (!_startUnityScheduled)
{
_startUnityScheduled = true;
// [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
}
_didResignActive = false;
}
- 修改appDelegate.h
#import <UIKit/UIKit.h>
@class UnityAppController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong,nonatomic)UINavigationController *navi;
@property (strong, nonatomic) UIWindow *window;
@property (strong,nonatomic)UnityAppController* unityAppController;
- (void)shouldAttachRenderDelegate;
@end
- 將appDelegate.m修改為appDelegate.mm,并對其內(nèi)容進(jìn)行修改
#import "AppDelegate.h"
#import "ViewController.h"
#import "UnityAppController.h"
@interface AppDelegate ()
@end
extern "C" void VuforiaSetGraphicsDevice(void* device, int deviceType, int eventType);
extern "C" void VuforiaRenderEvent(int marker);
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
BOOL returnBool;
if (_unityAppController == nil) {
_unityAppController = [[UnityAppController alloc] init];
}
[_unityAppController application:application didFinishLaunchingWithOptions:launchOptions];
ViewController *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]instantiateViewControllerWithIdentifier:@"viewVC"];
self.navi = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController=self.navi;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
[_unityAppController applicationWillResignActive:application];
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
[_unityAppController applicationDidEnterBackground:application];
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
[_unityAppController applicationWillEnterForeground:application];
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[_unityAppController applicationDidBecomeActive:application];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
[_unityAppController applicationWillTerminate:application];
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)shouldAttachRenderDelegate
{
UnityRegisterRenderingPlugin(&VuforiaSetGraphicsDevice, &VuforiaRenderEvent);
}
@end
- 在Classes的UnityAppController.h中,更改GetAppController()方法(當(dāng)然要先引入#import "AppDelegate.h"
inline UnityAppController* GetAppController()
{
AppDelegate *dele = (AppDelegate *)[UIApplication sharedApplication].delegate;
return (UnityAppController *)dele.unityAppController;
}
- 在UnityAppController.mm中重寫- (void)shouldAttachRenderDelegate方法
- (void)shouldAttachRenderDelegate {
AppDelegate *deleg = [UIApplication sharedApplication].delegate;
[deleg shouldAttachRenderDelegate];
}
ok,通過上面的步驟,我們就已經(jīng)將unity的功能導(dǎo)入自己的項(xiàng)目中,接下來做的,就是點(diǎn)擊按鈕,啟動(dòng)unity模塊
Main.storyBoard的ViewController創(chuàng)建一個(gè)UIButton,并將Storyboard ID設(shè)為“viewVC”

13.png
- 在ViewController.m實(shí)現(xiàn)其點(diǎn)擊事件(記得倒入#import "UnityAppController.h",#import "AppDelegate.h")
#pragma mark----打開AR-----
- (IBAction)doOpenAR:(id)sender {
static bool flg=NO;
if (flg) {//如果不是第一次啟動(dòng),則之間切換window
[GetAppController() restartUnity];
}
else//如果是第一次點(diǎn)擊按鈕,則需要啟動(dòng)unity
{
[GetAppController() preStartUnity];
[GetAppController() startUnity:[UIApplication sharedApplication]];
[UnityGetMainWindow() makeKeyAndVisible];
flg=YES;
}
}
- 在UnityAppController.h中申明-(void) restartUnity,在UnityAppController.mm中實(shí)現(xiàn)
-(void) restartUnity
{
_window.rootViewController=_rootController;
[_window makeKeyAndVisible];
[UnityGetMainWindow() makeKeyAndVisible];
if (_didResignActive) {
UnityPause(false);
_didResignActive=NO;
}
}
- 這樣,我們就能啟動(dòng)unity的模塊了,但是,關(guān)閉或隱藏unity模塊,也需要我們操作。
- 我們需要在UnityAppController.mm中的- (void)startUnity:(UIApplication*)application方法里,添加一個(gè)按鈕
- (void)startUnity:(UIApplication*)application
{
NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");
UnityInitApplicationGraphics(UNITY_FORCE_DIRECT_RENDERING);
// we make sure that first level gets correct display list and orientation
[[DisplayManager Instance] updateDisplayListInUnity];
UnityLoadApplication();
Profiler_InitProfiler();
[self showGameUI];
UIButton *button=[UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(doHideenUnity) forControlEvents:UIControlEventTouchUpInside];
button.frame=CGRectMake(0, 0, 50, 50) ;
[button setTitle:@"退出" forState:UIControlStateNormal];
//[button setBackgroundColor:[UIColor purpleColor]];
[_rootView addSubview:button];
[self createDisplayLink];
UnitySetPlayerFocus(1);
}
- 并實(shí)現(xiàn)按鈕的點(diǎn)擊事件
-(void)doHideenUnity
{
UnityPause(true);
_didResignActive=YES;
Profiler_UninitProfiler();
AppDelegate *delet=[UIApplication sharedApplication].delegate;
[delet.window makeKeyAndVisible];
}
- ok,完成
可能遇到了問題
- 重復(fù)的main.mm,記得刪除Classes文件的main.mm
- 如果有多個(gè)pch文件,記得進(jìn)行合并
