unity 和安卓互相交互

https://blog.csdn.net/u010407393/article/details/79423769

Unity和安卓交互

一:设置环境

  1.安装Android Build Support,Android Software Development Kit (SDK),Native Development Kit (NDK)。默认unity基于 OpenJDK.安装了Java Development Kit ,这些你可以通过UnityHub来完成安装,2018以前的版本不能通过unityhub安装

2.打开手机的usb调试

(1) 打开手机的开发者选项,打开usb调试,这样当手机通过usb连接到电脑上的时候就可以调试了,如果是在windows上连接,可能还会安装相应的驱动

(2)自定义sdk,ndk,jdk的安装,如果通过unity hub来安装,默认安装路径为---》unity安装路径/Editor/Data/PlaybackEngines/AndroidPlayer/Tools, 如果你有多个版本的unity,都需要相同的sdk,jdk,ndk,你又不想复制这些,你可以把这些在多个unity中1共享,打开unity的Preferences > External tools,取消勾选sdk,jdk,ndk,然后设置你想安装的路径

 下面是不同版本的unity对应的ndk的版本:

 

二:将unity整合到安卓应用程序当中

 Unity Runtime Library 运行库有一些空间来管理何时,以及如何在应用程序中加载,激活,卸载内容,就是在原生的安卓应用(用Android studio开发的app)里面嵌入unity,

 1.它是如何工作的

  任何一个unity生成的Gradle的安卓应用程序,都有以下结构:

  (1)unityLibrary模块中的一个库,可以集成到任何其他Gradle项目中,它包含了unity运行时的数据和玩家的数据

  (2)launcher模块中launcher部分,包含应用程序的名字和图标,

下面是它的一个例子:https://github.com/Unity-Technologies/uaal-example/blob/master/docs/android.md

其中如果出现两个图标,则删除unityLibrary里面的AndroidManufiest.xml

  2.限制

  unity作为一个应用程序的一个库,只能全屏渲染,不能只渲染一部分,也就是unity的内嵌程序,只能全屏显示

  不支持同时打开多个unity应用

  需要更新第三方插件,在unity运行的时候

三:unity Remote

 Unity Remote是一个可下载的应用程序,旨在帮助开发Android、iOS和tvOS,当你在Unity编辑器模式下运行你的项目时,这个应用程序会连接到Unity,unity编辑器下的内容被发送到手机设备的屏幕上,而手机实时输入则被发送回Unity中正在运行的项目中,这样就不必每次更改就发布一个应用程序了

1.设备和功能支持

Unity Remote 目前支持Android设备(通过USB连接在Windows和OS X上)和iOS设备(通过USB连接在OS X和安装有iTunes的Windows上,支持iPhone、iPad、iPod touch和Apple TV)。

运行中的Unity项目的游戏视图在手机设备屏幕上被复制,但是帧率降低了。以下来自设备的输入数据也流回到编辑器:

  • Touch and stylus input:手机屏幕触摸和手写笔输入
  • Accelerometer:加速器
  • Gyroscope:陀螺仪
  • Device camera streams:相机
  • Compass:指南针
  • GPS
  • Joystick names and input:操纵杆和输入

请注意,Unity Remote只是简单地显示设备上的可视输出并从中获取输入。游戏的实际处理仍然是由桌面机器上的编辑器完成的,所以它的性能并不是真正在手机上的运行的性能反映

2.下载使用unity remote

你可以在Unity项目中免费下载Unity Remote,也可以从设备的app store中下载一个预建的应用程序:

下载完之后就可以用usb连接手机到电脑上,调试了,前提手机打开usb调试

要使unity能够和安卓设备一起工作,打开 Edit > Project Settings->editor,并从Unity Remote部分选择要使用的设备

3.可能遇到的问题

1.用USB连接了多个设备,但只有一个设备显示画面

 unity remote不支持多个安卓设备同时连接,只会显示第一个连接的安卓设备,但是它支持一个安卓设备和多个ios/tvos设备,你可以通过editor里面的device来选择连接哪个设备。

