Unity Splash黑屏问题解决——使用Android Studio实现Splash

Unity Splash之前会有几秒的黑屏,即使是打个空包也会有一两秒,很难接受,所以要解决这个问题必须写Java代码,在安卓app启动时展示Splash logo,而不是在Unity activity中展示。

Unity Splash原理

Unity的Splash就是Unity logo或者你自定义的logo出现的那个界面。Splash界面会在第一个场景加载前出现,直到第一个场景Awake的时候才关闭。

实际运行我们会发现,即使是空工程也会先黑屏1-2秒再显示Splash,然后才会进入首个场景。这个原因是打出来的apk包需要先调用UnityPlayerActivity,在UnityPlayerActivity出来之前当然是黑屏。

解决思路

要想打开应用程序直接显示logo,只有在Java层写一个继承UnityPlayerActivity的Activity用于启动,创建时直接显示Java层的Splash。

再写一个用来管理Splash加载、显示、隐藏的类。加载Splash图片新建View之后添加到Unity的View里,并且设置View的长宽为Unity视图的长宽,这样可以保证不会变形。

实际情况下只需要写一个继承UnityPlayerActivity的类就足够了,写两个函数分别控制logo的显示与隐藏即可。

软件版本

Unity 2017.4.16f1 64bit

Android Studio 3.6.1

解决过程

Android Studio 3.6.1 新建Module

image.png

把Unity的classes.jar复制到Module的libs文件夹中,classes.jar的位置在\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar

image _1_.png

然后把jar包添加到依赖,这样在Java中才可以调用Unity的接口,继承UnityPlayerActivity。

image _2_.png

image _3_.png

9e5f7a37-9742-4207-a277-0d0736539be9.png

给Module添加ActivityClass,之后导出的jar包就是这个class。

image _4_.png

image _5_.png

Name可以随便写一个,点OK生成。

代码结构

类名我用的是SplashActivity,代码结构如下,后面会放完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.test.splash;

public class SplashActivity extends UnityPlayerActivity {
private ImageView logoView = null;

@Override
protected void onCreate(Bundle bundle){
super.onCreate(bundle);
onShowSplash();
}

public void onShowSplash(){

}

public void onHideSplash(){

}
}

首先SplashActivity要继承UnityPlayerActivity,这样可以在UnityPlayerActivity创建的时候调用到SplashActivity的onCreate,以此实现尽快的展示Splash logo。

然后写一个onHideSplash方法提供给Unity调用,用来隐藏logo。

资源放置

logo的资源需要先放到Java项目中,要不然没法调用,资源应该放在图中的main\res\drawble目录下。

Snipaste_2020-06-01_20-31-56.jpg

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.test.splash;

import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.ImageView;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class SplashActivity extends UnityPlayerActivity {
private ImageView logoView = null;

@Override
protected void onCreate(Bundle bundle){
super.onCreate(bundle);
onShowSplash();
}

public void onShowSplash(){
Log.d("Unity","----------------onShowSplash");
if(logoView != null){
Log.d("Unity","------------------logoView==null");
return;
}
try{
//获取屏幕尺寸信息计算缩放
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
float scaleX = dm.widthPixels/1366f;
float scaleY = dm.heightPixels/768f;
Log.d("Unity", "Screen Width:"+dm.widthPixels+";Screen Height:"+dm.heightPixels);

//创建ImageView
logoView = new ImageView(this);
//资源是logo.png
logoView.setImageResource(R.drawable.logo);

//添加View到布局容器
mUnityPlayer.addView(logoView);
Log.d("Unity","------------------addView");

//根据比例缩放
logoView.setScaleX(scaleX);
logoView.setScaleY(scaleY);
} catch (Exception e)
{
e.printStackTrace();
}
}

public void onHideSplash()
{
try {
if(logoView != null){
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run()
{
Log.d("Unity", "onHideSplash run");
mUnityPlayer.removeView(logoView);
logoView = null;
}
});
}
}catch (Exception e)
{
e.printStackTrace();
}
}
}

AndroidManifest.xml

除了代码还要设置AndroidManifest,指定你写的activity所在的包名com.test.splash.SplashActivity,这样启动入口就变成你写的SplashActivity了

1
2
3
4
5
6
7
8
9
10
11
12
13
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.splash">
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
<application>
<activity android:name="com.test.splash.SplashActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

构建jar包

写完就要构建jar包给Unity使用了,要编辑splash的build.gradle,添加以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Copy类型,请在Terminal中运行gradlew makeJar
task makeJar(type: Copy) {
//删除存在的
delete 'build/libs/' + "splash.jar"
//设置拷贝的文件
from("build/intermediates/aar_main_jar/release")
//打进jar包后的文件目录,将classes.jar放入build/libs/目录下
into('build/libs/')
//要打包的jar文件
include('classes.jar')
//重命名
rename('classes.jar', "splash.jar")
}
makeJar.dependsOn(build)

点击Android Studio的菜单-View-Tool Windows-Gradle,右侧会出现gradle指令,找到Tasks中Other下面的makeJar双击构建jar包。

构建成功的话会在splash\build\libs目录下出现splash.jar

Unity调用

把构建好的jar包和AndroidManifest放到Unity工程路径的Assets\Plugins\Android目录下

logo图片放在Unity工程路径的Assets\Plugins\Android\res\drawable目录下

最终在Unity中合适的时机,调用onHideSplash关闭logo即可。

1
2
3
4
5
6
7
void Start () {
#if (UNITY_ANDROID && !UNITY_EDITOR)
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("onHideSplash");
#endif
}

参考

https://www.jianshu.com/p/8256c0da444a

https://www.jianshu.com/p/5c1f438e07f1

https://www.jianshu.com/p/2dd6cf0da2be