Unity3D Native 插件开发(4)— 异步与回调
发表于2016-05-17
这篇文章的目的,主要是介绍 Unity 与 Android、iOS 直接的函数异步调用机制
使用异步回调
在 Unity 游戏中,帧率直接影响游戏体验,更高的帧率给到用户更流畅的游戏体验
特别是在手机设备上,机器性能远比不上PC机器。所以应该在 Unity 尽量避免同步的耗时方法调用,避免游戏卡顿
而一些 Native 代码调用有很多很“重”的函数调用,所以应该异步调用就十分必要
在之前的文章中已经介绍了如何在 Unity 中调用 Native 代码,本章节
消息机制
Unity 中提供了一种消息机制来解决与 Native 层代码调用的问题,Android 和 iOS 平台的函数定义如下:
iOS 函数定义在 UnityInterface.h 头文件中
void UnitySendMessage(const char* obj, const char* method, const char* msg);
Android 函数定义在 com.unity3d.player.UnityPlayer 类中
public static native void UnitySendMessage(String obj, String method, String msg);
Unity 提供了jar包,方便用户在 Android Studio(或ADT)中使用,Mac OS 路径为 Unity 程序子目录:
/PlaybackEngines/AndroidPlayer/Variations/mono/Development/Classes/classes.jar
Windows 路径为安装程序子目录
EditorDataPlaybackEnginesandroidplayerdevelopmentbinclasses.jar
该方法向 Unity 的一个名为 “obj” 的 GameObject 对象发送一个消息,调用 “method” 方法,传递参数为 “msg”
例如,在游戏场景中有一个名为 “SomeGameObject” 的 GameObject ,并且绑定了脚本 SomeScript.cs,其中有个方法为 SomeFunction :
namespace NameSpace {
class SomeScript : MonoBehaviour {
void SomeFunction(string msg) {
Debug.Log("get native message : " + msg);
}
}
}
那么我们在 Native 代码中调用
iOS
UnitySendMessage("SomeGameObject", "SomeFunction", "this is test message");
Android
UnitySendMessage("SomeGameObject", "SomeFunction", "this is test message");
就可以实现在 Native 代码中调用 Unity 中的方法
不过该方法限制性也比较强:
函数定义必须是带一个string参数的函数
有一帧延迟
完整的示例
我们以弹出 Native 系统提示框,在 Unity 获取用户是点击了确认还是取消为例
Unity 代码
在游戏场景中,新建一个名为 “NativeAlert” 的 GameObject,新建 NativeAlert.cs 脚本并绑定到新建的 GameObject 上;脚本中,添加如下代码:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class NativeAlert : MonoBehaviour {
[DllImport ("__Internal")]
private static extern void IOS_NativeAlert (string title, string message);
void OnNativeAlertResult(string flag) {
Debug.Log ("user choose : " + flag);
}
void Start () {
Debug.Log ("start ... ");
#if UNITY_EDITOR
if(EditorUtility.DisplayDialog("choose one", "choonse Yes or No", "Yes", "No")) {
OnNativeAlertResult("YES");
}
else {
OnNativeAlertResult("NO");
}
#elif UNITY_IOS
IOS_NativeAlert("choose one", "choonse Yes or No");
#elif UNITY_ANDROID
AndroidJavaObject jo = new AndroidJavaObject("com.tencent.imsdk.Alert", "choose one", "choonse Yes or No");
if(jo == null) {
OnNativeAlertResult("NO");
}
else {
try{
jo.Call("showAlert");
}
catch(AndroidJavaException e){
Debug.Log("get android java exeption : " + e.Message);
OnNativeAlertResult("NO");
}
}
#else
OnNativeAlertResult("NO");
#endif
}
}
iOS 代码
在 Unity 目录 Assets/Plugin/iOS 目录下,新建 NativeAlert.mm 文件,并编辑添加如下代码
extern "C" {
void IOS_NativeAlert (const char* title, const char* message) {
NSLog(@"call native alert : %s , %s", title, message);
UIAlertController* alertController = [UIAlertController alertControllerWithTitle:@(title) message:@(message) preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction* yesAction = [UIAlertAction actionWithTitle:@"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UnitySendMessage("NativeAlert", "OnNativeAlertResult", "YES");
}];
[alertController addAction:yesAction];
UIAlertAction* noAction = [UIAlertAction actionWithTitle:@"NO" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UnitySendMessage("NativeAlert", "OnNativeAlertResult", "NO");
}];
[alertController addAction:noAction];
[UnityGetGLViewController() presentViewController:alertController animated:YES completion:^() {
NSLog(@"show alert view done");
}];
}
}
Android 代码
新建 Android 组件(Module),命名包名为 com.tencent.imsdk,新建 Alert.java 文件,并添加如下代码:
package com.tencent.imsdk;
import android.app.AlertDialog;
import android.content.DialogInterface;
import com.unity3d.player.UnityPlayer;
public class Alert {
private String mTitile;
private String mContent;
public Alert(String title, String content) {
mTitile = title;
mContent = content;
}
public void showAlert() {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(UnityPlayer.currentActivity)
.setTitle(mTitile)
.setMessage(mContent)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
UnityPlayer.UnitySendMessage("NativeAlert", "OnNativeAlertResult", "YES");
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
UnityPlayer.UnitySendMessage("NativeAlert", "OnNativeAlertResult", "NO");
}
})
.show();
}
});
}
}
这里我们将代码放到了 UiThread 中运行,后续文件将针对这一使用方式进行说明
编译组件,将jar包拷贝到 Unity 的 Assets/Plugins/Android/libs目录下(如果没有就新建相应的目录)
运行结果
编译 iOS/Android 工程并运行,可以看到 Native 弹窗,并在各自平台输出日志
user choose : YES