2.在手机上显示的画面画质比较低

 在unity remote上显示的画面,其实是unity编辑器上的画面通过流发送到手机屏幕上的,所以受带宽的限制,视频流会受到压缩,所以画质有所降低,在unity editor里面你可以选择compress为JPEG或者PNG,PNG表示无损,画面的质量不会下降,但是需要更大的带宽

3.注意:unity remote仅仅是为了更快的在手机上观看画面的效果,如果需要完整的测试,还是要发不出来,在真机上调试

四:Android Player settings

下面是安卓playersetting里面的设置

 1.ICON

Property Function
Adaptive 为应用程序中的Android自适应图标设置纹理。
Round 在应用程序中为Android圆形图标设置纹理。
Legacy Set up textures for the Android Legacy icons in your app.
Enable Android Banner 构建Android T打开自定义 banner

2.Resolution and Presentation

Setting Function
Start in fullscreen mode 在启动屏幕或第一个场景加载时隐藏导航栏。未设置时,导航栏将在启动屏幕或第一个场景加载时出现。
Render outside safe area 启用此选项以允许使用所有可用的屏幕空间进行呈现,包括显示缺口(notch)区域。有关更多信息,请参见Android开发人员网站上的显示裁剪支持文档。
Optimized Frame Pacing 启用这个选项可以让Unity在帧率中均匀地分配帧,从而减少变化,创建一个更流畅的游戏。

Setting Function
Resolution Scaling Mode 允许您将缩放设置为等于或低于本机屏幕分辨率。
  FixedDPI 允许您调整安卓设备的屏幕分辨率,使其低于本机默认分辨率,并显示目标DPI属性。使用这优化性能和电池寿命或目标的具体DPI设置。
  Disabled 确保不应用缩放,游戏按照本安卓设备的分辨率渲染
Target DPI 设置目标安卓设备的分辨率, unity会降低设备的分辨率,如果手机设备比这个值要高的话. 缩放比例通过min(Target DPI * Factor / Screen DPI, 1)计算Factor 通过Quality设置里面的Resolution Scaling Fixed DPI Factor控制
Note: This option only appears when the Resolution Scaling Mode is set to Fixed DPI.
Blit Type  控制是否用一个blit把图片最终渲染到屏幕上
  Always (Always blit) 使Unity渲染到一个屏幕外的缓冲区,然后复制到系统帧缓冲区。这与大多数设备兼容,但通常比从不使用慢。
  Never (Never blit) 将Unity渲染到操作系统提供的framebuffer中.如果应用运行时因为某些原因造成此操作失败,应用程序会打印一次警告, 它比always快,但并不是使用所有设备
  Auto

显示用never,如果失败了使用always

设置屏幕的长宽比,可以设置本机,也可以自定义,自定义时最大为2.1

五:Requesting Permissions

  一:授权请求

   一般在手机刚打开的时候需要显示应用需要访问的权限,Googleplay中的应用程序默认是,第一个请求时,只显示请求的权限内容,不显示为什么使用该权限,当用户第一次拒绝使用该权限后,会显示为什么使用该权限,并再次请求,如果用户再次拒绝了请求,你应当在应用程序中关闭掉相应的功能,如果应用程序不能使用,应该通知用户,如果用户之前在第一次拒绝时勾选了不再提醒我,则需要手动开启相应的权限。

Permissions

Unity自动添加了一些权限到 manifest,且 Unity有在脚本中调用的API

自动添加的权限,在play setting中的other setting中可以设置,也有在脚本中访问的

