unity android 版本,Unity2019与Android混合开发

0. 开始前的版本对齐

Unity版本:Unity2019.3.4f1

AndroidStudio版本:3.5.3

1. Unity -- 准备项目

新建项目

打开File -> Build Setting

a68168463b29

File -> Build Setting

切换工程模式

首先选择Android Platform,然后点击Switch Platform切换工程模式。

a68168463b29

切换工程模式

导出Android工程

先勾上Export Project,否则下方的Export按钮会是一个Build,点击后Unity会直接导出一个Apk文件,而并不是一个Android Studio项目。

点击Export后,选择保存位置后会成功输出一个Android Studio项目,此时Unity的操作告一段落。

a68168463b29

导出工程

2. Android 打开项目

在使用Android studio 打开项目时,会跳出一个选择SDK的选项,此处我选择使用Android Studio’s SDK。Project’s SDK是Unity提供的,我觉得用此SDK可能对原生开发会有一定的影响。我并没有使用Project's SDK进行验证。

a68168463b29

sdk 选择

然后在弹出的Gradle 同步提示框中点击OK后项目就开始同步,如果无错误就可以进行开发了

3. Android 项目结构

Gradle同步完成后,可以看到以下目录(从Android视图切换为了Project)

a68168463b29

项目列表

其中launcher为平时Android开发中app主module,推荐在launcher主module中开发新的逻辑。(java目录需要自行创建)。

unityLibrary为Unity生成的子module。

在unityLibrary中包含一个UnityPlayerActivity的示例Activity,在不进行修改任何代码的时候默认启动的Activity就是这个UnityPlayerActivity。(可以在AndroidManifest中看到将这个activity配置成了启动Acitivity)

a68168463b29

image.png

而在unityLibrary module中的lib目录中可以看到有一个unity-classes.jar,一个非常重要的类UnityPlayer就是来自这个jar包。如果之前已经在Unity项目中添加过一些Android插件,在lib目录下也会出现这些其他的lib包。

那么我们来看下UnityPlayerActivity这个类

// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN

package com.unity3d.player;

import android.app.Activity;

import android.content.Intent;

import android.content.res.Configuration;

import android.graphics.PixelFormat;

import android.os.Bundle;

import android.view.KeyEvent;

import android.view.MotionEvent;

import android.view.View;

import android.view.Window;

import android.view.WindowManager;

import android.os.Process;

public class UnityPlayerActivity extends Activity implements IUnityPlayerLifecycleEvents

{

protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

// Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player

// The command line arguments are passed as a string, separated by spaces

// UnityPlayerActivity calls this from 'onCreate'

// Supported: -force-gles20, -force-gles30, -force-gles31, -force-gles31aep, -force-gles32, -force-gles, -force-vulkan

// See https://docs.unity3d.com/Manual/CommandLineArguments.html

// @param cmdLine the current command line arguments, may be null

// @return the modified command line string or null

protected String updateUnityCommandLineArguments(String cmdLine)

{

return cmdLine;

}

// Setup activity layout

@Override protected void onCreate(Bundle savedInstanceState)

{

requestWindowFeature(Window.FEATURE_NO_TITLE);

super.onCreate(savedInstanceState);

String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));

getIntent().putExtra("unity", cmdLine);

mUnityPlayer = new UnityPlayer(this, this);

setContentView(mUnityPlayer);

mUnityPlayer.requestFocus();

}

// When Unity player unloaded move task to background

@Override public void onUnityPlayerUnloaded() {

moveTaskToBack(true);

}

// When Unity player quited kill process

@Override public void onUnityPlayerQuitted() {

Process.killProcess(Process.myPid());

}

@Override protected void onNewIntent(Intent intent)

{

// To support deep linking, we need to make sure that the client can get access to

// the last sent intent. The clients access this through a JNI api that allows them

// to get the intent set on launch. To update that after launch we have to manually

// replace the intent with the one caught here.

setIntent(intent);

mUnityPlayer.newIntent(intent);

}

// Quit Unity

@Override protected void onDestroy ()

{

mUnityPlayer.destroy();

super.onDestroy();

}

// Pause Unity

@Override protected void onPause()

{

super.onPause();

mUnityPlayer.pause();

}

// Resume Unity

@Override protected void onResume()

{

super.onResume();

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);

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); }

}

其中UnityPlayer mUnityPlayer就是Unity最终绘制内容的View(是一个FrameLayout),而UnityPlayerActivity 将这个View设置为自己的根View,进行显示。所以也可以自定义一个任意大小的布局,将mUnityPlayer当做正常的View 添加到布局中,进行自定义大小的控制。

而UnityPlayerActivity 也重写了onResume、onPause等进行了对mUnityPlayer生命周期的管理。

4. Android与Unity跳转

一般情况下,混合开发都是会先启动原生界面,然后通过点击原生的中button根据业务逻辑跳转至包含Unity的Activity。这样我们就不能将UnityPlayerActivity设置为第一个启动的Activity。

取消UnityPlayerActivity默认启动

在AndroidManifest文件中删除或注释掉UnityPlayerActivity配置的下intent-filter

a68168463b29

删除intent-filter

小伙伴如果之前已经在Unity中导入了其他Android插件,那么这个AndroidManifest中显示的Activity应该是插件中自定义的Activity,而不是UnityPlayerActivity,注释掉相应的代码即可。

页面跳转

通过常规的startActivity即可启动UnityPlayerActivity

