您的位置:首页 > 移动开发 > Unity3D

Unity Android Plugin 开发教程

2017-07-28 18:04 507 查看


开发环境Windows平台(Unity2017.1.0f3 Personal ,Android Studio 2.3.3)

在Unity项目中构建和使用Android Plugin

Unity支持几种类型的Android plug-ins:



AAR 插件 和 Android Library

JAR 插件

继承UnityPlayerActivity

Native(C++) 插件

Unity Android runtime

Untiy Android runtime通过继承自FrameLayoutUnityPlayer实现,UnityPlayer实现了触控,键盘输入,相机,位置等特性。虽然这个UnityPlayer实现了大部分的native功能,但它不是应用程序的入口。

在通用的Android Unity应用程序中,程序的入口是UnityPlayerActivity。如果你看一下APK文件反编译后的AndroidManifest.xml文件,可以看到它是如何标记UnityPlayerActivity作为应用程序的Launcher的。



查看Unity安装目录发现,UnityPlayerActivity的源码可以在C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Source中查看。



public class UnityPlayerActivity extends Activity
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

// Setup activity layout
@Override protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);

getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy

mUnityPlayer = new UnityPlayer(this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}

.........

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

.........
}


可以看到UnityPlayerActivity继承自Activity,并且UnityPlayerActivity持有一个UnityPlayer实例。UnityPlayerActivity通过UnityPlayer分派native 事件。



通用的Android插件开发,通过继承UnityPlayerActivity,并使子类成为整个应用程序的LAUNCHER Activity,接下来将介绍继承UnityPlayerActivity方式的Android Plugin

继承UnityPlayerActivity方式的Android Plugin



Android Plugin需要包含Android项目中build后得到的app-debug.aarManifest.xml以及资源文件等,提供给Unity项目使用。文件存放在Unity项目中的/Assets/Plugins/Android。Unity项目中的代码通过app-debug.aar与封装在其中的Android代码进行交互。For more details about .aar, see Android Developer Doc. And for more information about “How Unity produces the Android Manifest”, see Unity Developer Doc





那么,接下来新建Android项目,进而生成app-debug.aar文件和Manifest.xml文件。

Android端的操作

新建Android 项目

1.将项目切换成project的视图,打开app目录下的build.gradle文件,

1.将apply plugin: ‘com.android.application’,改成apply plugin: ‘com.android.library’

2.然后删除applicationId



2.修改Manifest.xml文件

在activity中添加
<meta-data android:name="unityplaer.UnityActivity" android:value="true"/>




引入Unity的classes.jar

从Unity 的安装目录找到unity的classes.jar包。

Windows目录:C:\ProgramFiles\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\classes.jar

Mac下目录:/Applications/Unity/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes\classes.jar

​ 将其拷贝到UnityAndroid项目app目录下的libs目录下,右键Add as library,导入之后可以发现在build.gradle中就有他的引入了。





编写Android项目与Unity项目交互的代码

首先需要让MainActivity继承UnityPlayerActivity,同时删除onCreate方法中的setContentView()

public class MainActivity extends UnityPlayerActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
//示例1:
//Unity端调用该函数
public void ShowToast(final String msg){
// 需要在UI线程下执行
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),msg, Toast.LENGTH_SHORT).show();
new AlertDialog.Builder(MainActivity.this).setMessage(msg).show();
}
});
}
//示例2:
//Unity端调用该函数
public void setUnityText(){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),"Android 端调用setText", Toast.LENGTH_SHORT).show();
//调用Unity端函数
UnityPlayer.UnitySendMessage("Canvas","setText","Android 端调用setText");
}
});
}

}


Build得到app-debug.aar文件和Manifest.xml文件

分别在/app/build/outputs/aar和/app/src/main目录下



接下来需要将app-debug.aar用解压软件打开,并且删除libs目录下的classes.jar



Unity端的操作

创建Unity项目

新建Unity项目,并新建如下目录将Android端得到的app-debug.aar文件和Manifest.xml文件放在/Plugins/Android目录下,同时在Hierarchy下按照图示新建Canvas,Button和Text:



编写C#脚本

同时新建名为AndroidTest.cs的C#脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AndroidTest : MonoBehaviour {

// Use this for initialization
void Start () {

}

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

}
public void BtnShowMessage()
{
using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using(AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"))
{
// 调用Android端方法
jo.Call("ShowToast", "Unity调用了Android中的AlertDialog");
}
}
}

public void BtnSetText()
{
using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"))
{
// 调用Android端方法
jo.Call("setUnityText");
}
}
}

//Android端调用该方法
public void setText(string result){
Text text = GameObject.Find ("UnityText").GetComponent<Text> ();
text.text = result;
}

}


编译运行

按照标号步骤进行1. 选择Build的平台->->2. 添加Scenes ->-> 3. 设置Identification ->-> 4. 设置包名和 API Level ->-> 5. 编译运行



效果展示



使用AARJAR方式的Android Plugin

Android端的操作

在Android Studio中新建项目

1.选择Add No Activity



新建Modue,选择Android Library



添加AndroidPlugin.java

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import java.text.SimpleDateFormat;
import java.util.Date;

public class AndroidPlugin
{
// Needed to get the battery level.
private Context context;

public AndroidPlugin(Context context)
{
this.context = context;
}

// Return the battery level as a float between 0 and 1 (1 being fully charged, 0 fulled discharged)
public float GetBatteryPct()
{
Intent batteryStatus = GetBatteryStatusIntent();

int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);

float batteryPct = level / (float)scale;
return batteryPct;
}

// Return whether or not we're currently on charge
public boolean IsBatteryCharging()
{
Intent batteryStatus = GetBatteryStatusIntent();

int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
return status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
}