Permission结构体,里面包含了权限请求的内容和方法,需要引用unityEngine.Android 命名空间

 六:Support for APK expansion files (OBB)

  支持APK扩展文件:

  它是用来解决在GooglePlay商店里面,应用程序被限制大小为100M的方案,如果你的应用大于100MB(这对于大型游戏来说是很有可能的),你必须将你的输出包分成主要部分(APK)和扩展文件(OBB),Google Play 要求用户下载的压缩后 APK 大小不超过 100MB。对于大多数应用而言,这个空间足够存放应用的所有代码和资产。不过,有些应用需要更多空间来存放高保真图形、媒体文件及其他大型资产。以前,如果应用的压缩后下载大小超过 100MB,您必须自行托管这些额外的资源并在用户打开应用时下载这些资源。托管和传送额外文件的成本可能并不低,而且用户体验通常也不太理想。为简化这一过程并改善用户体验,Google Play 现在允许您附加两个大型扩展文件来补充您的 APK。补丁扩展文件可以用来更新,为了节省带宽,建议扩展文件为压缩文件,但是Google play会自会把它转换成 Opaque Binary Blob(OBB)文件。

Unity自动将输出包分成APK和OBB。这并不是分割app包的唯一方法(其他选项包括第三方插件和资产包),但这是Unity官方支持的唯一自动分割机制。

 一:Building the app with expansion files

如果你想 Unity 把资源包分成 APK 和OBB 扩展文件, 打开 Edit > Project Settings,,在Publishing Settings面板打开 Split Application Binary 属性

APK and OBB在你发布应用时会输出到同一文件夹下. 比如,如果  APK的名字为 mygame.apk,  OBB 会在同样文件夹下的mygame.main.obb.

If you select Build and Run, the APK and OBB files are installed on your device by Unity. If you select Build
and want to install the app manually using the ADB
utility, you must first install the APK and then copy the OBB into the correct location on your device. The OBB file name must correspond the format required by Google. Refer to the expansion files section of the Android Developer documentation for more information.

如果应用程序不能加载 OBB文件,则只有第一个场景可以打开,apk和obb文件是不能分离的,他们是同一个应用程序的不同部分

二:How data is split between the APK and OBB

当勾选 Split Application Binary 是,应用程序按照以下规则分离:

  • APK- 由可执行文件组成 (Java and native), plug-ins, scripts, and 第一个场景的数据 Scene (with the index 0).

  • OBB - 包含剩下的场景,资源和 streaming Assets.

如果您的APK仍然太大,无法在谷歌Play Store中发布(超过100MB),请尝试减小第一个场景的大小,使其尽可能小。

Downloading the OBB expansion file

The Unity Asset Store 提供一个 plug-in that允许您访问已修改版本的谷歌Play market_downloader库,您可以使用该库从谷歌Play Store或外部源下载OBB,并将其移动到正确的目录中。

Hosting OBB files on the Google Play Store:托管obb文件

OBB扩展文件应该与APK一起发布到Google Play Store。当用户从Google Play Store安装应用程序时,任何与APK一起发布的OBB文件都会自动下载。

在Google Play Store存储错误的情况下,或者用户从设备中删除了OBB文件的情况下,应该在应用程序中包含下载丢失的OBB文件的代码. For more information about downloading OBB files, refer to the APK Expansion file section of the Android Developer documentation.

七:快速开发应用程序补丁

根据项目的大小,为Android开发一个应用程序可能会花费大量的时间。为了在开发过程中执行更快的迭代,您可以选择修补应用程序包,而不是重新构建它。当您对构建进行修补时,仅向设备发送与脚本相关的文件。

Patching your app:修补你的应用

在你给一个应用打补丁之前,你必须在手机设备上建立并安装它. For more information, see Building apps for Android.

下面是打补丁的步骤:

  1. 更新脚本文件.

    Note:如果你改变了附加到游戏对象的脚本的布局,比如添加新的公共变量,脚本只构建和打补丁的过程将会失败.

  2.  Editor下点击 File > Build Settings.

  3. Build Type 下拉菜单下选择 Development.

  4. 如果你正在使用 IL2CPP scripting backend(其他情况移动到下一步):

    1. 点击 Player Settings 按钮.
    2. 打开 Other Settings 部分.
    3. 取消勾选 Strip Engine Code
  5. 勾选 Scripts Only Build 复选框

  6. 点击Patch or Patch & Run.

