Unity和Android混合開發

通用的流程

https://blog.csdn.net/zhangdi2017/article/details/65629589java


應用場景

Unity遊戲中一些功能須要安卓系統的支持,如搜索wifi等。並且想接入SDK時,不少都是針對安卓的SDK,不多有針對Unity的,因此必需要學習Unity和Android的互調。android

網上能搜到不少相關的內容,但大多因爲年代久遠,Unity和Android Studio版本更新等問題,致使各類無盡的報錯讓人崩潰,因此仍是要記錄一下。app

環境

  • Unity5.6或2017.3.0f3 + JDK1.8.0_131 + Android Studio3.0.1 + gradle-4.1-all + Visual Studio2017
  • Unity2017高版本取消了Internal的打包方式。
  • VS還好,另外幾個要當心各類版本感人莫名其妙的Bug兼容問題!!!!!
  • JDK能夠是7或8,必定不能是9!
  • Android Studio上次手賤點了升級,升到v3.1後打包出來目錄結構有變更,找不到jar包的位置。且打出的aar包在Unity中調用顯示找不到目標方法。

Unity與Android的交互有兩種思路

http://www.sikiedu.com/course/137/task/4910/show#
1、Unity作好項目以後導出爲Android Studio項目,導入到Android Studio中進行以後的功能開發。最後由Android Studio打包APK。即Unity輔助Android開發(Android開發爲主),對Android技能要求較高。
2、Android Sutido作好項目導出jar或aar包,導入到Unity中做爲Unity的插件使用,最後由Unity打包APK。即Android輔助Unity開發(Unity開發爲主),對Unity技能要求較高。編輯器

Unity打包APK時,調用安卓SDK,把全部遊戲內容整合打包出的APK中只有一個MainActivity。函數

1、導出Jar包 + 擴展MainActivity + Java主導

Unity調安卓

複雜度 4.5★    通用度 4.5★    注:官方已經再也不推薦這種方法。
http://www.sikiedu.com/course/137/task/4911/show#
一、打開Android Studio新建一個項目,新建一個模塊(Module),取名UnityAndroidLibrary。注意選擇最小SDK16,由於Unity最小支持的是16。
二、在該模塊(ProjectName/UnityAndroidLibrary/src/main/java/packageName/)下新建一個Empty Activity。建立時勾上Launcher Activity。
三、刪除跟該界面一同生成的activity_main.xml佈局文件(由於以後佈局歸Unity管理),同時刪除該模塊MainActivity中onCreate()裏調用setContentView()方法。
四、進入Unity的安裝目錄(如D:\Unity 5.4.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes下)複製classes.jar文件,粘貼到該模塊UnityAndroidLibrary/libs目錄下。右鍵該jar選擇Add as Library,選Add to Module UnityAndroidLibrary。
五、打開UnityAndroidLibrary模塊的AndroidManifest.xml清單文件,該文件會覆蓋掉Unity的一些設置,修改以下。(從默認的app模塊中的清單文件拷貝過來,把報錯的地方去掉便可。記得加後面的meta-data節點)工具

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="guxin.demo.unityandroidlibrary">

    <application android:allowBackup="true" android:label="UnityAndroidTest" android:supportsRtl="true">
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                <meta-data android:name="unityplayer.UnityActivity" android:value="true"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

六、回到模塊的MainActivity,修改該類繼承自UnityPlayerActivity。在該類中添加自定義的方法,用於給Unity調用。如:oop

public int add(int a, int b){ return a + b; }

七、在AS中Project目錄選中unityandroidlibrary,在Build菜單下選Make Module ‘unityandroidlibrary’單獨編譯這個模塊。
八、在unityandroidlibrary/build/intermediates/bundles/debug目錄右鍵Show in Explorer。刪除debug/libs/classes.jar(等同於剛從Unity那邊拷過來的內容),把debug/classes.jar拖到debug/libs中(這個是包含了剛新增的擴展方法的)。把libs和res這兩個文件夾備份(如複製到桌面)。 佈局

 

九、在unityandroidlibrary/build/intermediates/manifests/full/debug/AndroidManifest.xml右鍵Show in Explorer,也把這個清單文件複製出來(如複製到桌面)。打開在桌面的副本,修改package包名爲在Unity中想要的包名,如包名最後一段改成unityandroidtest(注意包名要所有小寫)。學習

十、打開Unity,建立一個工程UnityAndroidTest,Build Settings切換爲安卓平臺,Player Settings中修改包名,包名同上一步的一致(先調平臺再調包名)。在Assets下新建文件夾Plugins/Android(名字固定的,當心別漏了s),將上兩步獲得的三個文件拖到該文件夾中。gradle

十一、新建一個C#腳本,VS打開編輯以下。把該腳本掛到任一場景中的遊戲對象上(如Main Camera)。

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Test : MonoBehaviour { public Text text; // 用於展現調用結果

    void Start () { // 得到位於com.unity3d.player包下的UnityPlayer類,固定寫法。
        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");   // 參數是包名+類名 // 得到jc所表明的類裏的currentActivity對象,固定寫法。這是Unity提供的classes.jar中的功能,可經過currentActivity獲取到安卓端表明MainActivty的對象。
        AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); // 調用MainActivty中的自定義方法。
        text.text = jo.Call<int>("add", 1, 2).ToString(); } }

