主題

Unity嵌入至iOS筆記分享

pudding | 2021-09-11 23:57:32 | 巴幣 24 | 人氣 99

上回跟各位分享了怎麼把Unity嵌入至Android的筆記(這一篇)這一回我們則要來看看怎麼把Unity嵌入至iOS。
不得不說嵌入至iOS的過程與Android相比,真的是麻煩了好幾倍,我記得Android部分我大概找了2、3個教學就解決了,但到了iOS的時候我卻找了大約10多個教學才勉強搞定(雖然主要也是因為我對iOS不熟悉),因此我猜接下來的內容會有不少解釋錯誤的地方,還請各位多多提點或包含。

先貼上這次的主要參考來源
官方範例
上面這一篇可去下載他們github上的專案,網站上也有嵌入過程教學,但是官方的提供的XCode專案(原生iOS)和Unity都是有做過一些改動的,因此嵌入時有省略些許步驟,這篇筆記分享則是要帶各位從普通的空白專案開始做起。

先讓我們沿用上回的程式碼吧,在先前管理Unity和原生App溝通的MobileMsgMgr當中,讓我們添加以下內容
(雖然很多人應該都已經知道了,不過還是提一下,#if UNITY_IOS到#endif代表著該區段內的程式碼只在iOS環境下才會出現,否則的話就無視掉)
這邊特別注意到幾個大家應該挺陌生的地方,首先看到DllImport,在函式的上方加入該屬性框的話代表他是從動態函式庫(DLL)中被引入的,後面寫__Internal (兩個底線)則是因為iOS的插件都是直接與執行檔連結的關係,所以函式庫名稱是直接寫__Internal (內部)
……我必須承認我也不是很懂這一段的意思,這是截取自官方的說明
另外extern的意思簡單來講就是宣告說這個函式在其他地方才會被實作,當前在C#內就只管呼叫就好

接著讓我們去先前的溝通用函式中呼叫這些新宣告的內容吧

這裡建議額外建立一個Plugins/iOS資料夾,在裡頭放入未來要讓原生iOS那邊用到的文檔(避免每次匯出後又要再重新建立)
(題外話,現在有沒有特別用Plugins/iOS資料夾包住好像都沒差,Plugins之前會被Unity當成特殊資料夾,但現在貌似沒有,官方說明在這部分只更新到2017,新版中我目前沒看到相關內容)

這裡我們宣告了NativeCallForUnity的.h和.mm文檔(前者為標頭檔,有點像是單純宣告函式名稱的文檔,後面.mm則是實際實作的文檔)這樣之後我們每次匯出時就都會帶有這兩個文檔,不用重寫
以下讓我們先看看這兩份文檔的內容


題外話,我後來發現要傳字串給Unity的話不能使用NSString(Objective-C當中常用的字串類別),得要用char *(字元指標)

寫完後我們就可以來匯出了,先將建置平台改為iOS,並且在Signing Team ID中填入你在App Store中上架用的ID(後面這個動作應該可以在XCode那邊再設定就好,例如我工作用的專案就沒有先設定)
特別注意,如果你沒有iOS裝置,得靠模擬器的話,那記得要把Target SDK改為Simulator SDK

匯出後你就可以把這個匯出專案移到有XCode的Mac電腦上了
我們先來Mac電腦上建立一個XCode專案
預設模板我選用Storyboard,並且語言選用Objective-C

建立專案後,各位可以先看到專案的結構,特別注意,如果你是照著官方Github上的指示來做的話,你會發現官方提供的XCode專案長得不太一樣
那是因為官方把AppDelegate和ViewController的.h和.m文檔合併成了一個.mm檔了
(.mm跟.m的差異在於前者可以編譯C++語言,但我不確定在這個範例中並非必要)
(要改成.mm的話就直接去改檔名就好)

到了這一步後,我們先關閉上述兩個專案(Unity產生的XCode與原生XCode專案),另外建立一個WorkSpace,將剛才的兩個專案都加入其中

接著我們點擊原生的專案並看到General的區塊,往下找到Frameworks, Libraries, and Embedded Content的區域,按下+號去把UnityFramework.framework加進來
上圖擷取自官方文件

然後再去Build Phases的區塊,將UnityFramework.framework從 "Link Binary With Libraries" 當中移除 (對,前面先加,後面這個部分則是移除……我也看不太懂)
一樣擷取自官方文件

做完後再去點Unity-iPhone底下的Data資料夾,將TargetMembership中的UnityFramework勾起。

然後找到先前的NativeCallsForUnity.h檔,把他的TargetMembership設成public

都做完以後就可以進入到在iOS當中去呼叫並顯示Unity視窗的環節了,為此我們要先到AppDelegate.h底下新增這些內容
(Objective-C在變數宣告方面有strong,nonatomic等區分,有興趣了解的人不妨看看這一篇)

然後AppDelegate.m則需要實作,我們先試著生成原生iOS自己的視窗,要寫以下這些
覆寫原本AppDelegate.m當中的didFinishLaunchingWithOptions,AppDelegate是iOS當中用來控制App生命週期的類別,其中didFinishLaunchingWithOptions是整個App啟動後的起點
(didFinishLaunchingWithOptions部分可參考這篇文章)
在App啟動後,我們這裡宣告了一個自己的視窗,而該視窗則需要再去看ViewController.h和.m的程式碼
(上面的覆寫內容也包含了幾個全域變數的宣告,那一些是等下要叫出Unity視窗時會用到)

ViewController.h
這邊我們先宣告視窗中要用到的按鈕 (題外話,我後來發現這次的練習中其實用不到ShowUnity那個按鈕)

再來則是ViewController.m
.m檔的話則比較偏向實作,在這邊特別注意到我們會呼叫sharedApplication的delegate,然後再轉型成AppDelegate指標型態,UIApplication的sharedApplication可以讓我們存取到當前這個App的實體 (官方說明)

寫完後各位做第一次匯出應該可以看到類似這樣的畫面 (噢,我後來才發現藍色文字配藍色按鈕看得有些吃力)

接著讓我們來呼叫並顯示Unity吧,首先我們先去實作先前自行宣告的initUnity和showUnity函式
這裡特別注意初始化Unity時會用到先前宣告的全域變數,而這些變數則是在main(主程式入口)當中會被設定,為此我們要先將原生專案中的main.m文檔刪除
(你可以只移除參照就好,這樣硬碟上就仍會保有檔案)

移除後你就可以把int main函式(主程式進入點)移到AppDelegate.m文檔之中,並且將我們需要的變數存起來
順便再補充一下,這裡的gArgc和gArgv兩個變數,他們是App開啟時會傳入的argc(argument count)和argv(argument value)——參考來源

寫好後我們就可以來做第一次Unity視窗呼叫了……嗎?

結果卻叫不出來(但是Log訊息顯示有成功初始化)
這是為什麼?
再找了很多資料並比較了自己的專案與官方範例的不同後,最後發現了原來較新的XCode版本中,預設會讓另一個SceneDelegate的類別來負責視窗的控制(可參考此處),因此你無法自己去秀出Unity視窗,要解決的話可以先去點擊原生專案的info,然後看到其中的Application Scene Manifest欄位
把該欄位移除
然後刪掉SceneDelegate.h和.m

最後再去把application configurationForConnectingSceneSession和didDiscardSceneSessions拿掉

接著應該就能正常開啟了

確認好後我們來實作跟Unity溝通用的幾個函式吧
特別記得這裡為了要能夠接收Unity的字串或著被Unity呼叫,我們要去進行註冊動作

先來看看互傳字串的效果

然後再看到關閉Unity的部分,這裡我們用Unload的方式來關閉,除了呼叫以外我們也要記得去註冊Unload事件(unityDidUnload),它在Unity卸載了以後會去執行其中的內容

來看看實質效果
<AppDelegate.hAppDelegate.m 的程式碼>
題外話,除了Unload以外,UnityFramework也有另一個Quit函式可呼叫(可參考這裡),該方法可以釋放較多的記憶體,但是呼叫了以後在當前的App生命週期內就不能再開啟Unity了 (相當於你得把整個App關掉然後重開)

好了,這次的筆記分享就先到此告一段落了,希望有幫到大家,抱歉這次的內容真的又多又雜QwQ,我真心覺得iOS弄起來超級麻煩 (也可能只是我不習慣就是了)

創作回應

更多創作