Skip to content

Display Unity Scene as a Transparent Background Sub View in Android Studio Project.

Notifications You must be signed in to change notification settings

yicm/HelloUnity3D

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

[TOC]

开发环境

  • Unity 2019.3.14f1
  • Android Studio 3.4.1
    • Android9.+(Q)
    • 28.0.3 Build Tools

1 Unity导出Android工程

设置场景背景透明:

背景色RGBA设置为0,0,0,0。

2 Unity场景显示为透明子视图

2.1 默认导出工程

  1. 默认导出的 Android Project 包含 launcherunityLibrary 两个模块:

  1. 默认导出的 Android Project 启动窗口为 unityLibrary 模块中的 UnityPlayerActivity

2.2 将导出的Unity场景作为子模块添加到其他工程中

  1. 新建一个 Empty Activity 项目(包名随意命名,无需跟unityLibrary包名一致)
  2. 我们将 activity_main.xml 设计成这样:

  1. 导入 unityLibrary 模块到工程中
  2. app和unityLibrary模块设置
    • 设置依赖 unityLibrary 的lib目录aar文件,到工程的 build.gradle:
    allprojects {
        repositories {
            google()
            jcenter()
            // here
            flatDir {
                dirs "${project(':unityLibrary').projectDir}/libs"
            }
        }
    }
    • unityLibrary 设置为默认 app 模块的依赖模块,到 appbuild.gradle:
    dependencies {
        // ......
        // here
        implementation project(path: ':unityLibrary')
    }
    • 此时会出现合并AndroidManifest.xml 主题(Theme)错误,因为两个模块使用了不同的主题配置,将unityLibraryAndroidManifest.xml <applicationandroid:theme="@style/UnityThemeSelector.Translucent" 去掉即可。
    • 同时我们需要将 unityLibrary 的默认启动 activity UnityPlayerActivity 配置注释掉:
    <application android:isGame="true">
    <activity android:name="com.unity3d.player.UnityPlayerActivity" android:theme="@style/UnityThemeSelector" android:screenOrientation="fullSensor" android:launchMode="singleTask" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density" android:hardwareAccelerated="false">
      <!-- 注释掉 -->
      <!--<intent-filter>-->
        <!--<action android:name="android.intent.action.MAIN" />-->
        <!--<category android:name="android.intent.category.LAUNCHER" />-->
        <!--<category android:name="android.intent.category.LEANBACK_LAUNCHER" />-->
      <!--</intent-filter>-->
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
    • 为了能够调用到 unityLibrary 中的jar文件,即复用另一个模块的jar,需要修改 unityLibrary 中的build.gradle
    dependencies {
        // 将implementation修改为api
        api fileTree(dir: 'libs', include: ['*.jar'])
        implementation(name: 'UnityAds', ext:'aar')
        implementation(name: 'UnityAdsAndroidPlugin', ext:'aar')
    }
  3. 显示为子视图应用实现
    • app 模块添加 butterknife 资源绑定组件,简化我们编码,即app build.gradle:
    android {
        compileSdkVersion 29
        // ButterKnife需要开启Java8哦
        // Butterknife requires Java 8.
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        // ......
    }
    dependencies {
        // ......
    
        implementation project(path: ':unityLibrary')
    
        // here
        // 记得用10.2.1哦,不要用8.x,会跟AndroidX不适配
        implementation 'com.jakewharton:butterknife:10.2.1'
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    }
    • res/values/strings.xml添加一个资源描述,否则后面会出现找不到资源直接甭掉:
    <resources>
        <string name="app_name">xxx</string>
        <!-- here -->
        <string name="game_view_content_description">Game view</string>
    </resources>
    • 到 app 模块 MainActivity.java 中添加应用代码,注意onResumeonConfigurationChanged要重载:
    public class MainActivity extends AppCompatActivity {
        public final static String TAG = "MainActivity";
    
        @BindView(R.id.textView) TextView tv_test;
    
        private Unbinder mUnbinder;
        private UnityPlayer mUnityPlayer;
        private View mPlayerView;
        private LinearLayout mPlayerLayout;
        LinearLayout.LayoutParams mPlayerLayoutParams;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mUnbinder = ButterKnife.bind(this);
    
            // Create the UnityPlayer
            mUnityPlayer = new UnityPlayer(this);
    
            // Transparent background
            if (mUnityPlayer.getChildCount() > 0 && mUnityPlayer.getChildAt(0) instanceof SurfaceView) {
                SurfaceView surfaceView = ((SurfaceView) mUnityPlayer.getChildAt(0));
                surfaceView.setZOrderOnTop(true);
                surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
            }
    
            mPlayerView = mUnityPlayer.getView();
    
            // Add the Unity view SLIGHTY MODIFIED
            mPlayerLayout = (LinearLayout)findViewById(R.id.player_linear_layout);
            mPlayerLayoutParams = (LinearLayout.LayoutParams) mPlayerLayout.getLayoutParams();
    
            mPlayerLayout.addView(mPlayerView);
            mUnityPlayer.requestFocus();
        }
    
        @OnClick({R.id.player, R.id.launcher})
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.player:
                    tv_test.setText("Player");
                    UnityPlayer.UnitySendMessage("Player", "Jump", "");
                    break;
                case R.id.launcher:
                    tv_test.setText("Launcher");
                    UnityPlayer.UnitySendMessage("Launcher", "CreateBallPrefab", "");
                    break;
                default:
                    break;
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mUnbinder != null) {
                mUnbinder.unbind();
            }
        }
    
        // Pause Unity
        @Override protected void onPause()
        {
            super.onPause();
            mUnityPlayer.pause();
        }
    
        // Resume Unity
        @Override protected void onResume()
        {
            super.onResume();
            // step1
            mUnityPlayer.resume();
        }
    
        // Low Memory Unity
        @Override public void onLowMemory()
        {
            super.onLowMemory();
            mUnityPlayer.lowMemory();
        }
    
        // Trim Memory Unity
        @Override public void onTrimMemory(int level)
        {
            super.onTrimMemory(level);
            if (level == TRIM_MEMORY_RUNNING_CRITICAL)
            {
                mUnityPlayer.lowMemory();
            }
        }
    
        // This ensures the layout will be correct.
        @Override public void onConfigurationChanged(Configuration newConfig)
        {
            super.onConfigurationChanged(newConfig);
            // step2
            mUnityPlayer.configurationChanged(newConfig);
        }
    
        // Notify Unity of the focus change.
        @Override public void onWindowFocusChanged(boolean hasFocus)
        {
            super.onWindowFocusChanged(hasFocus);
            mUnityPlayer.windowFocusChanged(hasFocus);
        }
    
        // For some reason the multiple keyevent type is not supported by the ndk.
        // Force event injection by overriding dispatchKeyEvent().
        @Override public boolean dispatchKeyEvent(KeyEvent event)
        {
            if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
                return mUnityPlayer.injectEvent(event);
            return super.dispatchKeyEvent(event);
        }
    
        // Pass any events not handled by (unfocused) views straight to UnityPlayer
        @Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }
        @Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }
        @Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }
        /*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }
    }
  4. 最后结果

3 代码分析

我们在Unity工程中的Game Object 脚本中加了两个自己定义的成员函数:

public class Launcher : MonoBehaviour
{
    public GameObject ballPrefab;
    // Use this for initialization
    void Start() { // ...... }

    // Update is called once per frame
    void Update() { // ...... }

    // 自定义成员函数:创建小球
    void CreateBallPrefab()
    {
        Instantiate(this.ballPrefab);
    }
}
public class Player : MonoBehaviour
{
    protected float jump_speed = 5.0f;

    // Start is called before the first frame update
    void Start() { // ...... }

    // Update is called once per frame
    void Update() { // ...... }

    // 自定义成员函数
    void Jump()
    {
        this.GetComponent<Rigidbody>().velocity = Vector3.up * this.jump_speed;
    }
}

导出Android Project后,可以通过下面方式调用:

UnityPlayer.UnitySendMessage("GameObjectName", "MethodName", "parameter to send");

我们Android工程中通过使用两个按钮触发点击事件调用下面两个动作,从而操作Unity场景中的物体:

UnityPlayer.UnitySendMessage("Player", "Jump", "");
UnityPlayer.UnitySendMessage("Launcher", "CreateBallPrefab", "");

4 Github代码

https://github.com/yicm

5 参考链接

About

Display Unity Scene as a Transparent Background Sub View in Android Studio Project.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages