找回密码
 立即注册
查看: 380|回复: 0

Unity & iOS 数据通用交互方式

[复制链接]
发表于 2022-3-3 07:54 | 显示全部楼层 |阅读模式
背景

就是单纯想做。
干货开始

iOS 向 Unity 发送消息


这种情况下一般使用UnitySendMessage的方法来实现,看看文档怎么说
// https://docs.unity3d.com/cn/2021.2/Manual/PluginsForIOS.html
使用 UnitySendMessage 时具有以下限制:

1.通过原生代码,只能调用与以下签名对应的脚本方法:void MethodName(string message);。 2.对 UnitySendMessage 的调用是异步的,并有一帧延迟。 3.如果两个或多个游戏对象具有相同的名称,则在使用 UnitySendMessage 时可能导致冲突。
无论使用什么方式调用UnitySendMessage方法,方法最终会使用主线程运行。

假如客户端有一个渲染需求,然后呢,Unity这边正好有一个接口满足,叫 LoadRender,代码如下

iOS 这边需要简单封装一下方便调用
/*iOSObjc++*/- (void)api_LoadRender:(NSString *)json{  // RenderController 表示类名  //LoadRender 表示方法名  // [json UTF8String] 参数  UnitySendMessage("RenderController", "LoadRender", [json UTF8String]);}
Unity这边也需要有对应的方法实现
/*UnityC#*/ public class RenderController{  public void LoadRender(string json)  {    // 处理这次调用    // Unity 向 iOS 发送消息 告诉是成功或者失败    ...  }}Unity 向 iOS 发送消息


先看一个最简单的示例
Unity当中声明一个方法
/*UnityC#*/[DllImport("__Internal")]internal extern static void calliOS_method1();/*iOSObjc++UNITY_INTERFACE_EXPORT 和 UNITY_INTERFACE_API 在 Unity的IUnityInterface.h 中声明*/extern "C"{  //实现方法  void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API calliOS_method1()  {    // 运行逻辑1  }}
这样调用calliOS_method1时,客户端就及时收到并运行了代码,注意哦!这里是及时响应的。
当然业务多了之后,cs当中定义方法也会增多,维护成本也开始变高。
到目前为止,客户端想要回传数据给Unity端还不能支持。
所以我们得改造一下,支持返回值的方法来试试。
1. 重新定义下接口,使之能通用起来


cs 代码中申明一个通用方法,有一个指针输入,和一个指针返回
/*iOSObjc++*/[DllImport("__Internal")]internal extern static IntPtr onETPlatform(IntPtr entity);
这样的话就可以根据业务需求再定义输入结构
这时,上面的LoadRender方法就可以加入回调能力,
当客户端通过api_LoadRender发起API调用后,Unity下一帧运行好后就调用通用接口回调给iOS端
2. 双边定义相同结构

/*UnityC#*/public struct ETPlatformAPICallBack{  public int type;         //结  public int componentType;//构  public int status;       //一  public double re_status; //致}/*iOSObjc++*/extern "C"{  typedef struct _ETPlatformAPICallBack  {    int type;         //结    int componentType;//构    int status;       //一    double re_status; //致  } ETPlatformAPICallBack;}
Unity端的LoadRender方法做完后,需要创建一个 ETPlatformAPICallBack 对象,并且赋值(表示状态),再通过一个简单方法转换成 一个IntPtr 对象,如下
// T是范型private static IntPtr StructToIntPtr<T>(T a){  IntPtr sp = Marshal.AllocCoTaskMem(Marshal.SizeOf(a));  Marshal.StructureToPtr(a, sp, false);  return sp;}
转换了对象后,调用onETPlatform 通用接口,传入刚才转义好的结构体,iOS端就可以及时收到消息
3.在iOS端实现onETPlatform方法


C# 的 IntPtr 对应 void*
/*iOSObjc++*/extern "C"{  void* UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API onETPlatform(void* entity)  {    //由于所有的struct 都必须是 int type 开头,所以我拿到指针的前面位置转成int,然后判定是什么类型    int32_t *p_int = static_cast<int32_t *>(entity);    int type = p_int[0];    switch(type) {      case ETUnityPlatformStructType_LoadRenderCallBack: // 调用LoadRender 接口后回调      {         ETPlatformAPICallBack *cety = static_cast<ETPlatformAPICallBack *>(entity);         int status = cety.status; //使用 Unity 传过来的值 定义0代表成功 -1表示失败         cety.re_status = 1.2; //把新值传入到 Unity 端      } break;      default:        assert(false); // 不支持        break;    }    return entity;  }}
稍微解释一下
onETPlatform方法接收到一个entity,然后把entity强转成int指针,获取指针位置0的int值
通过type值确定它的所属struct类型,所以每一个struct的第一个字段都是int类型
然后再static_cast转换成对应的struct,然后根据业务使用字段或者修改字段
最后直接返回entity即可(因为是指针操作)。
4.Unity获取返回值


完成调用后会收到IntPtr对象,再把这个对象转换成strcut,如下
/*UnityC#*/private static T IntPtrToStruct<T>(IntPtr ptr){  return (T)Marshal.PtrToStructure(ptr, typeof(T));}
最后拿到iOS的返回数据

关键代码
// Unity C#public class RenderController{  public struct ETPlatformAPICallBack  {    public int type;         //结    public int componentType;//构    public int status;       //一    public double re_status; //致  }  private static IntPtr StructToIntPtr<T>(T a)  {    IntPtr sp = Marshal.AllocCoTaskMem(Marshal.SizeOf(a));    Marshal.StructureToPtr(a, sp, false);    return sp;  }  private static T IntPtrToStruct<T>(IntPtr ptr)  {    return (T)Marshal.PtrToStructure(ptr, typeof(T));  }  private T InvokePlatform_iOS<T>(T entity)  {    IntPtr sp = StructToIntPtr(entity); // 1 把要给iOS的 C# struct 转成 指针    var ptr = onETPlatform(sp);// 把数据传入到iOS端    var ver = IntPtrToStruct<T>(ptr);// 把iOS端的返回数据转换成 C# 的struct 对象    Marshal.FreeCoTaskMem(sp);// 释放    return ver;  }  public void LoadRender(string json)  {    // 处理这次调用    // Unity 向 iOS 发送消息 告诉是成功或者失败 的伪代码    ...    var cb = new ETPlatformAPICallBack(); // 创建对象    cb.type = ETUnityPlatformStructType_LoadRenderCallBack;// 这里要保证和iOS的枚举一致    cb.status = 0;// 0=成功     var ety = InvokePlatform_iOS(cb); // 请求客户端,并获取客户端返回的数据    // ety.re_status 值为客户端写入值 1.2  }}参考资料

构建适用于 iOS 的插件
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-17 02:38 , Processed in 0.118613 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表