构建更新的脚本文件后,Unity将更新后的文件发送到Run device字段中指定的设备。

如果您已经实现了自己的构建管道, 你可以给BuildPipeline.BuildPlayer 方法传递BuildOptions.BuildScriptsOnlyBuildOptions.PatchPackage 参数,在脚本中发布程序

For example:

    BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
    buildPlayerOptions.__scenes__ = new[] { "Assets/Scene1.unity"};
    buildPlayerOptions.target = BuildTarget.Android;

    // Build & Run the game normally, this will install the application on the android device
    buildPlayerOptions.options = BuildOptions.AutoRunPlayer
    BuildPipeline.BuildPlayer(buildPlayerOptions);

    // Modify some __scripts__ in your Unity Project
    // Patch & Run the application
    // (Unity will only recompile script files and push only the necessary files to the android device)
    buildPlayerOptions.options = BuildOptions.BuildScriptsOnly | BuildOptions.PatchPackage | BuildOptions.AutoRunPlayer;
    BuildPipeline.BuildPlayer(buildPlayerOptions);

How does app patching work?

Unity发送包含更新的脚本的文件到应用程序的缓存文件夹.当应用程序启动时,应用程序在加载文件之前检查缓存文件夹. If the 应用程序找到一个需要的文件,从缓存文件夹而不是应用程序的数据文件夹加载文件.

Unity发送的文件取决于你使用的脚本后端:

Mono

从项目工程中的脚本文件管理程序集编译,包的脚本文件或者 asmdef files 被发送到 /storage/emulated/0/Android/data/<PackageName>/cache/ScriptOnly/<UnityVersion>/mono/Managed

IL2CPP

  • libil2cpp.so 被发送到 /data/data/<PackageName>/cache/ScriptOnly/<UnityVersion>/il2cpp

    Note: 这是一个内部缓存. 由于Android 7.0引入了安全增强,不可能从模拟存储加载动态库. For additional information, see Android 7.0 Behavior Changes on the Android developer website.

  • IL2CPP 资源文件 /storage/emulated/0/Android/data/<PackageName>/cache/ScriptOnly/<UnityVersion>/il2cpp

Clearing patch files

您可以使用Android的存储设置来清除应用程序的缓存,从而删除脚本仅构建过程中安装的补丁文件. 要清除设备上的缓存,请转到设置并打开应用程序列表. 通常有一个清除存储数据和/或缓存的选项。在Android操作系统的某些实现中,您可能需要深入到存储选项中,以找到清除缓存的选项。

八:Building and using plug-ins for Android:

         unity和Android通过java或者c++交互,unity通过plug-in里面的dll或其它的插件,在c#中和其它语言的代码做交互

Unity supports several types of Android plug-ins:

 一:    AAR plug-ins and Android Libraries

AAR plug-ins

Android Archive (AAR) plug-ins是一个包含java编译代码,C/C++代码,资源,和一个Android Manifest的包.  .aar 文件本身是一个zip结构的压缩包,它里面包含了这些代码,For more details, see Android Developer documentation on creating an Android Library.

To add an AAR plug-in to your project, copy the .aar file into any of your project folders, then select it in Unity to open the Import Settings in the Inspector
window. Tick the Android checkbox to mark this .aar file as compatible with Unity:

AAR是Unity Android应用推荐的插件格式。

Android Library Projects

Android Library projects 和AAR 文件很想,他们都包含c/c++代码,Java代码 ,资源文件和一个Android Manifest. 但是Android Library 是不是一个单独的存档文件,而是一个包含所有资产的特殊结构的目录. , see Android Developer documentation on creating an Android Library.

把预编译好的Android library projects 导入 Assets/Plugins/Android 文件夹. 预编译是指所有的 .java 文件必须被编译进.jar 文件。并放到Android Studio project工程的bin/ 或者 libs/ 文件夹下,在这些文件夹中,AndroidManifest.xml在项目构建时自动与主清单文件合并。

