Unity 安卓插件教程(1)

声明  https://blog.csdn.net/z3465875/article/details/81054258原文章地址

系列教程请看原地址 Unity Android plugin tutorial (1/3) Fundamentals by Geri Borbás at blog.eppz.eu.

虽然Unity安卓插件开发已经与手机深度集成,但找到一个正确的Unity安卓插件开发流程并不是你想的那么容易。许多资源推荐你继承UnityPlayerActivity作为你自己插件的基类,然而,这样会导致与其他插件出现兼容问题。当你为一个Unity工程制作安卓插件时,对继承和配置的持续改动可以解决多个插件的问题。

但是如果你想要让自己的插件和别的插件无冲突的工作,或者你计划公开一个插件或者将插件封装到工程中。我强烈建议你不要使用“继承UnityPlayerActivity或者调整Android manifest配置文件”这些方法。而是像大多数插件一样,创建一个模块,将插件封装进去。

百度或者Google UnityPlayerActivity你会很容易的找到关于工程崩溃的问题抱怨。 Vuforia, Facebook, 和其他一些插件的不兼容问题。

虽然我认为插件开发需要深入了解底层原理,但我知道有些人认为直接看源码会更好,因此你可以直接从Github上下载,安装Android Studio开发工具,学习源代码,直接开发自己的插件。

描述|center
GitHub地址

Unity安卓运行时

Unity 安卓运行时运行在UnityPlayer,是一个继承自FrameLayout的特殊View类(通常用来持有一个单独的子View)。在创建的时候,这个视图初始化了一个特殊的视图类型SurfaceView,是一个可以直接用OpenGl来绘制的单独的子视图。这和IOS架构非常相似,Unity将底层的UIView调整为可以直接用OpenGL绘制的方式。除了绘制,该类还实现了关于本地特性的行为,像触控、键盘、位置、摄像机、网络、应用程序状态等。虽然UnityPlayer实现了许多的本地特性,但是它并不是应用的入口。


在这个捕获的实际层级中,在视图结尾层级中你可以看到UnityPlayer(一个FrameLayout的子类)包含一个SurfaceView来执行Unity OpenGL的绘制(不幸的是绘制内容在安卓监视器中不能预览)。

