平时我们都会用手机玩游戏,而有些方式是用Unity3D来开发的。这次我们来探究一下,Unity3D的android游戏是怎么形成的。
一.配置开发环境
首先要说明的一点是unity3D游戏的打包不是android接入游戏内容打包的,而是游戏接入android内容打包的,就是说一般是在unity中进行打包,而不是在androidstudio中,当然也可以在unity中导出项目用androidstudio打包。
首先下载unity开发环境。
在官网找到穷B版下载,然后跟着流程安装就行,如果没有Visual Studio的话,在安装过程中会顺便帮你安装,至少我下载的版本是这个的,环境大概占4G多内存,一定要留出足够的空间。
打开后发现主要分两部分,上面是画布,下面是放脚本。
之后打开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)
关于打jar包,很多人之前说在以前的bundle目录中找不到classes.jar,是因为现在的classes.jar换成了在packaged-classes目录下,而且我们也依然不能保证以后的版本不会换,反正找不到的话可以自己在intermediates目录下找。
还有一点是我仅仅打了代码到jar包中,没有和引用到的jar包一起打,因为引用的包可以直接扔到unity中,没必要一起打包,之前网上也有人说一起打也不会影响。
打完jar包之后android这边算是完成了,接下来看看unity端要怎么做处理。
2.unity部分
我们先画图,关于Unity3D其实我不太知道怎么用,但是画一个Button还是难不倒的。
右键Canvas然后选UI再选Button,在右边栏可以选择这个UI的属性。
我们先把这个unity的范围设置成我们手机的范围,这个会更清楚的看出按钮显示在手机上的预览图。按照图中的顺序设置
在中间的预览窗口点击Game,并且在图中的位置设置尺寸,我这里设置1920 x 1080竖屏,然后把按钮拖到中间。
这样我们算是把图简单画好了,接下来我们给控件设置脚本。我对Unity的开发不是很了解,但是好像他能为每个对象设置一个脚本。我们用C#写一个脚本设置给上面的Canvas对象,这样这个对象就能执行这个脚本,注意是设置给最外层的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#也没关系,至少写一个点击事件还难不倒我们,代码中脚本名为Test,AndroidJavaClass和AndroidJavaObject是C#和java通讯的类,这两个类的命名真的是教科书般的命名。
jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
获取到android类是这样的固定写法,调用类中的方法用Call函数,第一个参数是java那边定义的方法名,后面的参数是方法的参数。
GameObject btnObj = GameObject.Find("Canvas/Button");
Button btn = (Button)btnObj.GetComponent<Button>();
btn.onClick.AddListener(onClick);
这里是获取到Canvas下的Button对象,然后设置点击事件。这里只是简单的讲,有兴趣的可以自己去找详细的API。
代码写好之后把脚本添加给Canvas。
点击1中的Add Component,选着Test脚本,添加后就像2一样显示。同样,如果想移除脚本的话可以右键2的地方选着移除。
这样就把脚本添加成功了。那么现在是怎样的情况呢?点击按钮,就会执行脚本中的监听事件,调用AndroidJavaObject的Call方法,调用到android中的say方法。
最后,我们需要把android中的内容添加到unity中,最后打包的时候才会一起打进去。
如图一样,在Assets中创建Plugin文件夹,在创建Android文件夹,下面创建libs和res。
libs中放入wang.jar,就是我们上面打的包,wang.jar里面是不是调用了android的东西,我这里用的是v4,所以要把一个android的v4包也放进去,不是也用了unity的jar吗?没错,但是这个在打包时会提供,所以这里我们没必要重复放unity的jar包进去。注意了,不放unity的jar包进去。
res下就放android项目中res下的内容。
最后还要把manifest文件放到android文件夹下
这样就只剩最后的打包了。
三.打包
上边已经把android的内容加到unity中了,我们就可以在Unity中打android的apk包了。
还是多说一句,记得要在Edit -》Preferences -》External Tools中设置SDK路径
打包方法是点击File -》 Build Settings
选着player setting,在3中设置得和项目中的gradle一样。
可以看出在Build System中选中的是Gradle,所以unity最后会调用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.
我按照提示找到文件夹,发现重复的文件是BuildConfig。于是我的做法是把jar包的BuildConfig删除(先解压,再删除,再压缩)就正常了,合着unity打包的时候还会给你打进去一个BuildConfig。
虽然我能解决了这个问题,但不知道有没有更好的方法,不然难道每次都要删除jar包的文件吗,这样岂不是很麻烦。
按照上面的操作,我们就能打出一个可以运行的apk文件了,我测试过可以运行,Gif软件被我删了,这里就不演示了,不过我们可以反编译看看unity做处理之后的结构。可以看出多了很多文件。