Unity5.6与Xcode8.3原生工程整合交互

环境

  • Unity5.6.0f3个人免费版。
  • Xcode8.3.2。

参考

  • the_nerd.be上的这篇文章,还带视频。
  • Unity官方参考文档的iOS部分,这里有很多资料,包括Unity导出Xcode工程的目录结构以及在Unity和iOS交互问题等。

需求

  • Unity需求较多,Native需求较少:直接在Unity导出的Xcode工程中开发。
  • Unity需求较多,Native需求只有一两个页面:可以直接将写好的OC代码文件放到Unity的Assets/Plugins/文件夹里。
  • Unity需求较少,Native需求较多:需要将Unity导出的Xcode工程整合入原生的Xcode工程,也是本文接下来的内容。

实战

导出Unity工程

File->Build & Run

在这里添加场景,然后选择Player Settings进入设置。主要设置以下三项,其他按需求来。

导出后的位置如下图,我把两个工程放在同一个根目录下,这样对后期比较方便。

配置Native工程

复制文件

这一步最复杂,不过可以参考上面的视频教程,有些地方可能由于Unity和Xcode的版本需要变动一下。 首先拖入Unity工程的ClassesLibraries,为了确保以后维护起来方便,请不要勾选copy,如下图。

这样做的好处是,只保留文件的引用而不复制文件,减少依赖关系。 接下来修改一些文件:

  • Classes/main.mm文件的内容全部复制到你的main.m文件里,并且把main.m改名为main.mm,然后把里面的UnityAppController改成你的AppDelegate
  • 新建PrefixHeader,把Classes/Prefix.pch文件的内容全部复制到新建的refixHeader里。

接下来删除一些没用的文件,这里的所有删除都只是删除引用。

要删除的内容如下:

  • Classes/main.mm。
  • Classes/Prefix.pch。
  • Classes/Native下的所有.h文件,可以在下方的Filter过滤器里输入.h来过滤。
  • Libraries/libil2cpp文件夹

Build Setting

  • Build Setting里的Linking,Apple LLVM都按照Unity导出的工程来设置。
  • 添加User-Defined字段,也和Unity导出的工程一致。(在最上面有个+号)
  • Prefix Header如下设置。

如果有自己的头文件需要包含,需要放在如下位置:

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
#endif
  • Header Search Paths添加到Unity工程的引用。
  • Library Search Paths只需要添加一行${SRCROOT}/../Unity2iOS/Libraries指向Unity工程的Libraries目录。

Build Phase

  • 添加2行Run Script
rm -rf "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/DATA"
cp -Rf "$PROJECT_DIR/../Unity2iOS/Data" "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Data"

注意修改其中的目录到自己的Unity工程。

  • Link Binary With Libraries按Unity工程一个个添加,其中Libiconv.2.dylib,在Xcode8中已经找不到,从/usr/lib中找到然后拖进去,注意optional的设置。
  • 如果你是参考视频教程的话,视频中还要添加-fn-objc-arc,这一步千万不要添加,不然Build成功以后运行也会失败。可能是由于Unity版本导致的。

开始Build

到现在为止如果配置完全正确的话。是这个Build成功的,注意如果Unity导出的时候选择DeviceSDK的话,只能在真机上Build,选择模拟器就只能在模拟器上Build。

可能遇到的错误

有很多啦,比较Dirty的一个就是libiPhone-lib.a not found,在Build Phase里的Link Binary With LibrarieslibiPhone-lib.a移除再添加,然后clean一下就好了,视频里有说。 如果还有其他问题也都可以一个个解决,千万不要放弃。

运行Unity界面

Unity界面存在于UnityAppController.window里,因此只需要控制AppDelegate.windowUnityAppController.window的显示顺序就行,UIWindow本质就是UIView,因此可以直接使用hide等方法,只是要加上[window makeKeyAndVisable]方法。 另外要在App的生命周期方法里调用UnityAppController对应的周期方法。