Unity把 Assets/Plugins/Android下所有的子文件夹作为一个潜在的 Android Library, 禁止从这些文件夹下导入资源. 如果这些子文件夹下包含 AndroidManifest.xml文件,则该子文件夹被当作一个 Android Library,则project.propertiy工程属性的文件包含以下字符串android.library=true.

.

Providing additional Android Assets and resources

提供额外的Android资产和资源

如果想把unity中导入的资源原封不动的复制到apk中,需要把资源文件导入到 Assets/Plugins/Android/assets文件夹下,这样他们会出现在APK的 assets文件夹下,可以在Android studio中通过 getAssets()方法访问。

二:JAR plug-ins

JAR(Java Archive) plug-ins主要用于支持与Android操作系统的交互 或者从c#脚本中调用用Java编写的方法

jar文件只能包含Java代码(不能包含 Android resources), 这使得它们的使用非常有限. 复制.jar 文件到你工程目录下的任何文件夹下, 选中他们打开属性面板. 选中Android复选框,将此.jar文件标记为与Android兼容:

Using Java plug-ins

Unity使用Java本地接口(JNI),无论是从Java中调用代码,还是从c/c++或c#脚本中与Java或Java VM(虚拟机)进行交互。

Java source files as plug-ins

当使用 Gradle 发布系统,可以避免生成JAR 文件,下面是步骤:

  1. 把 .java 文件拖到 Unity 工程里面.
  2. 在属性面板Plugin Inspector, 勾选上 Android platform.
  3. 发布的时候设置 Gradle 模式

Unity将java文件复制到Gradle项目并使用它进行构建.

Using your Java plug-in from native (C/C++) code

从C or C++ 中访问Java代码需要访问Java VM. 下面是方法:

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  JNIEnv* jni_env = 0;
  vm->AttachCurrentThread(&jni_env, 0);
  return JNI_VERSION_1_6;
}

It is beyond the scope of this document to explain JNI completely, but this method usually involves finding the class definition, resolving the constructor (<init>) method and creating a new object instance, as shown in this example:

jobject createJavaObject(JNIEnv* jni_env) {
  jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class");         // find class definition
  jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V");      // find constructor method
  jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass);     // create object instance
  return jni_env->NewGlobalRef(obj_JavaClass);                      // return object with a global reference
}

For more information about JNI, see Android Developer documentation on JNI

Using your Java plug-in from C# scripts with helper classes

在c#中调用java文件里面的方法

 AndroidJNIHelperAndroidJNIUnity API类被用作“原始”JNI接口的包装器。

AndroidJavaObject 和 AndroidJavaClass Unity API类在使用JNI调用时自动执行许多任务,它们还使用缓存来更快地调用Java. AndroidJavaObject和AndroidJavaClass的组合构建在AndroidJNI和AndroidJNIHelper之上,但是也有一些附加的功能. 这些类还具有用于访问Java类的静态成员的静态方法。

有三种方法可以从你的c#脚本中调用Java JNI:

  • 原生JNI通过AndroidJNI方法 ;
  • AndroidJNIHelper class together with AndroidJNI;
  • AndroidJavaObject and AndroidJavaClass 最方便.

UnityEngine.AndroidJNI 在c语言中是一个JNI 方法的包装器. 该类中的所有方法都是静态的,并与Java本机接口进行1:1的映射。

UnityEngine.AndroidJNIHelper 提供下一层使用的辅助功能, 它作为公共方法公开,在特殊情况下可能有用。

 UnityEngine.AndroidJavaObject and UnityEngine.AndroidJavaClass 实例具有对 java.lang.Object and java.lang.Class (或者它的自类)一对一的映射,它们本质上提供了与Java端的3种类型的交互::

  • Call a method:调用方法

  • Get the value of a field:得到值

  • Set the value of a field:设置值

