iOS项目集成Unity详细教程

前言


Unity发布到iOS平台得到的是一个Xcode工程,这个Xcode工程可以直接编译运行,但是我们现在已经有个iOS项目了,Unity是作为项目的一部分,所以需要在已有的Xcode工程中集成Unity。
环境:Xcode 9 + Unity 2017。

Unity导出时设置图形API不要用Metal,因为我发现按我的集成方法使用Metal会崩,不知道什么原因。
api设置

下图为Unity发布得到的Xcode工程,红框中是我们要添加到自己的项目的文件夹。

需要添加的文件夹

步骤


1. 将Unity Xcode工程中的Classes和Libraries文件夹添加到工程中,注意勾选Copy items if needed,并选择Create groups

(这里我把Unity相关的文件都放入了Unity逻辑文件夹下,便于管理,话说Xcode 9终于默认创建物理文件夹了,老版本默认创建的是逻辑文件夹,逻辑文件夹只在Xcode中显示,在Finder中是看不到的)

Create groups

2. 将Unity Xcode工程中的Data文件夹添加到工程中,注意勾选Copy items if needed,并选择Create folder references

Create folder references

3. 删除Libraries下的libil2cpp,注意删除选项选择Remove References

libil2cpp

4. 删除Classes下的main.mm,直接Move To Trash,因为我们使用自己的main文件,main.mm中有一些Unity初始化的代码,我将其放入了UnityController.mm中,下文会说到。

5. 接下来修改项目的Build Settings,建议从Unity导出的Xcode工程的Build Settings中复制,不容易出错。

  • Other Linker Flags:
    $(inherited) -weak_framework CoreMotion -weak-lSystem

Other Linker Flags

  • Header Search Paths:
    $(inherited) “$(SRCROOT)/Classes” “$(SRCROOT)” $(SRCROOT)/Classes/Native $(SRCROOT)/Libraries/bdwgc/include $(SRCROOT)/Libraries/libil2cpp/include
    (如果你在第一步中建立的是物理文件夹,就把物理文件夹路径加进去)

Header Search Paths

  • Other C Flags:
    $(inherited) -DINIT_SCRIPTING_BACKEND=1 -fno-strict-overflow -DRUNTIME_IL2CPP=1

Other C Flags

  • Precompile Prefix Header:YES
    Prefix Header:Classes/Prefix.pch

Prefix

  • 在User-Defined添加以下属性,注意版本号换成自己的Unity版本号。
    GCC_THUMB_SUPPORT:NO
    GCC_USE_INDIRECT_FUNCTION_CALLS:NO
    UNITY_RUNTIME_VERSION:2017.1.0p5
    UNITY_SCRIPTING_BACKEND:il2cpp

添加User-Defined

添加完成

  • 添加框架,建议按自己导出的Unity-Xcode工程来,不同人的可能不一样,注意Optional(弱引用)。

framework

6. 创建类UnityController.mm继承自UnityAppController.mm

Classes下的UnityAppController类就是用于启动、显示Unity界面的类,不过它创建的是全屏界面,而我们需要的是让Unity只占整个界面的一部分,所以需要修改;我的方法是自己建一个类继承它,从而自定义自己的方法,代码如下:

//
//  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
//

#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 <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() {
    @autoreleasepool
    {
        UnityInitStartupTime();
        UnityInitTrampoline();
        UnityInitRuntime(0, NULL);


        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();


        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

7. 打开AppDelegate.m文件,添加UnityController属性,并在应用程序加载完成后创建对象。

AppDelegate.m

8. 打开Classes下的UnityAppController.h文件,修改GetAppController()函数。

inline UnityAppController*  GetAppController()
{
//    return (UnityAppController*)[UIApplication sharedApplication].delegate;
    return (UnityAppController *)[[UIApplication sharedApplication] valueForKeyPath:@"delegate.unityController"];
}

9. 为ViewController.m添加代码。

//
//  ViewController.m
//  


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

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button.frame = CGRectMake(60, 60, 80, 40);
    [button setTitle:@"开启Unity" forState:UIControlStateNormal];
    [self.view addSubview:button];
    [button addTarget:self action:@selector(clickHandler:) forControlEvents:UIControlEventTouchUpInside];

    UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    button1.frame = CGRectMake(160, 60, 80, 40);
    [button1 setTitle:@"暂停Unity" forState:UIControlStateNormal];
    [self.view addSubview:button1];
    [button1 addTarget:self action:@selector(clickHandler1:) forControlEvents:UIControlEventTouchUpInside];

    // 供Unity显示的View
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 150, 300, 300)];
    [view setBackgroundColor:[UIColor grayColor]];
    [view setTag:22];
    [self.view addSubview:view];

}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void) clickHandler:(id)sender
{
    [[UnityController instance] initUnity];
    [UnityController instance].playView.frame = [self.view viewWithTag:22].bounds;
    [[self.view viewWithTag:22] addSubview:[UnityController instance].playView];
}

- (void) clickHandler1:(id)sender
{
    if ([[UnityController instance] isPaused]) {
        [[UnityController instance] startUnity];
    }
    else {
        [[UnityController instance] pauseUnity];
    }
}

@end

好了,大功告成!Demo的运行效果如下:

Demo

后记


  • 网上有些教程说还要添加MapFileParser.sh文件,我看了这个文件是用于Unity托管堆栈跟踪的,暂时用不到,就不添加了。
  • 启动Unity后只能暂停/运行Unity,没有找到让Unity完全销毁的方法,这样的话内存占用肯定是不会减少了,CPU在暂停的时候还是占用很小的,如果有人有让Unity完全销毁的办法,欢迎指教。
  • Demo工程下载链接:
    https://download.csdn.net/download/suwk1009/10351477

猜你喜欢

转载自blog.csdn.net/suwk1009/article/details/78500684