十二、最後一步,連上真機並打開USB調試,Build & Run,路徑選擇桌面。此時會把APK輸出到桌面並安裝到真機上運行,便可看到調用結果。下圖是我用AS的安卓模擬器(AVD)運行的效果。注意了,要打包發佈出來用,直接在Unity編輯器點運行會報錯,但打包出來安卓機運行沒有問題的。

安卓調Unity

繼續上面的工程。

一、在Unity剛纔的腳本中,添加一個方法,給安卓調用。

// 交由安卓調Unity。
public void ChangeColor() { text.color = Color.red; }

二、在安卓端unityandroidlibrary模塊的MainActivity類中,修改add()方法。該方法由Unity調用,方法中再由安卓調Unity中的方法,從而實現二者的互調。

// Unity調安卓
public int add(int a, int b){ // 安卓調Unity
    UnityPlayer.UnitySendMessage("Main Camera", "ChangeColor", ""); // 參數:GameObject名 + 方法名 + 參數
    return a + b; }

三、從新把該模塊編譯。build菜單下選Make Module 'unityandroidlibrary'。一樣是在unityandroidlibrary/build/intermediates/bundles/debug目錄右鍵Show in Explorer,拖拽debug/classes.jar到libs中替換掉libs/classes.jar。把libs和res文件夾拷貝到桌面。而後放到Unity中替換掉以前Assets/Plugins/Android目錄下的libs和res文件夾。(AndroidManifest.xml能夠不用替換)

四、Build & Run 。效果以下。能夠看到Text文本顏色變紅色了。

 

關於APP在手機桌面上顯示的軟件名稱

當Assets/Plugins/Android/AndroidManifest.xml中的android:label節點指定的軟件名,與Unity的Player Settings中Product Name指定的軟件名不一致時,最終打出的APK包之前者爲準!以下圖。

若是上面二者不一致,以安卓清單中的軟件名爲準。

 

2、導出aar包 + 擴展MainActivity + Java主導

複雜度 4★    通用度 4.5★    注:官方推薦

http://www.sikiedu.com/course/137/task/4915/show#

一、接着上面的項目繼續。跟前面導出Jar包的區別是,導出Jar包時(已放到Unity中Assets/Plugins/Android/libs/classes.jar),res文件夾不能跟jar打包到一塊兒,因此Unity2017推薦咱們導出aar包(其實解壓後就是jar + res)。

二、爲演示aar包中包含了資源,在unityandroidlibrary模塊的清單文件中加上icon(從另外一個app模塊的複製),把相應的res/mipmap也複製過來。Build菜單選擇‘Build Module unityandroidlibrary’。

三、編譯完成後,此次不須要再像上面導出jar那樣去找res和libs文件夾,直接找到unityandroidlibrary/build/outputs/aar文件夾,裏面就是導出的aar包。把這個aar包和清單文件(仍是在unityandroidlibrary/build/intermediates/manifests/full/debug/AndroidManifest.xml)一併拷貝到桌面。

四、打開桌面的清單文件,修改成想要的包名。例如最後一段改成unityandroidtest(全小寫,跟Unity中的保持一致)。

五、打開桌面的aar包(zip壓縮工具可查看),注意,把包里根目錄的classes.jar移動到libs裏覆蓋裏面較大的classes.jar。(複製粘貼,而後刪掉libs外面的)

六、觀察aar包內容可知,相比導出jar包的方式(只有libs、res、清單xml)多了aidl、jni(提供安卓的高級功能)和R文件、assets(資源文件),這也是Unity推薦使用導出aar替代導出jar的緣由之一。

七、把桌面的清單xml文件剪切到Unity的Assets/Plugins/Android/裏,這份xml是指導Unity打包APK的總清單,它的包名須要與Unity裏寫的一致,相對aar包來講是外部的清單。

八、打開桌面aar包裏的xml清單文件,刪除android:icon和android:label這兩行並保存。由於這個xml清單是該模塊本身的,爲了防止跟aar包外的總清單衝突。

九、最後,再把改好的aar包導入Unity的Assets/Plugins/Android中,項目結構以下。能夠看到Unity把aar包識別成了一個插件。

十、啓動安卓模擬器,Unity執行Build & Run後便可看到效果。與導出jar包的運行結果一致。

注意:可能會出現Unity能導出APK,可是不能安裝到模擬器上的,Unity報錯以下。

 直接把APK拖到模擬器中安裝,模擬器報錯以下。

緣由是模擬器上面有過該軟件的早期版本,要先卸載了才能再裝新版本上去。

 

關於I/Unity: AndroidJavaException: java.lang.NoSuchMethodError: no non-static method with name='add' signature='(II)I' in class Ljava.lang.Object;