该调用分为两类: 对“void”方法的调用,以及对非void返回类型的方法的调用。泛型类型用于表示返回非空类型的方法的返回类型。Get和Set总是使用表示字段类型的泛型类型。

Examples

Example 1

 AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); 
 // jni.FindClass("java.lang.String"); 
 // jni.GetMethodID(classID, "<init>", "(Ljava/lang/String;)V"); 
 // jni.NewStringUTF("some_string"); 
 // jni.NewObject(classID, methodID, javaString); 
 int hash = jo.Call<int>("hashCode"); 
 // jni.GetMethodID(classID, "hashCode", "()I"); 
 // jni.CallIntMethod(objectID, methodID);

这个例子创建了一个java.lang.String的实例,并用一个字符串吃实话, 并返回那个字符串的哈希值.

 AndroidJavaObject 构造函数至少接受一个参数 - 来构造实例的类的名字.类名之后的任何参数都是对象的构造函数调用的参数,在本例中是字符串“some_string“. 接下来对hashCode()的调用将返回一个“int”,在本例中,它用作调用方法的泛型类型参数。

Note:不能使用点符号实例化嵌套的Java类。内部类必须使用$分隔符. Use android.view.ViewGroup$LayoutParams or android/view/ViewGroup$LayoutParams, where LayoutParams class is nested in ViewGroup class.、

Example 2

这个例子展示了如何在不使用插件的情况下在c#中获取当前应用程序的缓存目录:

AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
 // jni.FindClass("com.unity3d.player.UnityPlayer"); 
 AndroidJavaObject jo = jc.GetStatic <AndroidJavaObject>("currentActivity"); 
 // jni.GetStaticFieldID(classID, "Ljava/lang/Object;");  
 // jni.GetStaticObjectField(classID, fieldID); 
 // jni.FindClass("java.lang.Object"); 

 Debug.Log(jo.Call <AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath")); 
 // jni.GetMethodID(classID, "getCacheDir", "()Ljava/io/File;"); // or any baseclass thereof! 
 // jni.CallObjectMethod(objectID, methodID); 
 // jni.FindClass("java.io.File"); 
 // jni.GetMethodID(classID, "getCanonicalPath", "()Ljava/lang/String;"); 
 // jni.CallObjectMethod(objectID, methodID); 
 // jni.GetStringUTFChars(javaString);

上面的例子从 AndroidJavaClass开始,而不是 AndroidJavaObject是为了访问com.unity3d.player.UnityPlayer 类里面的静态成员,而不是创建一个新的对象. 然后访问静态字段“currentActivity”,但这次使用AndroidJavaObject作为泛型参数. .这是因为实际的字段类型 android.app.Activityjava.lang.Object的子类, 任何非基元类型都必须被访问为AndroidJavaObject. 这个规则的例外是字符串,它们可以直接访问,即使它们在Java中不代表原始类型。.

getCacheDir() 可以在 Activity 物体上获得当前代表缓存文件夹的对象

getCanonicalPath()可以获得一个字符串的表示

Unity提供了可以访问应用程序缓存和文件的文件夹的API,Application.temporaryCachePath  Application.persistentDataPath APIs.

Example 3

这个例子展示了如何使用UnitySendMessage将数据从Java传递到Unity。

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour { 

    void Start () { 
        AndroidJNIHelper.debug = true; 
        using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { 
        jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "NewMessage");
        } 
    } 

    void JavaMessage(string message) { 
        Debug.Log("message from java: " + message); 
    }
} 

com.unity3d.player.UnityPlayer java类含有一个静态方法 UnitySendMessage, 在iOS下也一样,也是这个方法,用来从Java向unity传递参数

虽然 UnitySendMessage 是在unity中调用的,但是它时使用java来传递信息的,然后java回调c/c++或者unity代码把信息传递给名字为 “Main Camera的物体,这个物体上挂有一个脚本,脚本中有一个方法叫 JavaMessage.,后面是这个方法的参数

