2012年2月3日 星期五

Unity 3D Plug-in for Android 開發指南

這是一個摸索筆記。

以下是正文。

一. Unity3D Plug-in 必須知道的概念

Unity 的 Plug-in 是以二進位存在的,所以,在不同的平台上,必須開發不同的 Plug-in,例如,在 PC Windows 平台,使用 dll 檔,在 Android 系統,使用 .so 檔。

在使用 Unity 開發專案時,因為是在 PC Windows 平台上操作,所以當使用了 Android 的 .so 檔時,會造成無法在 Unity Editor 中即時操作遊戲的問題。至於是否可以有個取代方案,例如,另外寫個相同功能的 dll 檔來在 PC Windows 平台上操作,這要再做進一步測試。

二. Unity3D Plug-in 的安裝

Unity 的官方文件,是建議將 Plug-in 複製到專案的 Assets/Plugins/Android 目錄下,如果沒這個目錄,就自行建立一個。

所以,就是將寫好編譯好的 .so 檔,複製到這個目錄下就可。

官方文件上有一個模稜兩可的說法,說 Plug-in 安裝後,Unity 會自動抓出其中的 Java Class 什麼的,不要相信它,因為完全試不出來。

三. Unity3D Plug-in 的使用

.so 檔複製到專案以後,是無法取得任何資料的。必須透過 C# Script 來載入。 官方文件中,只提供了使用 C# Script 來參考 Plug-in 的方法,找不到 Java Script 的方法,所以就先以 C# Script來做。

官方網站上有一個可以參考的範例,"AndroidNativePlugin.zip",可以下載來參考。

zip 檔解開後,將 unity package import 進 Unity Editor,可以抓到 .so 檔,原始程式碼,以及使用 Plug-in 的 C# Script。

C# Script 如下 (已經改過了)

using UnityEngine;

using System.Collections;

using System.Runtime.InteropServices;

public class CallNativeCode : MonoBehaviour {

public GUISkin mySkin;

[DllImport("ttt")]

private static extern float add(float x, float y);

void OnGUI ()

{

GUI.skin = mySkin;

float x = 3;

float y = 10;

GUI.Label (new Rect (15, 125, 450, 100), "add" + x + " and " + y + " equals " + add(x,y));

}

}

幾個重點:

1. [DllImport(“ttt”)] : 這行程式,是負責載入 Plug-in,依照官方規定,這個Plug-in 的檔名是 libttt.so,也就是,前綴必須有”lib” 三個字母,後面是 .so 副檔名。

2. private static extern float add(float x, float y); -- 這一行是宣告Plug-in中的函式。

3. 載入與宣告函式完成後,在 Script 的 OnGUI 函式中,就可以使用這個函式了。

四. Android Plug-in 的開發

接著就是,.so 這個檔怎麼做出來了。

.so 檔,可以視作是 Android 系統上的 Dynamic Load Library (dll)

1. 建立開發環境

一般書籍或網站上找到的 Android 應用開發,多是以 Java 環境來做的,這是無法編譯 .so 檔案的。

Android 的 Development Kit 有兩種,一種是 SDK (Software Development Kit), 一種是 NDK (Native Development Kit),編譯 .so 檔,需要 NDK。

傳統的建立開發環境的方法是,要安裝 cygwin (Windows 上的 Linux 模擬器)、NDK、SDK、Eclipse IDE 等等,現在有個神人把整個開發環境整合到了 Visual Studio 2010上,所以建立開發環境的方法,就以整合 VS 2010 來做了。

vs-android -- Integrated development of Android NDK C/C++ software under Visual Studio

下載vs-android,sample solution,然後下載 Android NDK (r7 or later),SDK,Java SDK (32 bits版),以及 Ant。

需要安裝的安裝,可以解壓縮複製的,就解壓縮複製。( Android SDK 的安裝,比較複雜些,請找資料看看 )

然後要設定幾個環境變數

ANDROID_HOME : Android SDK 的根目錄位置,例如 C:\Develop\android-sdks

ANDROID_NDK_ROOT : Android NDK 的根目錄位置,例如 C:\Develop\android-ndk-r7

ANT_HOME : Apache Ant 的目錄,例如 C:\Develop\apache-ant-1.8.2

2. 測試範例程式 san-angeles

設定完成,打開 VS 2010,載入 Sample Solution,USB接上測試用設備,Build Solution,然後就 OK 了。

<< 不過,還是會碰到編譯不過的情形啦。以下是幾個可能要處理的狀況。

a.
Unable to resolve target 'android-4'
    因為在 SDK中的 Platform 版本裡沒有安裝 API Level 4,修改 project.properties 這個檔案中的 target=android-4,改為 SDK 有安裝的API