03-30 02:08:29.073 3957-3972/guxin.demo.unityandroidtest I/Unity: AndroidJavaException: java.lang.NoSuchMethodError: no non-static method with name='add' signature='(II)I' in class Ljava.lang.Object; java.lang.NoSuchMethodError: no non-static method with name='add' signature='(II)I' in class Ljava.lang.Object; at com.unity3d.player.ReflectionHelper.getMethodID(Unknown Source) at com.unity3d.player.UnityPlayer.nativeRender(Native Method) at com.unity3d.player.UnityPlayer.c(Unknown Source) at com.unity3d.player.UnityPlayer$c$1.handleMessage(Unknown Source) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:154) at com.unity3d.player.UnityPlayer$c.run(Unknown Source) at UnityEngine.AndroidJNISafe.CheckException () [0x00000] in <filename unknown>:0 at UnityEngine.AndroidJNISafe.CallStaticObjectMethod (IntPtr clazz, IntPtr methodID, UnityEngine.jvalue[] args) [0x00000] in <filename unknown>:0 at UnityEngine.AndroidReflection.GetMethodMember (IntPtr jclass, System.String methodName, System.String signature, Boolean isStati

升級到Android Studio3.1後會出現這個問題,打包出來aar,在Unity中調用顯示找不到add()方法!重裝Android Studio3.0.1版本。

若是還有該報錯,從新新建一個工程,或改用Internal來取代Gradle打包。(高版本Unity2017將取消Internal打包方式)

http://www.sikiedu.com/course/137/thread/1372

3、互調模式之提供Java擴展類

提供額外的類 + Java主導       複雜度 3★    通用度 3.5★

適用場景:在安卓中用Java執行一些操做,且不須要資源(圖片等)的狀況。能夠把Java代碼封裝成jar後導入Unity中供Unity調用。因爲在安卓端編寫代碼,可使用安卓的各類高級語法(監聽器等)。但因爲Test類不能用安卓上下文Context對象,能力有限,因此通用性不高(想傳入安卓上下文對象須要額外的操做)。跟上一種方法相比,不是擴展MainActivity,而是另寫到一個類中(也所以失去了上下文)。

 

一、打開AS新建一個工程叫SimpleClass,新建一個Module(選Android Library)取名SimpleLibrary。把Unity的classes.jar包導入到該模塊中(跟上面同樣,這是讓安卓能調用Unity的標準操做,即若只想Unity調安卓能夠不導入該classes.jar)。

二、新建一個Test類。在該擴展類中編寫跟Unity交互的方法。

public class Test { public int add(int a, int b){ UnityPlayer.UnitySendMessage("Main Camera", "ChangeColor", ""); return a + b; } }

三、Build該模塊後,切到Project視圖,把simplelibrary/build/intermediates/bundles/debug/classes.jar文件拖到Unity中Asset/Plugins/Android目錄下。

四、複製到Unity後,能夠給改jar包更名,如改成test.jar。

五、Unity中調用該方法。非靜態方法由類的對象來調用。

void Start () { // 得到安卓中test.jar中的Test類對象
    AndroidJavaObject jo = new AndroidJavaObject("me.guxin.simplelibrary.Test"); // 參數:包名,調用空參構造函數 // 調用類中的自定義方法。
    text.text = jo.Call<int>("add", 1, 2).ToString(); /* 若是的靜態方法 // 得到安卓中test.jar中的Test類 AndroidJavaClass jc = new AndroidJavaClass("me.guxin.simplelibrary.Test"); // 調用靜態方法 text.text = jc.CallStatic<int>("add", 1, 2).ToString(); */ }

六、打包APK後丟到安卓模擬器中運行,便可看到運算結果。


4、互調模式之C#主導式調用

特色:Unity中設置了安卓SDK路徑後,能夠直接調用安卓SDK中的原生方法,不用從AS中導出插件再導入Unity中。但C#中用反射的寫法不能使用Java的一些高級語法。

對於不熟悉安卓開發的同窗,能夠打開Android Studio新建一個空工程,查看安卓Log()方法的簽名,再在Unity中照着安卓的結構寫C#代碼調用安卓SDK原生方法。

手動打印的安卓錯誤日誌,能夠在AS的Log Cat中看到輸出。

void Start () { AndroidJavaClass jc = new AndroidJavaClass("android.util.Log"); // 安卓SDK中的包名
    text.text = jc.CallStatic<int>("e", "UnityAndroidTest", "ErrorTest").ToString(); // 至關於在安卓中寫 Log.e("UnityAndroidTest", "ErrorTest"); }

5、各類模式的適用狀況

(圖來源 http://www.sikiedu.com/course/137/task/4918/show)

Unity中接安卓SDK一般使用的組合:導出aar + 拓展MainActivity + Java主導 。由於第三方SDK一般是爭對安卓平臺作的。

簡單功能可用組合: Class拓展 + C#主導 。適用於簡單功能,如調用安卓土司Toast。好處是不用在AS導出項目給Unity當插件使用。


 

參考:

相關文章
相關標籤/搜索