平時我們都會用手機玩游戲,而有些方式是用Unity3D來開發(fā)的。這次我們來探究一下,Unity3D的android游戲是怎么形成的。
一.配置開發(fā)環(huán)境
首先要說明的一點是unity3D游戲的打包不是android接入游戲內(nèi)容打包的,而是游戲接入android內(nèi)容打包的,就是說一般是在unity中進行打包,而不是在androidstudio中,當然也可以在unity中導出項目用androidstudio打包。
首先下載unity開發(fā)環(huán)境。

在官網(wǎng)找到窮B版下載,然后跟著流程安裝就行,如果沒有Visual Studio的話,在安裝過程中會順便幫你安裝,至少我下載的版本是這個的,環(huán)境大概占4G多內(nèi)存,一定要留出足夠的空間。

打開后發(fā)現(xiàn)主要分兩部分,上面是畫布,下面是放腳本。
之后打開AndroidStudio,準備寫代碼。
二.Unity3D對接android
兩塊的對接,主要是把android代碼丟到Unity中,android代碼可以打jar包,也可以打aar,我這里主要講jar包
1.android部分
先說說android部分,要讓unity游戲展示在android的Activity中,需要讓這個Activity繼承UnityPlayerActivity。當然我們默認的androidSDK中是沒有這個類的,我們可以從之前下載的unity中找到。
這個包的位置在
你下的unity的文件\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes

把這個jar包放到android項目中引用就行。還需要注意的一點是這個包的路徑不一定在這個文件夾下,我不能保證之后的版本不會變,當前我用的版本是在這里,但不管怎樣都可以在Unity中找到。
我android端的代碼很簡單,就寫一個方法用Log打印一句話。
public class MainActivity extends UnityPlayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void say(String str){
Log.v("mmp",str);
}
}
還需要注意的一點是,這個Activity是供Unity的游戲展示的,所有不能寫setContentView方法,不然顯示的就是本地的xml頁面
這樣android端就寫完了,之后我們打jar包。在gradle中加入jar包命令
task makeJar(type: Jar) {
archiveName = 'wang.jar' //最終jar包的名稱
from(project.zipTree('build/intermediates/packaged-classes/release/classes.jar')) //需要打的juhepaysdk的編譯文件
destinationDir = file('build/libs') //jar包輸出路徑
}
makeJar.dependsOn(build)
關(guān)于打jar包,很多人之前說在以前的bundle目錄中找不到classes.jar,是因為現(xiàn)在的classes.jar換成了在packaged-classes目錄下,而且我們也依然不能保證以后的版本不會換,反正找不到的話可以自己在intermediates目錄下找。
還有一點是我僅僅打了代碼到j(luò)ar包中,沒有和引用到的jar包一起打,因為引用的包可以直接扔到unity中,沒必要一起打包,之前網(wǎng)上也有人說一起打也不會影響。
打完jar包之后android這邊算是完成了,接下來看看unity端要怎么做處理。
2.unity部分
我們先畫圖,關(guān)于Unity3D其實我不太知道怎么用,但是畫一個Button還是難不倒的。

右鍵Canvas然后選UI再選Button,在右邊欄可以選擇這個UI的屬性。

我們先把這個unity的范圍設(shè)置成我們手機的范圍,這個會更清楚的看出按鈕顯示在手機上的預覽圖。按照圖中的順序設(shè)置

