前言

现在的需求是合并IOS工程和unity3d工程需要。
目前的方案有两种:
1.把unity导出的ios工程嵌入IOS原生工程
2.把IOS工程嵌入unity导出的IOS原生工程

两种方式各有优劣,下面来分析下
1.unity导出工程包含几个类库,脚本,需要修改一部分配置,
如果ios原生工程是个空项目或者就几个简单页面,那么毫无疑问,是把原生的ios工程导入unity工程,这个非常简单,给修改main.mm的启动,再添加一个ViewController页面跳转到Unity即可。
如果IOS工程也很复杂,比如需要导入地图,聊天,支付等一系列SDK,那么就代表原生工程也需要配置很多东西,这种情况下,建议把unity工程嵌入到ios原生工程,因为unity导出工程是有一个模板的,我们照着改参数相对来说没有想象中那么复杂(其实也很复杂,坑我两天时间),所以接下来呢,我就详细阐述一下unity工程嵌入IOS原生工程的步骤。

1.准备工作

1.准备一个unity工程(建议先用空工程测试),导出ios工程,Target SDK,选择Device SDK,真机调试(模拟器也没问题,而且比真机好跑通,但建议最终以真机为准),导出后先在xcode试运行,确保没有编译错误。

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity


2.准备一个IOS原生工程(建议先用空工程测试),自己学习下ios的基本知识,添加一个按钮,要写ios跳转unity场景的点击事件,然后先试运行,确保没有错误。

unity3d 发布到ios手机 unity导出ios怎么在手机安装_IOS_02

2.合并类库

1.拷贝unity工程文件到ios原生工程

打开unity导出工程目录,选择Class,Libraries文件夹和MapFileParser.sh文件,拖拽到ios原生工程中。选择:Copy Items if needed和Create groups

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_03


选择Data文件夹,拖拽到ios原生工程,选择:Copy Items if needed和Create folder refrences

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_04


导入完成后,工程目录如下图:

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_05


3.添加Framework

根据unity导出工程的Framework,以此添加到ios原生工程

快捷方式:直接拖拽复制,然后选中它们再勾选下Target Membership

添加libiconv.2.dylib动态库引用,没有.dylib文件,直接添加.tbd也行

添加完后的结构

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity嵌入IOS_06

3.合并main文件

1.移动main.mm文件,到ios工程文件夹下,复制main.m内容到main.mm下,然后删除main.m文件。

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity嵌入IOS_07

4.修改Build Phases

1.添加一个Run Script
2.点击+号,选择New Run Script Phase
3.粘贴MapFileParse.sh的路径(根据unity导出工程)

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_08

5.修改Build Setting

1.修改Build Options->Enable Bitcode:NO

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity_09


2.修改Linking->Other Linker Flags

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity_10


3.修改头文件引用和库引用(让ios能够找到unity的文件)

Search Paths->Header Search Paths

Search Paths->Library Search Paths

unity3d 发布到ios手机 unity导出ios怎么在手机安装_unity3d 发布到ios手机_11


unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_12


4.修改C和C++标记

Apple LLVM9.0 - Custom Compiler Flags-> Other C Flags

Apple LLVM9.0 - Custom Compiler Flags-> Other C++ Flags

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_13


unity3d 发布到ios手机 unity导出ios怎么在手机安装_IOS_14


5.添加Prefix.pch文件的引用,如果ios原工程也有这个文件,需要合并。

Apple LLVM9.0 - Language->Precompile Prefix Header:Yes

Apple LLVM9.0 - Language->Prefix Header:Classes/Prefix.pch

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_15

7.修改(搜索objc即可找到)

Apple LLVM9.0 - Preprocessing->Enable Strict Checking of objc_msgSend Calls:No

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_16


8.添加User-Defined,点击Build Setting的+号,选择Add User Defined Setting

User-Defind->GCC_THUMB_SUPPORT:NO

User-Defind->GCC_USE_INDIRECT_FUNCTION_CALLS:NO

User-Defind->UNITY_RUNTIME_VERSION:2018.4.10f0

User-Defind->UNITY_SCRIPTING_BACKEND:il2cpp

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity_17

6.注意事项

以上参数仅供参考,最终修改为什么值,要根据你的unity导出的xcode工程来定,因为每个人xcode版本和unity版本以及项目都不一样,不要太死板。我也是比对着参数找过来的。

7.可选修改

有几个参数,需要根据你的实际情况修改(如果上面的参数改完后,编译没有问题,就不用改了)
1.修改真机和模拟器的平台:

  • Architectures->Supported Platforms:iphoneos*

    2.修改C和C++语言的版本:
  • C Language Dialect -> C99[-std=c99]*
  • C++ Language Dialect ->C++11[-std=c++11]*

到此为止,应该能编译成功了。
如果还有错误,不管是25个还是170个都不用慌,继续往下看错误修改

8.错误修改

1.DeviceSetting.mm文件内错误

解决方案:方法添加一个return 0;

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_18


2.Underfined Symbols for architecture arm64:

“xxxxx”,refrenced from:DynamicLibEngineAPI.o

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity嵌入IOS_19


unity3d 发布到ios手机 unity导出ios怎么在手机安装_unity3d 发布到ios手机_20


解决方案:打开Build Phases,搜索DynamicLibEngineAPI,然后选择-号删除

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_21


3.dyld:Library not loaded:@executable_path/lib/libiPhone-lib.a

Reason:image not found

解决方案:Build Phases找那个添加Copy Files,然后添加libiPhone-lib.a

unity3d 发布到ios手机 unity导出ios怎么在手机安装_IOS_22

9.修改AppDelegate

与其说修改,不如所示直接替换

AppDelegate.h

