將 Unity 集成到原生 iOS 應(yīng)用程序中(2020+)

前言

最近接手一個項(xiàng)目,主要是需求就是iOS原生+Unity。網(wǎng)上拖四個文件(Classes、Libraries、MapFileParser.sh、Data)的方式,新版本也是各種報錯。下面說的是一種更簡單方式。

Unity官方給出的操作(有點(diǎn)簡單了)

要將“Unity 用作 iOS 庫”,請像往常一樣,先從 Unity 構(gòu)建 Xcode 項(xiàng)目(有關(guān)更多信息,請參閱 iOS 構(gòu)建設(shè)置)。

每個 Unity iOS Xcode 項(xiàng)目具有以下結(jié)構(gòu)

·UnityFramework 目標(biāo)中的一個庫部分,其中包含源、插件和相關(guān)框架。它還生成 UnityFramework.framework 文件。
· Unity-iPhone 目標(biāo)中的一個瘦啟動器部分,其中包含應(yīng)用程序表示數(shù)據(jù)并會運(yùn)行該庫。Unity-iPhone 目標(biāo)對 UnityFramework 目標(biāo)具有單一依賴關(guān)系。

要將 Unity 集成到另一個 Xcode 項(xiàng)目中,必須將兩個 Xcode 項(xiàng)目(原生項(xiàng)目和 Unity 生成的項(xiàng)目)合并到一個 Xcode 工作空間中,并將 UnityFramework.framework 文件添加到原生 Xcode 項(xiàng)目的應(yīng)用程序 (Application) 目標(biāo)的嵌入式二進(jìn)制文件 (Embedded Binaries) 部分中。完成此操作后,可以使用 UnityFramework 類來控制 Unity 運(yùn)行時。

上面??的看不懂沒關(guān)系,簡單來說就是將兩個TARGETS 合并在一個workspace里面。下面??開始具體詳細(xì)的步驟。

首先說一下開發(fā)環(huán)境
Xcode:12.5.1
Unity :2020.3.17f1

Unity版本一定要注意,老版本的就按照老前輩們的拖動文件的方式。(老版本集成的方式太惡心人了)

1、創(chuàng)建新的Unity工程并導(dǎo)出Xcode工程(已經(jīng)導(dǎo)出好了,請移步下面??操作2)

為了展示效果,只是簡單創(chuàng)建個buttom在unity。


image.png

創(chuàng)建方法點(diǎn)擊左上角加號。這里就不多說了。有興趣的自己研究unity創(chuàng)建物體。

工程創(chuàng)建好之后,就可以導(dǎo)出了。File->Build Setting(快捷鍵:shift + Command +B)

選擇iOS平臺,點(diǎn)擊Player Settings,填寫 Bundle Identifier、和Version。


image.png

這邊Bundle Identifier默認(rèn)是 com.Company Name.Product Name,所以你只要對應(yīng)寫一下,會自動生成Bundle Identifie。

image.png

點(diǎn)擊下面的Other Settings 設(shè)置具體的運(yùn)行平臺、模擬器還是真機(jī)。


image.png

設(shè)置完成之后就可以Bulid了(沒有Build按鈕的,回事一個選擇平臺的按鈕,你一下就出現(xiàn)Build了),如果沒有install iOS平臺,你要install 一下。比如選擇安卓平臺,就會出現(xiàn)下面的界面,你點(diǎn)擊install With Unity Hub就可以了。


image.png

剩下的就是Build之后的等待......

導(dǎo)出完成,下面就是導(dǎo)出之后的工程包


image.png

打開工程,真機(jī)run一下,沒問題就行,證書這基本配置就不在這說了。
到此第一步就完成了

2、將上面??Unity導(dǎo)出的Xcode 嵌入原生Xcode

為了掩飾,創(chuàng)建新的Xcode工程-iOSUseUnity。將Unity導(dǎo)出的Xcode 復(fù)制到iOSUseUnity下面


image.png

如果已經(jīng)通過pods 創(chuàng)建的工程,會有一個xcworkspace,那么就可以忽略下面的操作:
創(chuàng)建xcworkspace(File-> New->workspace),將xcworkspace移動到iOSUseUnity下,然后打開,點(diǎn)擊Xcode左下角的"+"號,將原生工程、unity導(dǎo)出的工程添加進(jìn)來。

image.png

如果已經(jīng)pods有workspace后,直接只需要將unity導(dǎo)出的工程添加進(jìn)來就行了,下面是添加完成之后的圖。


image.png

添加framework引用Data


image.png

編譯一下framework
編譯一下framework
編譯一下framework
Command + B

image.png

然后就是在原生工程iOSUseUnity中添加framework。