private Intent GetBatteryStatusIntent()
{
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
return context.registerReceiver(null, ifilter);
}

//Get System Time
public String getSysTime(){
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()).toString();
}
}


Build Module之后,在模块的子目录/build/outputs/arr中得到.aar.jar



Unity端的操作

创建Unity项目

将得到的.arr(将.arr解压就可以得到.jar) 放在Assert目录下,可以放在任意的目录下,官方推荐使用.aar 。并且按照如下目录新建Canvas和Text。



编写C#脚本

编写BatteryLevelPlugin.cs :

Unity提供了两种方式与java进行交互

AndroidJNI 和 AndroidJNIHelper

AndroidJavaClass , AndroidJavaObject 和 AndroidJavaProxy



同时,官方推荐如下调用方式:



using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BatteryLevelPlugin{

public static float GetBatteryLevel()
{
if (Application.platform == RuntimePlatform.Android)
{
using (var javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
using (var androidPlugin = new AndroidJavaObject("com.hiscene.androidsysinfo.AndroidPlugin", currentActivity))
{
return androidPlugin.Call<float>("GetBatteryPct");
}
}
}
}

return 1f;
}

public static string GetSysTime() {

AndroidJNI.AttachCurrentThread();

IntPtr javaClass = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");

IntPtr fid = AndroidJNI.GetStaticFieldID(javaClass, "currentActivity", "Landroid/app/Activity;");

IntPtr obj = AndroidJNI.GetStaticObjectField(javaClass, fid);

IntPtr pluginClass = AndroidJNI.FindClass("com/hiscene/androidsysinfo/AndroidPlugin");

IntPtr initMethod = AndroidJNI.GetMethodID(pluginClass, "<init>", "(Landroid/content/Context;)V");

jvalue[] jv = new jvalue[1];

//TODO

IntPtr pobj = AndroidJNI.NewObject(pluginClass, initMethod,jv);

IntPtr enableMethod = AndroidJNI.GetMethodID(pluginClass, "getSysTime", "()Ljava/lang/String;");

return AndroidJNI.CallStringMethod(pobj, enableMethod, new jvalue[1]);

}

//方式一:
public static string GetSysTimeAndroidJNI()
{

AndroidJNI.AttachCurrentThread();

IntPtr javaClass = AndroidJNI.FindClass("com/hiscene/androidsysinfo/SysTime");

IntPtr initMethod = AndroidJNI.GetMethodID(javaClass, "<init>", "()V");

IntPtr pobj = AndroidJNI.NewObject(javaClass, initMethod, AndroidJNIHelper.CreateJNIArgArray(new object[1]));

IntPtr enableMethod = AndroidJNI.GetMethodID(javaClass, "getSysTime", "()Ljava/lang/String;");

return AndroidJNI.CallStringMethod(pobj, enableMethod, new jvalue[1]);

}

//方式二:
public static string GetSysTimeAndroidJavaClass()
{
if (Application.platform == RuntimePlatform.Android)
{
using (var javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (var currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
using (var androidPlugin = new AndroidJavaObject("com.hiscene.androidsysinfo.AndroidPlugin", currentActivity))
{
return androidPlugin.Call<string>("getSysTime");
}
}
}
}

return "Time yyyy-MM-dd HH:mm:ss";
}
}


编写BatteryMonitor.cs :

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class BatteryMonitor : MonoBehaviour {

public Text batteryLevelText;
public Text batteryLevelIcon;
public Text sysTime;

static readonly string BATTERY_LEVEL_100 = Char.ConvertFromUtf32(0xf240);
static readonly string BATTERY_LEVEL_75 = Char.ConvertFromUtf32(0xf241);
static readonly string BATTERY_LEVEL_50 = Char.ConvertFromUtf32(0xf242);
static readonly string BATTERY_LEVEL_25 = Char.ConvertFromUtf32(0xf243);
static readonly string BATTERY_LEVEL_0 = Char.ConvertFromUtf32(0xf244);

// Update is called once per frame
void Update () {
UpdateStatusIndicators();
sysTime.text = BatteryLevelPlugin.GetSysTime();

}

void UpdateStatusIndicators()
{
var currentBatteryLevel = BatteryLevelPlugin.GetBatteryLevel() * 100f;
batteryLevelText.text = String.Format("{0}%", currentBatteryLevel);

// Show the icon that matches the current level most closely.
if (currentBatteryLevel >= 88)
{
batteryLevelIcon.text = BATTERY_LEVEL_100;
}
else if (currentBatteryLevel >= 63)
{
batteryLevelIcon.text = BATTERY_LEVEL_75;
}
else if (currentBatteryLevel >= 38)
{
batteryLevelIcon.text = BATTERY_LEVEL_50;
}
else if (currentBatteryLevel >= 13)
{
batteryLevelIcon.text = BATTERY_LEVEL_25;
}
else
{
batteryLevelIcon.text = BATTERY_LEVEL_0;
}
}
}


按照步骤编译运行

步骤:1. 选择Build的平台->->2. 添加Scenes ->-> 3. 设置Identification ->-> 4. 设置包名和 API Level ->-> 5. 编译运行

效果展示



源代码

https://github.com/Rolyyu/UnityAndroidPlugin

参考文献

[1] https://docs.unity3d.com/Manual/PluginsForAndroid.html
[2] http://www.voidcn.com/blog/Silk2018/article/p-6632911.html
[3] http://blog.csdn.net/zhangdi2017/article/details/65629589
[4] https://www.yangzhenlin.com/unity-android-plugin/
[5] https://medium.com/@datdeveloper/how-to-make-android-plugin-for-unity-take-photo-from-camera-and-gallery-c12fe247c770
[6] http://addcomponent.com/android-native-plugin-unity/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息