findViewById(R.id.btn_button1).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

Intent intent = new Intent(HomeActivity.this, UnityPlayerActivity.class);

startActivity(intent);

}

});

但是,当你finish到这个UnityPlayerActivity时你会发现,即使还有Activity显示,应用还是自动关闭了。这个问题是因为在UnityPlayerActivity中的onDestroy方法中调用了mUnityPlayer中destroy方法。

// Quit Unity

@Override protected void onDestroy ()

{

mUnityPlayer.destroy();

super.onDestroy();

}

我们点进mUnityPlayer.destroy()看一下

public void destroy() {

//...省略无用代码

if (this.mProcessKillRequested) {

if (this.m_UnityPlayerLifecycleEvents != null) {

this.m_UnityPlayerLifecycleEvents.onUnityPlayerQuitted();

} else {

this.onUnityPlayerQuitted();

}

Process.killProcess(Process.myPid()); // 结束自己的进程

}

unloadNative();

}

发现在mProcessKillRequested为true的时候,会进行一个杀自己进程的操作,而我们一般app都是一个进程,就会导致我们的app被kill掉。

解决办法就是在AndroidManifest配置一下UnityPlayerActivity,UnityPlayerActivity以一个新的进程启动。

android:process=":e.unitry3d"

5. Android 自定义Unity显示形式

由于业务的需求决定,混合开发中的Unity不一定为全屏幕显示或者可能需要多个Unity界面,那么就需要继承UnityPlayerActivity进行自定义一个显示Unity的界面。

当我们的业务需求决定了我们需要实现一个UnityPlayerActivity的子类进行扩展功能的时候,需要进行以下步骤:

禁止UnityPlayerActivity中添加mUnityPlayer

在UnityPlayerActivity的onCreate中注释setContentView和requestFocus代码,因为要在子类中按需加载mUnityPlayer,防止多次设置View,就注释掉父类的相关代码。

// Setup activity layout

@Override protected void onCreate(Bundle savedInstanceState)

{

requestWindowFeature(Window.FEATURE_NO_TITLE);

super.onCreate(savedInstanceState);

String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));

getIntent().putExtra("unity", cmdLine);

mUnityPlayer = new UnityPlayer(this, this);

//setContentView(mUnityPlayer);

//mUnityPlayer.requestFocus();

}

实现子类,将mUnityPlayer设置给布局

public class UnityActivity extends UnityPlayerActivity{

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.layout_unity);

FrameLayout frameLayout = findViewById(R.id.framelayout);

frameLayout.addView(mUnityPlayer);

mUnityPlayer.requestFocus();

}

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(mUnityPlayer);

mUnityPlayer.requestFocus();

}

这个地方需要注意两点:1.如果之前导入过插件,这里一定要继承自插件中实现的UnityPlayerActivity子类,否则,插件的方法不会被调用。2. 记得要将实现的Activity配置为新的进程。

如果想启动不同的Unity界面,也不需要实现多个Activity子类,和Unity开发约定下通信规则,确定好发送什么参数启动什么页面,在Activity启动后调用相关的方法,发送约定好的参数即可。

例如:

启动界面:

findViewById(R.id.btn_button1).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

Intent intent = new Intent(HomeActivity.this, UnityActivity.class);

intent.putExtra("panelName","LunchPanel");

startActivity(intent);

}

});

UnityActivity

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(mUnityPlayer);

mUnityPlayer.requestFocus();

String panelName = getIntent().getStringExtra("panelName");

UnityPlayer.UnitySendMessage("UIRoot","openPanel",panelName);//unity方法

}

6. 使用Fragment当做Unity显示的载体

目前我试出来的方案就是将mUnityPlayer在Fragment将要挂载的Activity中进行创建并进行生命周期的管理。

Activity

public class HomeActivity extends FragmentActivity {

protected UnityPlayer mUnityPlayer;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.home_activity);

mUnityPlayer = new UnityPlayer(this, null);

FragmentManager fragmentManager = getSupportFragmentManager();

fragmentManager.beginTransaction().add(R.id.fl,new UnityFragment(mUnityPlayer)).commit();

}

@Override protected void onNewIntent(Intent intent)

{

// To support deep linking, we need to make sure that the client can get access to

// the last sent intent. The clients access this through a JNI api that allows them

// to get the intent set on launch. To update that after launch we have to manually

// replace the intent with the one caught here.

setIntent(intent);

mUnityPlayer.newIntent(intent);

}

// Quit Unity

@Override protected void onDestroy ()

{

mUnityPlayer.destroy();

//mUnityPlayer.unloadNative();

super.onDestroy();

}

// Pause Unity

@Override protected void onPause()

{

super.onPause();

mUnityPlayer.pause();

}

// Resume Unity

@Override protected void onResume()

{

super.onResume();

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);

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); }

}

fragment

public class UnityFragment extends Fragment{

private UnityPlayer mUnityPlayer;

public UnityFragment(UnityPlayer unityPlayer) {

mUnityPlayer = unityPlayer;

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

return mUnityPlayer;

}

}

7. Unity与Android之间的通讯

此内容网络上已有较多文章,本文不再叙述。

8. 注意事项

当Unity与Android同时开发时,每次从Unity导出新的项目覆盖之前的老代码的时候主launcher中的AndroidManifest文件会被重置,导出前务必要备份。

文章可能因为个人能力原因出现错误,忘谅解。希望能够指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值