------------到這基本結(jié)束了工程的配置------------
下面就是上代碼了

main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:@(argc) forKey:@"argc"];
    [userDefaults synchronize];
    
    [userDefaults setObject:[NSString stringWithFormat:@"%p",argv] forKey:@"argv"];
    [userDefaults synchronize];
    
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

AppDelegate.h

#import <UIKit/UIKit.h>
#include <UnityFramework/UnityFramework.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate,UnityFrameworkListener>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, strong) UnityFramework *ufw;

- (void)showUnityView;

- (void)showNativeView;

@end

AppDelegate.m

#import "AppDelegate.h"

/* UnityFrameworkLoad */
UIKIT_STATIC_INLINE UnityFramework* UnityFrameworkLoad()
{
    NSString* bundlePath = nil;
    bundlePath = [[NSBundle mainBundle] bundlePath];
    bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"];

    NSBundle* bundle = [NSBundle bundleWithPath: bundlePath];
    if ([bundle isLoaded] == false) [bundle load];

    UnityFramework* ufw = [bundle.principalClass getInstance];
    if (![ufw appController])
    {
        // unity is not initialized
        [ufw setExecuteHeader: &_mh_execute_header];
    }
    return ufw;
}

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    [self initUnity];
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:launchOptions forKey:@"launchOptions"];
    [userDefaults synchronize];
    
    return YES;
}


#pragma mark - Unity

- (BOOL)unityIsInitialized
{
    return [self ufw] && [[self ufw] appController];
}

- (void)initUnity
{
    /* 判斷Unity 是否已經(jīng)初始化 */
    if ([self unityIsInitialized]) return;
    /* 初始化Unity */
    self.ufw = UnityFrameworkLoad();
    [self.ufw setDataBundleId:"com.unity3d.framework"];
    [self.ufw registerFrameworkListener:self];
//    [NSClassFromString(@"FrameworkLibAPI") registerAPIforNativeCalls:self];
    
    NSString *argvStr = [[NSUserDefaults standardUserDefaults] valueForKey:@"argv"];
    char **argv;
    sscanf([argvStr cStringUsingEncoding:NSUTF8StringEncoding], "%p",&argv);
    int argc = [[[NSUserDefaults standardUserDefaults] valueForKey:@"argc"] intValue];
    NSDictionary *launchOptions = [[NSUserDefaults standardUserDefaults] valueForKey:@"launchOptions"];
    [self.ufw runEmbeddedWithArgc:argc argv:argv appLaunchOpts:launchOptions];
    
}

- (void)showUnityView
{
    if (![self unityIsInitialized]){
        NSLog(@"Unity 還未初始化");
    }
    
    [self.ufw showUnityWindow];
}


- (void)showNativeView
{
    [self.window makeKeyAndVisible];
}

#pragma mark - UnityFrameworkListener
- (void)unityDidUnload:(NSNotification *)notification
{
    NSLog(@"========== %s ============",__func__);
    [self.window makeKeyAndVisible];
    [[self ufw] unregisterFrameworkListener: self];
    [self setUfw: nil];
}

- (void)unityDidQuit:(NSNotification *)notification
{
    NSLog(@"========== %s ============",__func__);
}


- (void)applicationWillResignActive:(UIApplication *)application {
    [[[self ufw] appController] applicationWillResignActive: application];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
    [[[self ufw] appController] applicationDidEnterBackground: application];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
    [[[self ufw] appController] applicationWillEnterForeground: application];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
    [[[self ufw] appController] applicationDidBecomeActive: application];
}
- (void)applicationWillTerminate:(UIApplication *)application {
    [[[self ufw] appController] applicationWillTerminate: application];
}

@end

添加一個觸發(fā)Unity打開的事件的方法(我這邊是在Main.storyboard添加一個按鈕)。

- (IBAction)btnClick:(UIButton *)sender {
    AppDelegate *appDelegate = (AppDelegate *)([UIApplication sharedApplication].delegate);
    [appDelegate showUnityView];
}

---------------------------------------常見問題---------------------------------------------

1、#include <UnityFramework/UnityFramework.h> 這一行archive的時候提示file not found?
本人發(fā)的工程只是添加的framework引用,在archive的時候肯定找不到framework。這個時候把unity工程的framework復(fù)制自己工程里面,然后添加引用的時候,從文件夾里添加。


image.png

點(diǎn)擊show in finder,找到文件。然后就復(fù)制,拷貝到自己的工程中。



選擇add files,找到你剛才復(fù)制的工程,open 即可。skr~~

通了之后就是這么的簡單,總結(jié)多嘗試,多學(xué)習(xí),不放棄。

Unity和iOS之間的通信留在下一篇詳解,歡迎糾錯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容