Android 应用和操作系统总的来说是由大量的Activity组成的,用户与一个单一的视图进行交互(更多的知识请看安卓应用基础)。Activity可以被另一个Activity或者用户启动。如果一个Activity中包含一个被标记为main Activity的应用,操作系统会在用户每次点击应用图标的时候启动它。(更多请看运行Activity

在一个常见的Unity安卓应用程序中,main Activity就是 UnityPlayerActiviey。如果你查看在APK文件中编译后的AndroidMainfest.xml文件,你可以看到UnityPlayerActiviey被标记为这个应用的MAIN和LAUNCHER Activity。Apk的部分编译文件可以在Unity工程中的Temp/StagingArea下找到(在你build安卓应用程序之后)。并且我也反编译了安卓环境下的UnityPlayer,你可以在下面看到所有的细节。

GitHub上查看反编译的UnityPlayer for Android(UnityPlayer.jar)

...
<activity android:label="@string/app_name"
          android:name="com.unity3d.player.UnityPlayerActivity"
          ...
        <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>
...


 
Unity应用的安卓运行时在UnityPlayerActiviey实例上执行,包括了可以与用户交互的一个单独OpenGL视图。在程序manifest配置文件中,这个Activiey被标记为main/launcher activity.

 

UnityPlayerActiviey是一个简单的拥有70行代码的类,代码初始化UnityPlayer实例并将其设置为这个整个应用程序Activity的内容视图,除了这些,它还发送本地事件到UnityPlayer

public class UnityPlayerActivity extends Activity
{
    protected UnityPlayer mUnityPlayer;
    protected void onCreate(Bundle bundle)
    {
        this.requestWindowFeature(1);
        super.onCreate(bundle);
        this.getWindow().setFormat(2);
        this.mUnityPlayer = new UnityPlayer(this);
        this.setContentView(this.mUnityPlayer);
        this.mUnityPlayer.requestFocus();
    }
    ....
    public boolean onTouchEvent(MotionEvent event)
    { return this.mUnityPlayer.injectEvent(event); }
    ...
}

通常人们使用的Unity安卓插件架构

从Unity应用的角度来看,本地特性通常被考虑用作外部服务,因此第一直觉就是创建一个单例作为这个特性的入口(像Google Analytics,Facebook,Chartboost服务大多都是单例)。当查找Unity 安卓插件开发时,一般的建议就是继承UnityPlayerActiviey并开发插件功能,然后告诉应用程序用新的继承类替换之前的 Launcher Activity。

 


 
上图显示一个常见的Unity安卓插件开发建议,继承UnityPlayerActivity,并且将继承的新类作为整个应用程序的Launcher Activity。

 

在这个Unity安卓插件系列教程中,我们使用eppz!Alert作为示例项目,这是一个简单的使用本地提示视图的插件,并且在用户选择后调用回调函数。在IOS中它使用UIAlertController类,在安卓中它使用DialogFragment/AlertDialog类,然后使用UnitySendMessage将结果返回给Unity。

虽然这个特性看起来像程序的一个小组件,它还是建议我们应该为我们的安卓应用创建一个全新的Launcher Activiey。 
如果你想正确的使用我的插件和其他的插件,就像我必须与Vuforia一起使用,类的结构视图应该如下。

 


这里写图片描述 
使用继承UnityPlayerActivity的方法,开发多个安卓插件制作了一个勉强的交叉依赖的层级。

 

如何实现Unity 安卓插件

就像我上面提到的,一个插件应该包含实现一些特性的单独的类,通常通过公共静态接口合并到主程序中。在我为一个准备发布到UnityAssetStore的工程制作插件时(主要是关于相片和邮件附件),我个人没有使用继承程序的main Activity。我想要避开兼容问题并且发布一个封装的,独立的的软件。

了解了Android Activity是用户与程序交互的基本视图,将整个Unity插件包含到一个Activity中听起来不错。从操作系统的角度来看,这是一个用户与应用交互的单独的OpenGL视图。

我的首次封装尝试是为我的插件创建一个单独的Activity,因此我可以把它组合到main Activity中。一个Activity可以轻易地启动其他Activity(像相册,分享等),并且内部有回调函数来接收来自其他Activity的回调或信号(看上方例子中的OnTouchEvent)。我还是一个安卓开发新手,但已经学习到了不在Activity中会导致事情变得困难。

因此我将插件包含或封装到一个Activity类中,然后启动(激活)它。但这会强制让当前活跃的UnityPlayerActivity进入不活跃的状态,虽然我的插件Activity实例正在运行,但是这样导致了黑屏。我尝试仅把插件包含到一个简单类中,但是获取安卓本地特性(获得回调)突然变得非常繁重。瓶颈发生在使用startActivityForResult()方法从Activity中获取回调上。

有些人会使用全局的BroadcastReceiver,或者缩小使用范围使用LocalBroadcastManager发送本地广播,但是还是感觉有些难于开发,并且还是没有很好解决startActivityForResult()方法的问题。这篇教程– Creating a native Android plugin for Unity3d – 作者是 Ashley Davis 和 Rory Dungan,使用这种方法制作了一个不错的Unity安卓插件。我从里面也受到了很多启发。

下一个尝试是把插件包含到Service中,在开始的时候感觉这样做很有希望。和Activitiy非常相似(可以简单的在AndroidManifest.xml配置文件中定义),但是并没有UI和后台运行的能力。运行其他Activity并回调还是有问题,因此我感觉使用它会有点冒险。然而方向应该是正确的,根据这个观点我开始试验不使用UI创建一个Activity

整合Unity安卓插件架构

然后我找到了Fragments,根据官方文档“一个Fragments代表一个行为或在一个Activity中的部分用户接口”。听起来就是我要找的。

基本来说应该是一个小的子Activity,甚至可以在没有UI的情况下启动。在给定一个Activity的情况下,可以完美的提供额外的服务,就像在文档中的建议:“你可以将fragment想像成一个activity中的模块功能,有自己的生命周期,接收自己的输入事件,并且可以在Activity运行的任意时刻添加和删除(就像一个“子activity”可以在不同的Activity中复用)”。

这听起来就是一个独立的插件。它也可以很好的植入UI/Activity层级中,因此任何本地或UI特性都可以获取。我们从插件中启动的许多Activity都可能有各自的回调方法,幸运的是Fragments实现了像Activity那样的onActivityResult()回调。当我在做邮件Activity例子时,这个要点就是关键。而且更好的是,它可以非常好的与Activity兼容运行,并且不需要在AndroidManifest.xml文件中进行任何配置!

这对于UnityPlayerActivity来说是一个完美的模块功能,从这之后我就一直基于Fragments来开发Unity安卓插件。

 


这里写图片描述 
用Fragments封装Unity安卓插件,这是一个更加模块化的架构。应用与插件完全独立,插件之间也不会相互干预。

 

将Fragments Unity安卓插件整合到主Activity中

可以用一个简单的工厂方法将一个Fragments实例整合到UnityPlayerActivity中,然后把Fragments添加到Unity ActivityUnityPlayer中有个静态变量currentActivity

package com.eppz.plugins;
public class EPPZ_Alert extends Fragment
{
    public static EPPZ_Alert instance; // Singleton instance
    public static void start()
    {
        // Instantiate Fragment.
        instance = new EPPZ_Alert_Fragment();
        // Add to the current 'Activity' (a static reference is stored in 'UnityPlayer').
        UnityPlayer.currentActivity.getFragmentManager().beginTransaction().add(instance, "EPPZ_Alert").commit();
    }
    ...
}

在Unity中可以像下面代码中这样调用它。在插件类中,调用静态的初始化方法start()。

public class EPPZ_Alert_Android : MonoBehaviour
{
  AndroidJavaClass _class;
  AndroidJavaObject instance { get { return _class.GetStatic<androidjavaobject>("instance"); } }
  void Start()
  {
    // Start plugin <span class="eppz inlineCode">Fragment</span>.
    _class = new AndroidJavaClass("com.eppz.plugins.EPPZ_Alert");
    _class.CallStatic("start");
  }
  ...
}
</androidjavaobject>

有了这个之后,插件的其余部分就可以在Fragment的继承类中进行开发了。在程序运行时,Unity会持有一个唯一的我们的插件实例(就像一个后台服务),因此我们可以从Fragment实例直接调用插件的方法。

Unity本体插件工作流程

如果你已经熟悉了Unity插件制作,你应该立即使用Fragment类来开发自己的插件。

这个教程剩余的部分主要讨论关于健康的Unity插件工作流程,IOS对应的覆盖,Android StudioXcode本地插件的工程设置,编译后处理,https://developer.apple.com/xcode/(与Unity应用通信并隐藏的多平台代码),简化工程文件结构等。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值