b.
ANTBUILD : [dx] error : Could not create the Java Virtual Machine.
ANTBUILD : [dx] error : A fatal exception has occurred. Program will exit.
[dx] Error occurred during initialization of VM
[dx] Could not reserve enough space for object heap
這問題,打開 android-sdks\platform-tools 目錄中的 dx.bat 檔案,將
set defaultXmx=-Xmx1024M
改為
set defaultXmx=-Xmx512M

>>

3. 建立要編譯 .so 檔的專案

因為 vs-android 的神人作者,還沒有寫好 Project Wizard,所以,要靠半手動的方式來建立新專案。

簡單一點的做法是,就在範例程式 solution 下新加入一個專案,選擇 Win32 Console App,然後建立 Empty Project。

Project 的名稱,以 lib 開始,符合 Unity Plug-in 的規則。例如,libunityplug1

專案建立完成,開啟專案的 Property Page,點選 Configuration Manager

clip_image004

新建立的專案還是 Win32 Platform,點選專案的 Platform 下拉箭頭,選擇 <<New>>,建立新的專案 Platform。

clip_image006

Platform 選擇 Android, Copy Setting 選擇 <Empty>,底下的 Create new solution platforms 要取消打勾。

然後按下 OK

新專案的 Project Platform 就會改成 Android,而專案中的 Property 也會自動調整為 Android 平台相關的設定。

因為要產生的是 Dynamic Library ( .so ) 檔,所以,將 Configuration Type 調整為 Dynamic Library。

clip_image008

其他的 API Level, Architecture,有需要也可以改看看。

然後就開始加入原始程式碼檔案吧!!

4. Plug-in 程式碼與使用

很快,隨便寫了個 .c 的程式

float multiply(float x, float y)

{

return x * y;

}

然後 Build 這個 libunityplug1 專案,沒問題的話,會得到 libunityplug1.so 的檔案,把 .so 檔案複製到 Unity 的 Assets/Plugins/Android 目錄下

修改 Unity C# Script

[DllImport("unityplug1")]

private static extern float multiply(float x, float y);

然後,就可以使用這個函式了。

12 則留言:

小衛 提到...

你好,我最近也建了一個遊戲引擎的網誌,請問可以連結到你的網誌嗎?

藍斯洛 提到...

可以啊,
請不用客氣

迷途小書僮XXX 提到...

您好,感謝板主分享這麼讚的指南!!

另外,想請教一個問題,如果我要用Unity去呼叫Android native application 或 activity能做得到嗎?或該怎麼開始Try呢? 能否指引一條明燈?

小衛 提到...

感謝

藍斯洛 提到...

To 迷途小書僮:
你提的需求我沒有嘗試過,所以沒辦法給你很確切的回答。

如果是我要做的話,我會先把 Unity的專案先擴展 Activity,然後用 Android SDK中的 API 來試試。

Unknown 提到...

您好,感謝版主寫出這麼詳細的解說!!

我想要請教版主稍微進階點的問題,在使用.SO上數字間的傳遞是沒有問題的,但是如果切換到字串間的傳遞(不是字元),這方面要怎麼去解決呢?在C#中的編碼似乎跟C/C++有所不同,這方面希望能與版主討論或是指導一下,

謝謝。

藍斯洛 提到...

我記得的是,要用 GCHandle.Alloc() 配置一塊 Pinned 記憶體,再使用AddrOfPinnedObject()取出記憶體位址

Unknown 提到...

喔~你說要先配置一快記憶體呀,這是新的方式我沒看過,可是一般不是傳字串及長度給C/C++處理,然後再C/C++再回傳字串給C#做接收及輸出;所以很多資料都在做轉換上的配置不是嗎?

能否請版主給個小範例呢?

謝謝。

藍斯洛 提到...

C# 的 managed 物件,是不能直接存取記憶體位址的,必須先鎖住它。從釘住(Pinned) 的物件Handle,再存取記憶體位址。
底下這個可以參考:
http://blogs.msdn.com/b/clyon/archive/2004/09/17/230985.aspx

匿名 提到...

您好~ 在此先感謝
版主寫得真的好詳細
小妹受益良多 (((跪
不好意思 因為我是完全沒碰過unity3D的初學者
最近想要嘗試AR的製作
也上網看了很多的教學
但是現在卡在我的unity3D的build setting無法點選android
也試過將一些Plug-in匯進去了
也去過官網安裝相關的套件了
但還是無法點選
想要請問版主 是和一些安裝程序出問題有關嗎?
還是在環設上有要特別注意什麼? ><
萬分感謝!!!

藍斯洛 提到...

那個.... 如果 Unity並沒有Android 版本的授權,是沒辦法切換到 Android Build的。

Patrick Cheng (ccp) 提到...

我在Android開發有點資歷,不過在遊戲領域,是個全然新人,勉強的經驗,就是研究 SDK 附的 貪吃蛇範例。近日為研究 Unity ,來到你的 bolg,也算是有緣。發現你的文章,對後進者幫助很大。

期望你能持續寫下去,有心人,終將有回報的。