- (void)applicationWillResignActive:(UIApplication *)application {
    [self.unityController 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 {
    [self.unityController 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 {
    [self.unityController 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 {
    [self.unityController 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 {
    [self.unityController applicationWillTerminate:application];
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

别忘了在DidFinishLaunch里给self.unityController赋值。 运行会发现有运行期错误,UnityAppControllerGetAppController()方法得到了nil,修改如下。

inline UnityAppController*  GetAppController()
{
    return [(AppDelegate*)[UIApplication sharedApplication].delegate unityController];
}

显示Unity和隐藏Unity界面最简单的方法。

//显示
        ApplicationDelegate.unityWindow.hidden = NO;
       [ApplicationDelegate.unityWindow makeKeyAndVisible];
//隐藏
        [ApplicationDelegate.window makeKeyAndVisible];
        ApplicationDelegate.unityWindow.hidden = YES;

Unity和Native交互

Unity调用iOS方法

  • C#中
[DllImport ("__Internal")]
private static extern void sim_showSelectTitleDialog();
  • iOS中,文件名:UnityFunctionManager.mm,注意是.mm,该文件需要放到unity的Plugins目录下,这样打包时会被自动打包到Xcode工程里。
extern "C" void sim_showSelectTitleDialog(char* title,char* msg){
    SIMUnityDialogManager* dialogManager = [SIMUnityDialogManager shareManager];
    [dialogManager vrb_showSelectTitleDialogWithTitle:title Message:msg];
    
}

这里建议在Native工程里实现一个单例SIMUnityDialogManager来实现该文件中的方法,这样就实现了具体的代码和接口分离,UnityFunctionManager.mm这个文件可以由Unity的同学负责,iOS同学只需要负责SIMUnityDialogManager里具体的方法实现。

iOS调Unity方法
  • iOS里,任意文件都可以
UnitySendMessage("GameObject", "Function",[sendMsg UTF8String]);  

第一个参数是GameObject,第二个参数是方法名,第三个参数是传输的数据。

  • Unity里
void Function(string message)  
{ 
//挂载在相应GameObject上的脚本
}

代码更新方案

由于Unity代码里需要更新维护,这样每次重新合并工程就很繁琐,并且不易做CI。 但是如果是通过以上教程实现工程合并,就会发现Unity工程和Native工程实际上并没有文件的关联,只存在文件的引用关系。每次Unity更新直接重新打包覆盖原来的工程就可以了。但是存在以下问题。

  • C#文件的增删 文件增删会导致导出的Classes文件夹中的文件的增删,因此在做CI的时候,可以考虑每次Unity工程更新都重新添加引用,但是要记得删除Classes/Native里的头文件。
  • UnityAppController被覆盖 每一次导出会重新生成UnityAppController文件,但是这个文件我们改了其中的GetAppController()方法,虽然只改了一行,但是每次这个文件都被覆盖。这里我的做法是,把该文件复制到Native工程目录,然后删除Classes里面该文件,这样每次重新打包Unity工程的时候只要多执行一行rm /Classes/UnityAppController就可以了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

IIS7 request routing 和load balancing module发布

Application Request Router (ARR) 已经正式发布,并可以免费下载, 支持所有版本的 IIS7。Application Reques...

22050
来自专栏進无尽的文章

代码管理| 手把手教你封装自己的静态库SDK

这里重点说下,怎样饮用Bundle中的图片资源。这里使用的是简写:@"JWJFramework.bundle/open"其中JWJFramework.bundl...

33010
来自专栏科技前线

升级Bash修复Shellshock漏洞

Shellshock,又称Bashdoor,是一个安全漏洞,于2014年9月12日被发现,直到9月24日被赋予了CVE编号CVE-2014-6271才得以控制。...

18920
来自专栏娱乐心理测试

SDWebImage源码解读(一)

SDWebImage 是目前最流行、使用最广泛的第三方图片处理框架,它不仅能够异步加载网络图片,还提供了一套图片缓存管理机制(内存缓存+磁盘缓存),功能非常强大...

6430
来自专栏偏前端工程师的驿站

.Net魔法堂:史上最全的ActiveX开发教程——自动更新、卸载篇

一、前言                               B/S模式的特点之一,客户端版本升级相对简单、快捷,适合产品的快速迭代。而ActiveX组...

20280
来自专栏我是攻城师

IntelliJ IDEA的光芒会盖过Eclipse吗

37250
来自专栏软件测试经验与教训

Fiddler用法整理

读书与实践是获取知识的主要渠道,学习的权力只掌握在每个人自己手中,让学习成为一种生活的习惯,这比任何名牌大学的校徽重要得多!

19910
来自专栏向治洪

React native开发中常见的错误

react native环境搭建请移步:react native环境搭建 这里说说react native创建完成之后,运行中出现的常见问题, 问题1: jav...

34360
来自专栏java闲聊

SpringBoot+Vue2.x登陆功能

33440
来自专栏Web 开发

用fiddler进行debug

昨晚在家里调试一个页面,弄了好一会,fiddler都没有抓到数据。最后Google之,才发现因为我等都装了SwitchySharp这类自动翻墙插件。导致Chro...

8700

扫码关注云+社区

领取腾讯云代金券