做软件开发几年了,随着经验的增长,你会发现,很多时候遇到一些抓狂的问题,他并不是来源于技术本身的,而是来源于技术以外的一些周边。
比如说运维、安全、流程、自动化、编译构建啊等等。
我上一篇文章《Unity自动构建思考(1)|自动构建机器的选择经验和思考》中,提及了自动构建这件事情。在几年的Unity游戏开发工作中,其中一个让我非常抓狂的地方,就是使用Unity引擎进行iOS编译出包。
为什么说使用Unity编译iOS会很麻烦?
我们都知道,Unity游戏引擎,使用了Mono引擎作为它的脚本语言引擎,而苹果iOS开发的官方语言是Objective-C和Swift(或其它方式编译出来的静态库.a),编译工作,必须是在Xcode工程上进行的。
所以,要进行Unity的苹果编译,本质上,首先要把Unity的工程,包括脚本、资源等等,转换成Xcode工程。
(Unity编译iOS,实质是转换成Xcode工程)
这,就是麻烦的开始。
因为生成出的是Xcode工程,编译的Xcode工程,也就是说,你在Unity上的一些开发知识,就变得不再有用了,你进入了iOS开发领域。
来到iOS开发领域,就要接受iOS工程编译的洗礼,常见的坑有依赖库、签名、第三方插件集成几个问题。
依赖库,需要外挂脚本处理
在Unity5以前,比如Unity4,Unity有一个很蛋痛的设计,就是,它生成的那个Xcode工程,是不会帮你处理依赖的Framework的。
怎么理解这件事呢?
你比如说,游戏里面引用了一个第三方的库插件(比如极光推送),这个插件需要引用了一个iOS静态库(jpush.a文件),但是问题是,这个.a库可能依赖了一个UserNotifications.framework,Unity是并不知道你的.a静态库会对Framework有依赖的。
所以,生成出来的这一个Xcode工程,根本编译不过,在编译的阶段,它就会告诉你,缺少了对某个Framework的依赖。
那这事怎么办呢?一般来说,常见的解决方法是使用一些开源的Xcode工程修改工具,在Xcode编译工程出来以后,对这个Xcode工程进行依赖库的修改和补充。
比如我喜欢用Python写成的mod-pbxproj;有的人喜欢Ruby写成的XcodeProj,功能更强大。
一般自动化的Unity编译,使用BuidPipeline.BuildPlayer命令后,这些工具就会出场,对Xcode进行工程层面的一些修改。
常见的操作就是诸如对一些依赖库(Framework、Library)和iOS功能(比如GameCenter/In-App-Purchase等)进行添加、修改,尽可能做到Unity生成的工程,在经过脚本修改后,直接就编译成功,减少人工干预。
签名,各种小问题
苹果对安全的投入是非常大的,从它的签名机制就能看出来了——太tm麻烦了。
(Xcode 8中“自动管理签名"功能)
其中有一次,Xcode 7升级到Xcode 8,增加了“自动管理签名”的功能。在官方看来,他在这一个签名流程进行了一些改进,还有优化简化了这件事情。
但是对于当时我来说,还有公司的其他小伙伴来说,简直崩溃。因为之前的手动签名流程,都不再有效了。
这个“自动管理签名”的功能,要求使用者,在iOS Developer登录后,就能方便地进行Xcode的打包签名管理。
问题是,实际的公司作业流程中,iOS证书一般是由发行方(运营商)进行颁发,并把证书文件发往研发方(游戏研发)。出于发行方的保密,研发方在打包iOS最终包时,使用的是具体的证书参数设置,而没有具体的iOS Developer登录权限。
对于这种流程,“自动管理签名”这个功能,其实是没啥作用的。你需要把它关闭掉,并且手动设置。起初为了达到这功能,我把开源项目mod-pbxproj也进行了一点小改,以达到“关闭自动管理签名”这个功能。
(iOS签名这件事,有挺不简单的一整套安全体系,刚开始挺折腾的,你可以上网去搜索更多关于iOS签名的内部实现机制)
第三方SDK的坑
Unity游戏开发,往往是多个渠道方同时发布的。比如说,我们的游戏在5个国家有语言版本,在5个国家有独立合作的发行商,对应着5个发行商SDK...
游戏开发时,发行商SDK接入是一件非常的麻烦吃力不讨好的事情,十分的折腾。所以这时候就有很多的“中介式SDK”的出现,比如我用过的有棱镜、西瓜sdk,简单来说,就是只要接入一次SDK,剩下的无数其它SDK接入的问题,都可以交由它们进行自动处理,节省成本。
尽管如此,但是有时候,也避免不了,需要对Xcode工程的Objective-C代码进行修改或依赖库处理。
比方说,上一个游戏,就需要加一个Facebook分享的功能。这个功能,“中介SDK”并没有提供,那就只能自己对Xcode工程的部分Objective-C代码进行定制了。
我们知道,Unity编译iOS的本质,是生成一个Xcode工程。那就是说,每一次的Unity编译,都会生成一个Xcode工程。
同样就是说,你对一个Xcode工程进行部分代码定制后,再进行一次Unity编译,这些部分定制代码就会消失。
像前面所说,在Unity编译后,mod-pbxproj这些Xcode工程修改工具,就会出场,对Xcode工程进行自动化的匹配修改。但是一些第三方SDK的接入,可就不是修改依赖库那么简单了,它们经常需要对Objective-C语言的文件进行修改,添加一些Objective-C代码。
这时候我一般的做法是,使用Python脚本,进行代码的“搜索-替换”......
后记
其实在Unity 5新版中,对iOS依赖库的处理能在Unity编辑器进行,Unity引擎在iOS编译这方面其实还是一直有进步的。
之所以觉得Unity的iOS编译麻烦,可能是因为我自己比较强调自动构建的全自动化,我发现有不少的其他项目组,一般是打出Xcode工程后,安排人手进行一些手动的操作,比如插代码、处理依赖库、接入第三方SDK、导出IPA等,都是通过在mac系统里的鼠标人为操作实现的。
虽然我所在的项目实现了全自动产出IPA,但是经常有一些外部因素会影响到最终的IPA文件生成,比如说....iOS签名过期、系统版本更新、第三方SDK版本更新,都会影响到最终IPA的全自动产出。
这些外部因素影响到自动构建的特殊情况,有时确实无可奈何。要不就追加自动化的处理代码,要不就放弃这部分自动,进行人工操作呗。
一个即将离开Unity界的程序员。算是写给交接工作的同学,同学你看到了吗。