在中間的預覽窗口點擊Game,并且在圖中的位置設(shè)置尺寸,我這里設(shè)置1920 x 1080豎屏,然后把按鈕拖到中間。
這樣我們算是把圖簡單畫好了,接下來我們給控件設(shè)置腳本。我對Unity的開發(fā)不是很了解,但是好像他能為每個對象設(shè)置一個腳本。我們用C#寫一個腳本設(shè)置給上面的Canvas對象,這樣這個對象就能執(zhí)行這個腳本,注意是設(shè)置給最外層的Canvas對象而不是里面的Button對象。
(1)先寫C#腳本
public class Test : MonoBehaviour
{
private AndroidJavaClass jc;
private AndroidJavaObject jo;
// Start is called before the first frame update
void Start()
{
jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
GameObject btnObj = GameObject.Find("Canvas/Button");
Button btn = (Button)btnObj.GetComponent<Button>();
btn.onClick.AddListener(onClick);
}
void onClick()
{
jo.Call("say", "fuck");
}
// Update is called once per frame
void Update()
{
}
}
不懂C#也沒關(guān)系,至少寫一個點擊事件還難不倒我們,代碼中腳本名為Test,AndroidJavaClass和AndroidJavaObject是C#和java通訊的類,這兩個類的命名真的是教科書般的命名。
jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
獲取到android類是這樣的固定寫法,調(diào)用類中的方法用Call函數(shù),第一個參數(shù)是java那邊定義的方法名,后面的參數(shù)是方法的參數(shù)。
GameObject btnObj = GameObject.Find("Canvas/Button");
Button btn = (Button)btnObj.GetComponent<Button>();
btn.onClick.AddListener(onClick);
這里是獲取到Canvas下的Button對象,然后設(shè)置點擊事件。這里只是簡單的講,有興趣的可以自己去找詳細的API。
代碼寫好之后把腳本添加給Canvas。

點擊1中的Add Component,選著Test腳本,添加后就像2一樣顯示。同樣,如果想移除腳本的話可以右鍵2的地方選著移除。
這樣就把腳本添加成功了。那么現(xiàn)在是怎樣的情況呢?點擊按鈕,就會執(zhí)行腳本中的監(jiān)聽事件,調(diào)用AndroidJavaObject的Call方法,調(diào)用到android中的say方法。
最后,我們需要把android中的內(nèi)容添加到unity中,最后打包的時候才會一起打進去。

如圖一樣,在Assets中創(chuàng)建Plugin文件夾,在創(chuàng)建Android文件夾,下面創(chuàng)建libs和res。

libs中放入wang.jar,就是我們上面打的包,wang.jar里面是不是調(diào)用了android的東西,我這里用的是v4,所以要把一個android的v4包也放進去,不是也用了unity的jar嗎?沒錯,但是這個在打包時會提供,所以這里我們沒必要重復放unity的jar包進去。注意了,不放unity的jar包進去。
res下就放android項目中res下的內(nèi)容。
最后還要把manifest文件放到android文件夾下

這樣就只剩最后的打包了。
三.打包
上邊已經(jīng)把android的內(nèi)容加到unity中了,我們就可以在Unity中打android的apk包了。
還是多說一句,記得要在Edit -》Preferences -》External Tools中設(shè)置SDK路徑

打包方法是點擊File -》 Build Settings

選著player setting,在3中設(shè)置得和項目中的gradle一樣。
可以看出在Build System中選中的是Gradle,所以unity最后會調(diào)用Gradle工具來進行打包。
打包過程中如果出錯的話,可以在Console中查看錯誤日志

這里再講一個比較坑的地方
我第一次打包時報了一個錯,報文件重復。
CommandInvokationFailure: Gradle build failed.
D:\u3d\Editor\Data\PlaybackEngines\AndroidPlayer/Tools\OpenJDK\Windows\bin\java.exe -classpath "D:\u3d\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-4.6.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"
stderr[
D8: Program type already present: com.example.kylin.androidunitydemo.BuildConfig
java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: D:\testu3d\TestOne\Temp\gradleOut\build\intermediates\transforms\dexBuilder\release\8, D:\testu3d\TestOne\Temp\gradleOut\build\intermediates\transforms\externalLibsDexMerger\release\0, D:\testu3d\TestOne\Temp\gradleOut\build\intermediates\transforms\dexBuilder\release\7.jar
Program type already present: com.example.kylin.androidunitydemo.BuildConfig
Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.
我按照提示找到文件夾,發(fā)現(xiàn)重復的文件是BuildConfig。于是我的做法是把jar包的BuildConfig刪除(先解壓,再刪除,再壓縮)就正常了,合著unity打包的時候還會給你打進去一個BuildConfig。
雖然我能解決了這個問題,但不知道有沒有更好的方法,不然難道每次都要刪除jar包的文件嗎,這樣豈不是很麻煩。
按照上面的操作,我們就能打出一個可以運行的apk文件了,我測試過可以運行,Gif軟件被我刪了,這里就不演示了,不過我們可以反編譯看看unity做處理之后的結(jié)構(gòu)??梢钥闯龆嗔撕芏辔募?/p>
