開發(fā) Unity3d 手機游戲的時候,不免要和第三方 SDK 打交道。于是總是需要實現(xiàn)自己的 AppController 來維護 SDK 的生命周期。
Unity3d 提供了一套插件機制,可以很方便地在項目中使用自己的 CustomAppController 繼承并重寫默認的 UnityAppController 的方法。
CustomAppController
在 Unity 插件目錄下創(chuàng)建以下文件:
?/path/to/unity/project/Assets/Plugins/iOS/CustomAppController.mm?
注意:
文件名必須是 ___AppController,前綴可自選,但不能省略;否則在 Build 項目的時候,會被移動到錯誤的目錄中去。
下面是集成BBase的入口,也是程序的入口
`//`
`// CustomAppController.m`
`// `
`#import "UnityAppController.h"`
`#import <BBase/BBaseManager.h>`
`@interface CustomAppController : UnityAppController`
`@end`
`IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)`
`@implementation CustomAppController`
`- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions`
`{`
` [super application:application didFinishLaunchingWithOptions:launchOptions];`
` [BBaseManager.sharedManager loadConfigureWithApplication:application registerPushNotificationLater:YES itunesConnectSharedSecret:@"" needReceiptVerification:YES gdprAppName:@"" gdprAgreementLink:@"" gdprPolicyLink:@"" ezalterDiversions:nil defaultEzalterParams:nil activateSuccessHandler:^(NSDictionary * _Nonnull json, NSString * _Nullable token) {`
` } activateFailureHandler:^(NSError * _Nonnull error) {`
` } activeSuccessHandler:^(NSDictionary * _Nonnull json) {`
` } activeFailureHandler:^(NSError * _Nonnull error) { `
` }];`
`return YES;`
`}`
`@end`
在 Build iOS Project 的時候,Unity 會自動把 CustomAppController.mm 復制到 ?/path/to/project/Libraries/CustomAppController.mm?
而原來的 UnityAppController.mm 則在 ?/path/to/project/Classes/UnityAppController.mm?
Unity 通過 ** IMPL_APP_CONTROLLER_SUBCLASS **知道要使用我們定制的 CustomAppController 而不是使用默認的 UnityAppController
IMPL_APP_CONTROLLER_SUBCLASS
UnityAppController.h 里面有這樣一個宏:
`#define IMPL_APP_CONTROLLER_SUBCLASS(ClassName) \`
`@interface ClassName(OverrideAppDelegate) \`
`{ \`
`} \`
`+(void)load; \`
`@end \`
`@implementation ClassName(OverrideAppDelegate) \`
`+(void)load \`
`{ \`
` extern const char* AppControllerClassName; \`
`AppControllerClassName = #ClassName; \`
`} \`
`@end`
將這個宏加到 CustomAppController.mm 中,即可實現(xiàn)自動設置 AppControllerClassName :
`IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)`
IMPL_APP_CONTROLLER_SUBCLASS 使用了兩個 Objective-C 的特性,一是 ?category? ,用來給已有的類擴展新的方法;二是 ?+(void)load? 靜態(tài)方法,它會在運行時 CustomAppController 類被加載到內(nèi)存中時觸發(fā),這個時間點比 ?int main()? 函數(shù)還要早,所以能夠提前“篡改” ?AppControllerClassName?,達到我們的目的。
- 程序入口
程序的入口為main.mm文件,通過IMPL_APP_CONTROLLER_SUBCLASS 調(diào)用了自定義繼承UnityAppController的 CustomAppController文件,在該文件的 下面這個方法- (BOOL)application:(UIApplication)application didFinishLaunchingWithOptions:(NSDictionary)launchOptions; 中注冊了BBase,調(diào)用父類UnityAppController中的方法,實現(xiàn)UI的創(chuàng)建
iOS平臺Unity引擎的IL2CPP機制
實現(xiàn)原理
Unity引擎作為目前最為主流的3D游戲開發(fā)引擎,游戲平臺移植性非常好,Unity引擎4.6.2之后的版本采用了IL2CPP機制支持IOS平臺64位游戲編譯,針對IL2CPP機制進行深入分析之后有利于評估IOS平臺的Unity游戲安全性。
Unity引擎采用IL2CPP機制在Mac平臺成功編譯之后,會生成一個完整的XCode工程,Unity游戲邏輯代碼采用C#編寫,游戲開發(fā)方的C#代碼最終會生成在/Classes/Native目錄中,對應文件結(jié)構(gòu)如下圖所示:
UI顯示機制
Unity導出的iOS工程里面的結(jié)構(gòu)大致是這樣的,有一個Window,Window上有一個UnityView,但是并沒有控制器,也沒有根控制器,雖然在導出的iOS工程中Classes文件夾下的UnityAppController中有rootController的屬性,但是上面也標注為空~ 直接在window上顯示Unity界面的
導出xcode項目后,會產(chǎn)生DisplayManager 這樣的一個顯示管理器文件, 該文件中會輔助UnityView,Keyboard等等Unity轉(zhuǎn)成iOS的View的顯示,在DisplayManager 這個類中,引入了EAGLContext,通過OpenGL 去實現(xiàn)UI 的繪制
注釋:
1. CAEAGLLayer 支持在iOS和tvOS應用程序中繪制OpenGL內(nèi)容的圖層。
2.?EAGLContext?對象管理一個OpenGL ES 渲染上下文 狀態(tài)信息,命令和使用OpenGL ES繪制所需要的資源。要執(zhí)行OpenGL ES命令,您需要當前的渲染上下文。
補充:
Unity中的ViewController
Unity中沒有viewcontroller的概念,頁面之間的切換其實就是在同一個viewcontroller上重繪不同的view,一個叫做UnityView的UIView來顯示繪制的頁面。
但既然要在iOS上顯示界面,就必須要有一個viewcontroller的載體。Unity中沒有,所以Unity中有一個_viewControllerForOrientation[5]的數(shù)組來維持屏幕4個方向的viewcontroller+autorotate的viewcontroller,也就是說Unity中從始至終最多只有5個viewcontroller。
viewcontroller如何切換
如果現(xiàn)在要顯示豎屏頁面,就從數(shù)組中取出豎屏的viewcontroller,如果沒有就創(chuàng)建一個,然后將_unityView賦值給豎屏的viewcontroller.view,如果現(xiàn)在要進入橫屏頁面,就從數(shù)據(jù)中取出橫屏的viewcontroller,然后將_unityView賦值給橫屏的viewcontroller.view。
createDisplayLink
那么手機從豎屏變成橫屏后什么時候重繪呢,什么時候顯示呢?這個時候就需要createDisplayLink函數(shù),它會創(chuàng)建一個60Hz的timer,為什么是60,因為iPhone的屏幕刷新率就是60Hz,每次的timer執(zhí)行函數(shù)中會去檢查當前要顯示哪個viewcontroller,以及是否需要重繪,然后顯示