Best practice when using Java plug-ins with Unity

AndroidJavaObject and AndroidJavaClass 花销比较大,因为他们的方法使用的都是原生的JNI,. 尽量减少托管代码和本机/Java代码之间的转换,

//The first time you call a Java method like 
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // somewhat expensive
int hash = jo.Call<int>("hashCode"); // first time - expensive
int hash = jo.Call<int>("hashCode"); // second time - not as expensive as we already know the java method and can call it directly

 Mono garbage collector垃圾收集器 应该释放所有已经创建的AndroidJavaObject and AndroidJavaClass 的对象, 可以使用using(){} 代码块,让他们尽可能快的释放. 没有这个,你不确定是否被删除了,如果设置.  AndroidJNIHelper.debug为 true, 你会看到一个垃圾收集器的一个回收记录

//Getting the system language safely
void Start () { 
    using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) { 
        using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) { 
            Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage")); 

        } 
    } 
}

十:扩展UnityPlayerActivity Java Code

一:Extending the UnityPlayerActivity file

当你开发一个 Unity Android application, 你可以扩展 UnityPlayerActivity 类(Android上的UnityPlayer的主要Java类 , 和 Unity iOS上的 AppController.mm相似).一个应用程序可以重写任何Android OS 安卓系统和apk之间的基本的交互:

重写默认的 activity也就是重写默认的交互:

  • 创建 一个new Activity 继承自 UnityPlayerActivity (see Android documentation on Activity);
  • 修改 Android Manifest 让new Activity 作为应用程序的入口点

最简单的方法是从Unity导出你的项目,然后在Android Studio中对UnityPlayerActivity类进行必要的修改。

创建一个新的new Activity 并添加到你的安卓项目中, 您必须执行以下步骤:

  1. 默认 the UnityPlayerActivity.java 文件在

    Mac:

    /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/src/com/unity3d/player

    Windows:

    C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player
     

    要扩展 UnityPlayerActivity 文件, 找到包含在Unity中的classes.jar文件. 在下列文件夹下你可以找到它(  Windows:C:\Program Files\Unity\Editor\Data, Mac:/Applications/Unity on), 在这些子文件夹中的一个:

    • PlaybackEngines/AndroidPlayer/Variations/mono

    • il2cpp/Development

    • Release/Classes/

    找到该文件,并将classes.jar添加到类路径中 Unity 用来编译 new Activity.编译你的 Activity 源文件,并把它打包进 JAR 或者 AAR package, 然后将其复制到项目文件夹中r.

  2. 创建一个新的 Android Manifest 设置 new Activity 作为应用程序的入口点, 然后把它放到 Assets/Plugins/Android 文件夹下

从自定义UnityPlayerActivity文件中指定Unity启动参数

 当你扩展UnityPlayerActivity, 你可以重写 String UnityPlayerActivity.updateUnityCommandLineArguments(String cmdLine) 将启动参数传递给Unity。

UnityPlayerActivity 在启动期间调用此方法.它接受当前的命令行参数,可以是null或空,并返回一个新的命令行参数字符串传递给Unity。

有关Unity命令行界面的概述, see Command line arguments.

下面的例子演示了如何使用它来选择基于当前设备的图形API:

package com.company.product;
import com.unity3d.player.UnityPlayerActivity;
import android.os.Bundle;
import android.os.Build;

public class OverrideExample extends UnityPlayerActivity {
    private boolean preferVulkan() {
        // Use Vulkan on Google __Pixel__ devices
        if (Build.MANUFACTURER.equals("Google") && Build.MODEL.startsWith("Pixel"))
            return true;
        else
            return false;
    }