//
//  AppDelegate.h
//
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UIWindow *unityWindow;

- (void)showUnityWindow;
- (void)hideUnityWindow;

@end

AppDelegate.m

//
//  AppDelegate.m
//

#import "AppDelegate.h"
#import "ViewController.h"
#import "UnityController.h"

@interface AppDelegate ()
@property(nonatomic,strong)UnityAppController *unityController;
@end

@implementation AppDelegate

//unity 交互unity
-(UIWindow *)unityWindow{
    return  UnityGetMainWindow();
}

-(void)showUnityWindow{
    [self.unityWindow makeKeyAndVisible];
}

-(void)hideUnityWindow{
    [self.window makeKeyAndVisible];
}

-(void)addUnityView{
    [self.window addSubview:self.unityController.unityView];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    //unity viewcontroller
    _unityController = [[UnityAppController alloc] init];
    [_unityController application:application didFinishLaunchingWithOptions:launchOptions];
    
    //ios window
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    
    //ios viewcontroller
    ViewController *viewController = [[ViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];
    
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)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 throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    
    [_unityController applicationWillResignActive:application];
}

- (void)applicationDidEnterBackground:(UIApplication *)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.
    
    [_unityController applicationDidEnterBackground:application];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    
    [_unityController applicationWillEnterForeground:application];
}

- (void)applicationDidBecomeActive:(UIApplication *)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.
    
    [_unityController applicationDidBecomeActive:application];
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    
    [_unityController applicationWillTerminate:application];
}
@end

10.添加UnityAppController

UnityController是UnityAppController的子类,我们添加UnityController的目的是为了避免修改UnityAppController,而是直接去扩展,每次合并工程,直接拷贝过来即可。
UnityController.h

//
//  UnityController.h
//

#import "UnityAppController.h"

@interface UnityController : UnityAppController

@property (nonatomic, readonly, weak) UIView *playView;  /* 展示Unity的view */

+ (instancetype)instance;

- (void)initUnity;

- (void)pauseUnity;

- (void)startUnity;

- (BOOL)isPaused;

@end

UnityController.mm

//
//  UnityController.mm
//  UnityDemo
//

#import "UnityController.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 <mach/mach_time.h>
#include <csignal>

@interface UnityController()

@property (nonatomic, assign) BOOL isInitUnity;

@end

@implementation UnityController

+ (instancetype)instance {
    return (UnityController *)[[UIApplication sharedApplication] valueForKeyPath:@"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;
}

- (UIView *)playView {
    
    return self.unityView;
    
}

static const int constsection = 0;

void UnityInitTrampoline();

//void initMain(int argc, char* argv[])

void initMain(int argc, char* argv[]) {
#if UNITY_USES_DYNAMIC_PLAYER_LIB
    SetAllUnityFunctionsForDynamicPlayerLib();
#endif
    
    UnityInitStartupTime();
    @autoreleasepool
    {
        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);
        
    }
}

- (void)initUnity {
    
    if (!self.isInitUnity) {
        
        initMain(0,NULL);
        
        if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
            [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
        
        UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]);
        [self selectRenderingAPI];
        [UnityRenderingView InitializeForAPI: self.renderingAPI];
        _window = nil;
        _unityView      = [self createUnityView];
        
        
        [DisplayManager Initialize];
        _mainDisplay    = [DisplayManager Instance].mainDisplay;
        [_mainDisplay createWithWindow: _window andView: _unityView];
        
        [super applicationDidBecomeActive:[UIApplication sharedApplication]];
        
        self.isInitUnity = YES;
    }
    
}

- (void)pauseUnity {

    //[self applicationWillResignActive:[UIApplication sharedApplication]];
    UnityPause(1);
}

- (void)startUnity {
    
    //[self applicationDidBecomeActive:[UIApplication sharedApplication]];
    UnityPause(0);
}

- (BOOL)isPaused {
    if (UnityIsPaused() == 1) {
        return YES;
    }
    else {
        return NO;
    }
}

- (void)appWillEnterForeground:(NSNotification *)notification {
    [self applicationWillEnterForeground:[UIApplication sharedApplication]];
}

- (void)appDidBecomeActive:(NSNotification *)notification {
    if (nil == self.unityView) {
        return;
    }
    [self applicationDidBecomeActive:[UIApplication sharedApplication]];
}

- (void)appWillResignActive:(NSNotification *)notification {
    [self applicationWillResignActive:[UIApplication sharedApplication]];
}

- (void)appWillTerminate:(NSNotification *)notification {
    [self applicationWillTerminate:[UIApplication sharedApplication]];
}

- (void)appDidReceiveMemoryWarning:(NSNotification *)notification {
    [self applicationDidReceiveMemoryWarning:[UIApplication sharedApplication]];
}

@end

11.从IOS跳转到Unity

给oc的页面添加一个点击事件,这个需要一点IOS的基础,请自行百度吧

我这里,给ViewController.xib添加了一个按钮,并绑定了点击事件如下

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity合并IOS_23

- (IBAction)OnButtonClick:(id)sender {
    NSLog(@"Open Unity Cick");
    
    //进入unity场景为小窗口
    //UIView* _view = [(AppDelegate *)[UIApplication sharedApplication].delegate unityWindow].rootViewController.view;
    //_view.frame = CGRectMake(0, 0, 200, 300);
    //[self.view addSubview:_view];
    
    //弹出unity场景为全屏幕
    [(AppDelegate *)[UIApplication sharedApplication].delegate showUnityWindow];
    UnityPause(false);
}

最后全部搞定,运行成功

unity3d 发布到ios手机 unity导出ios怎么在手机安装_Unity_24

希望这篇文章能够对你有所帮助—passion