Unity3D Native 插件开发(4)— 异步与回调

发表于2016-05-17
评论3 3.9k浏览
这是一个系列教程,主要内容是如何开发 Unity 插件,在理解这个教程之前需要读者有一定的 Unity、iOS、Android 开发知识:
       

Unity3D Native 插件开发(1)— 原理解析


  这篇文章的目的,主要是介绍 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

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引