    private boolean preferES2() {
        // Use OpenGL ES 2.0 on devices that run Android 5.1 or older
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1)
            return true;
        else
            return false;
    }

    private String appendCommandLineArgument(String cmdLine, String arg) {
        if (arg == null || arg.isEmpty())
            return cmdLine;
        else if (cmdLine == null || cmdLine.isEmpty())
            return arg;
        else
            return cmdLine + " " + arg; 
    } 

    @Override protected String updateUnityCommandLineArguments(String cmdLine)
    {
        if (preferVulkan())
            return appendCommandLineArgument(cmdLine, "-force-vulkan");
        else if (preferES2())
            return appendCommandLineArgument(cmdLine, "-force-gles20");
        else
            return cmdLine; // let Unity pick the Graphics API based on PlayerSettings
    }

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

UnityPlayerActivity example file

The following is an example UnityPlayerActivity file:

OverrideExample.java:
package com.company.product;
import com.unity3d.player.UnityPlayerActivity;
import android.os.Bundle;
import android.util.Log;

public class OverrideExample extends UnityPlayerActivity {
  protected void onCreate(Bundle savedInstanceState) {
    // call UnityPlayerActivity.onCreate()
    super.onCreate(savedInstanceState);
    // print debug message to logcat
    Log.d("OverrideActivity", "onCreate called!");
  }
  public void onBackPressed()
  {
    // instead of calling UnityPlayerActivity.onBackPressed() we just ignore the back button event
    // super.onBackPressed();
  }
}

The corresponding AndroidManifest.xml might look like the following:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:name="com.YourPackage.name.OverrideExample"
             android:label="@string/app_name"
             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>

十一:Android mobile scripting

Note: 对于跨平台项目,使用UNITY_ANDROID #define指令有条件地编译特定于android的c#代码。 See documentation on

访问设备特定的特性和属性

Apps 可以 通过 Input and Handheld 类. 访问Android设备的一些特性:For more information, see:

Vibration support:振动的支持

你可以通过呼叫来触发振动Handheld.Vibrate. 有振动硬件的设备忽略这个调用.

Activity indicator

移动操作系统有内置的活动指示器,您的应用程序可以在运行缓慢时使用。 For more information, see Handheld.StartActivityIndicator.

To access device-specific properties, use these scripts:

Script Device property
SystemInfo.deviceUniqueIdentifier Always returns the md5 of ANDROID_ID. For more information, see Android developer documentation on ANDROID_ID.
SystemInfo.deviceName 返回设备的名字,在安卓上,Unity尝试读取设备名和蓝牙名,如果没有设置,则返回  <unknown>.
SystemInfo.deviceModel 返回设备模型.这通常包括制造商名称和型号(for example, “LGE Nexus 5 or ”SAMSUNG-SM-G900A").
SystemInfo.operatingSystem 返回操作系统名称和版本。

Anti-piracy check:反盗版检查

获取用户授权信息,防止盗版 , Google 提供一项名为Google Play Application Licensing. See the Google Play Application License Verification 资产存储上的示例插件,用于演示如何将此功能集成到Unity应用程序中。您还可以从其GitHub存储库下载插件源代码。

Screen orientation:屏幕方向

你可以在iOS和Android设备上控制应用程序的屏幕方向。检测方向的变化或强制一个特定的方向对于创建依赖于用户如何持有设备的游戏行为是有用的。

通过 Screen.orientation 属性检索设备方向,定向可以是下列之一:

Orientation Behavior
Portrait 该设备是在纵向模式,与设备直立和home按钮在底部。
PortraitUpsideDown 该设备处于纵向模式,但上下颠倒,设备保持直立,home键在顶部。
LandscapeLeft 设备处于横屏模式,设备直立,home键在右侧。.
LandscapeRight 设备处于横屏模式,设备直立,home键在左侧。

 设置屏幕的朝向可以使用Screen.orientation t也可以使用 ScreenOrientation.AutoRotation . 当您启用自动旋转时,您仍然可以根据具体情况禁用某些方向。

Use these scripts to control autorotation:

发布了80 篇原创文章 · 获赞 7 · 访问量 2679

猜你喜欢

转载自blog.csdn.net/qq_37672438/article/details/104370325