定製 UnityAppController

開發 Unity3d 手機遊戲的時候,不免要和第三方 SDK 打交道。於是總是需要實現自己的 AppController 來維護 SDK 的生命週期。

Unity3d 提供了一套插件機制,可以很方便地在項目中使用自己的 CustomAppController 繼承並重寫默認的 UnityAppController 的方法。

0x00 CustomAppController

在 Unity 插件目錄下創建以下文件:
/path/to/unity/project/Assets/Plugins/iOS/CustomAppController.mm

注意,文件名必須是 ___AppController,前綴可自選,但不能省略;否則在 Build 項目的時候,會被移動到錯誤的目錄中去。

CustomAppController.h 頭文件是可選的,不過通常直接把 @interface 直接放在 .mm 文件裏就好。下面以微信 SDK 爲例:

#import "UnityAppController.h"
#import "WXApi.h"

@interface CustomAppController : UnityAppController < WXApiDelegate >
@end

IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)

@implementation CustomAppController

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    [super application:application didFinishLaunchingWithOptions:launchOptions];

    [WXApi registerApp: @"_________"];
    
    return YES;
}

- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation
{
    return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
    return [WXApi handleOpenURL:url delegate:self];
}

- (void)onResp:(BaseResp *)resp
{
    // do something
}

- (void)onReq:(BaseReq *)req
{
    // do something
}

@end

在 Build iOS Project 的時候,Unity 會自動把 CustomAppController.mm 複製到 /path/to/project/Libraries/CustomAppController.mm

而原來的 UnityAppController.mm 則在 /path/to/project/Classes/UnityAppController.mm

那麼 Unity 是如何知道要使用我們定製的 CustomAppController 而不是使用默認的 UnityAppController 呢?

0x01 IMPL_APP_CONTROLLER_SUBCLASS

很多文章在提到繼承 UnityAppController 後,需要找到 /path/to/project/Classes/main.mm 裏面的:

const char* AppControllerClassName = "UnityAppController";

將其修改爲:

const char* AppControllerClassName = "CustomAppController";

從而使 Unity 在啓動的時候使用我們制定的 CustomAppController 類。

這樣一來,每次 Build 項目都需要手動去修改這個常量,豈不是自找麻煩。其實完全可以利用 Objective-c 的特性來自動完成這個操作。

注意到 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 中,即可實現自動設置 AppControllerClassName :

IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)

是不是很神奇呢!IMPL_APP_CONTROLLER_SUBCLASS 使用了兩個 Objective-C 的特性,一是 category ,用來給已有的類擴展新的方法;二是 +(void)load 靜態方法,它會在運行時 CustomAppController 類被加載到內存中時觸發,這個時間點比 int main() 函數還要早,所以能夠提前“篡改” AppControllerClassName,達到我們的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章