iOS項目集成UnityFramework

背景:最近公司要做關于幼兒編程的項目,在iOS項目中需要使用視頻交互的功能,最后選擇的方案是使用Unity實現,主要涉及到iOS原生項目和Unity界面來回切換,iOS和Unity的相互通信。

1、iOS原生項目和Unity界面來回切換

1.1 從iOS原生項目通過點擊按鈕進入Unity

- (void)enterUnityButtonClick {
  [self initUnity];
}

- (void)initUnity
{
    if([self unityIsInitialized]) {
        showAlert(@"Unity already initialized", @"Unload Unity first");
        return;
    }
    if([self didQuit]) {
        showAlert(@"Unity cannot be initialized after quit", @"Use unload instead");
        return;
    }
    
    [self setUfw: UnityFrameworkLoad()];
    [[self ufw] setDataBundleId: "com.unity3d.framework"];
    [[self ufw] registerFrameworkListener: self];
    [[self ufw] runEmbeddedWithArgc: gArgc argv: gArgv appLaunchOpts: appLaunchOpts];
    // set quit handler to change default behavior of exit app
    [[self ufw] appController].quitHandler = ^(){ NSLog(@"AppController.quitHandler called"); };
  }

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;
}
//其中 [self ufw] 是所在類的一個熟悉
@property (nonatomic, strong) UnityFramework *ufw;
[[self ufw] runEmbeddedWithArgc: gArgc argv: gArgv appLaunchOpts: appLaunchOpts];
//gArgc 、 gArgv 這兩個參數是程序入口main函數中的參數,可以使用NSUserDefaults保存起來
//appLaunchOpts 是- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 函數中的launchOptions參數

1.2 從Unity界面回到iOS界面

上面的函數正確調用后,從iOS界面跳轉到Unity應該是沒問題了,那怎么從Unity退出回到iOS原生界面呢?

我們首先從Unity項目中導出Unity-iPhone項目中的接口文件UnityFramework看一下,接口文件如下圖
UnityFramework.png

很容易我們調用- (void)pause:(bool)pause函數.

然后再調用- (void)unloadApplication或者- (void)quitApplication:(int)exitCode

結果LuaClient:Destroy(),連同iOS的程序也Crash了,那一定是調用的方法不對,或者順序不對?那到底問題出在哪了呢?

最終找到了一個iOS集成Unity項目的工程 https://github.com/Unity-Technologies/uaal-example ,最終才理清楚調用順序。

正確的退出順序:

1.2.1、遵守協議注冊監聽

1、首先是所在的控制器要遵守協議 <UnityFrameworkListener>,監聽Unity狀態的變化

2、 注冊監聽 [[self ufw] registerFrameworkListener: self];

1.2.2、點擊退出按鈕
- (void)quitUnityButtonClick {
    if ([self unityIsInitialized]) {
        [UnityFrameworkLoad() unloadApplication];
    }
}

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

2、創建一個能與iOS交互的Unity項目

2.1、引用接口文件遇到的鏈接錯誤

由于Unity-iPhone項目是另一個部門提供的,提供的接口文件在iOS項目中能編譯,但是鏈接時報錯,如下圖

UnityForiOSInteface.png
LinkerError.png

查閱了大量資料也沒搞清楚原因,最后在 Unity的官方文檔 中看到iOS去調用Unity有兩種方式

1、UnitySendMessage

2、delegates

由于我們的項目要在Unity啟動前就把數據準備好,用于加載不同的內容,UnitySendMessage這種方式只適用于Unity已經啟動好了,iOS向Unity通信,所以就要使用delegates的方式進行通信

為了調試項目,只能自己創建一個Unity項目區驗證delegates這種方式是否行得通了,之前沒有Unity項目基礎就在騰訊課堂看到一個講解的還不錯的適合Unity入門的項目

2.2、創建一個Unity項目

https://ke.qq.com/course/3101943?taid=10573792874026231

3、iOS和Unity的相互調用:

UnityToiOS.png
iOS與Unity相互通信的文件放在Plugins/iOS/ 目錄下
//UnityForiOSInterface.h 文件

#import <Foundation/Foundation.h>

typedef void (*ResultHandler) (const char * _Nonnull object);

//Unity退出時觸發該通知
#define kQuitUnityNotification  @"kQuitUnityNotification"
//使用NSUserDefaults通過kCourseInfoKey保存課件信息
#define kCourseInfoKey          @"kCourseInfoKey"

NS_ASSUME_NONNULL_BEGIN

@interface UnityForiOSInterface : NSObject

#ifdef __cplusplus
extern "C" {
#endif
    
    void unitySendToiOSMessage(const char *msg); //打開Unity后,Unity回調課程的狀態給iOS
    void getiOSParams(ResultHandler resultHandler); //Unity調用iOS,iOS通過函數指針參數賦值的方式向Unity賦值
    
#ifdef __cplusplus
}
#endif
@end

NS_ASSUME_NONNULL_END

//UnityForiOSInterface.m 文件
#import "UnityForiOSInterface.h"

@implementation UnityForiOSInterface

void unitySendToiOSMessage(const char *msg) {
    NSLog(@"msg:%@",[NSString stringWithUTF8String:msg]);
    [[NSNotificationCenter defaultCenter] postNotificationName:kQuitUnityNotification object:[NSString stringWithUTF8String:msg]];
}

void getiOSParams(ResultHandler resultHandler) {
    NSString *paramsStr = [[NSUserDefaults standardUserDefaults] objectForKey:kCourseInfoKey];
    resultHandler (paramsStr.UTF8String);
}
@end
這里寫一個簡單的Unity項目 用于和iOS做交互,也可以參考騰訊課堂的Unity項目講解 ,簡單來說就是在場景上掛在一個腳本,通過點擊場景的按鈕由Unity向iOS發送消息
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using AOT;
using UnityEngine;
using UnityEngine.UI;

public class UnityCallObjc : MonoBehaviour
{
    public InputField input;  
    public Text text;

    [DllImport("__Internal")]
    static extern void UnitySendToiOSMessage(string msg);

    [DllImport("__Internal")]
    static extern void getiOSParams(IntPtr resultHandler);

    public void OnButtonClick()
    {
        //IOSLog(input.text);
        ResultHandler handler = new ResultHandler(resultHandler);
        IntPtr fp = Marshal.GetFunctionPointerForDelegate(handler);
        getiOSParams(fp);
    }

    //非托管方法
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ResultHandler(string resultString);

    [MonoPInvokeCallback(typeof(ResultHandler))]
    static void resultHandler(string resultStr)
    {
        Debug.LogFormat("result string = {0}", resultStr);
    }
}
Unity的主要語言是C#,因此可以利用C#的特性來訪問C語言所定義的接口,然后再通過C接口再調用ObjC的代碼,UnityCallObjc腳本為什么這么寫,Unity3D與iOS的交互,講得非常清楚了,具體可以看一下.

參考資料:

https://github.com/Unity-Technologies/uaal-example.git

https://ke.qq.com/course/3101943?taid=10573792874026231

https://docs.unity3d.com/Manual/PluginsForIOS.html

http://www.lxweimin.com/p/1ab65bee6692

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容