• Unity3D与iOS的交互 简单版开箱即用


    本文适合的情况如下:

    Unity客户端人员 与 IOS端研发人员合作的情况

    目录

    From U3D to iOS

    实现原理

    1.unity工程目录创建2个文件 NativeCallProxy.m、NativeCallProxy.h 并且放到Unity工程目录Plugins/iOS/unity_ios_plus目录下

    2.创建C#调用脚本 定义对应.mm脚本的 调用接口,调用也如下


    From U3D to iOS

    实现原理

    由于U3D无法直接调用Objc或者Swift语言声明的接口,幸好U3D的主要语言是C#,因此可以利用C#的特性来访问C语言所定义的接口,然后再通过C接口再调用ObjC的代码(对于Swift代码则还需要使用OC桥接)。

    下面演示:利用C#的特性来访问C语言所定义的接口,然后再通过C接口再调用ObjC的代码

    1.unity工程目录创建2个文件 NativeCallProxy.m、NativeCallProxy.h 并且放到Unity工程目录Plugins/iOS/unity_ios_plus目录下

     NativeCallProxy.m代码内容:

    1. #import <Foundation/Foundation.h>
    2. #import "NativeCallProxy.h"
    3. //固定写法
    4. @implementation FrameworkLibAPI
    5. id<NativeCallsProtocol> api = NULL;
    6. +(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi
    7. {
    8. api = aApi;
    9. }
    10. //固定写法结束
    11. @end
    12. //固定写法
    13. extern "C" {
    14. //void showHostMainWindow(const char* color) { return [api showHostMainWindow:[NSString stringWithUTF8String:color]]; };
    15. //返回字符串的1个函数
    16. const char* unityCallGetInitData(){
    17. NSString* str=[api unityCallGetInitData];
    18. char* ret = nullptr;
    19. // ret = (char*)malloc([str length]+1);
    20. // memcpy(ret, [str UTF8String], ([str length])+1);
    21. ret = (char*)malloc([str length]);
    22. memcpy(ret, [str UTF8String], [str length]);
    23. return ret;
    24. };
    25. //无返回的1个函数
    26. void unityCallJumpLogin(){
    27. return [api unityCallJumpLogin];
    28. };
    29. //无返回、传入字符串的函数
    30. void unityCallJumpToRecharge(const char* gameStatus,const char* receOBName,const char* methodName){
    31. return [api unityCallJumpToRecharge:[NSString stringWithUTF8String:gameStatus] :[NSString stringWithUTF8String:receOBName] :[NSString stringWithUTF8String:methodName]];
    32. };
    33. void unityCallCloseVC(){
    34. return [api unityCallCloseVC];
    35. };
    36. //隱私按鈕
    37. void onPrivacyButton(){
    38. return [api onPrivacyButton];
    39. }
    40. //儲值按鈕
    41. const char* storedValue(){
    42. NSString* str=[api storedValue];
    43. char* ret = nullptr;
    44. ret = (char*)malloc([str length]);
    45. memcpy(ret, [str UTF8String], [str length]);
    46. return ret;
    47. }
    48. //切换游戏
    49. const char* ChangeGame(){
    50. NSString* str=[api ChangeGame];
    51. char* ret = nullptr;
    52. ret = (char*)malloc([str length]);
    53. memcpy(ret, [str UTF8String], [str length]);
    54. return ret;
    55. }
    56. }
    57. //extern "C" {
    58. //
    59. //}

    NativeCallProxy.h代码内容

    1. // [!] important set UnityFramework in Target Membership for this file
    2. // [!] and set Public header visibility
    3. //固定写法
    4. #import <Foundation/Foundation.h>
    5. // NativeCallsProtocol defines protocol with methods you want to be called from managed
    6. @protocol NativeCallsProtocol
    7. @required
    8. // other methods 自定义方法
    9. /**
    10. 获取初始json数据:baseUrl、mac_id、gameType、jwt 【对应mm文件的自定义函数名】
    11. */
    12. -(NSString*)unityCallGetInitData;
    13. /**
    14. 重新登录 【对应mm文件的自定义函数名的接口】
    15. */
    16. -(void)unityCallJumpLogin;
    17. /**
    18. 跳充值页前保存数据,充值页返回后把保存的数据发信息给unity 【对应mm文件的自定义函数名的接口】
    19. */
    20. -(void)unityCallJumpToRecharge:(NSString*) gameStatus :(NSString*) receOBName :(NSString*) methodName;
    21. //ios——关闭vc 【对应mm文件的自定义函数名的接口】
    22. -(void)unityCallCloseVC;
    23. //隱私按鈕 【对应mm文件的自定义函数名的接口】
    24. -(void)onPrivacyButton;
    25. //储值按钮 【对应mm文件的自定义函数名的接口】
    26. -(NSString*)storedValue;
    27. //ch Game 【对应mm文件的自定义函数名的接口】
    28. -(NSString*)ChangeGame;
    29. //自定义方法结束
    30. @end
    31. //以下为固定写法
    32. __attribute__ ((visibility("default")))
    33. @interface FrameworkLibAPI : NSObject
    34. // call it any time after UnityFrameworkLoad to set object implementing NativeCallsProtocol methods
    35. +(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi;
    36. @end

    勾选iOS平台

    2.创建C#调用脚本 定义对应.mm脚本的 调用接口,调用也如下

    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using LitJson;
    5. using UnityEngine;
    6. using UnityEngine.UI;
    7. using System.Runtime.InteropServices;
    8. using UnityEngine.SceneManagement; //引入 这个命名空间,让unity可以使用 Assets/Plugins/iOS 或 Android/ 这里的dll文件
    9. ///
    10. /// 呼叫安卓或 IOS 工具类
    11. ///
    12. public class CallAndroidOrIos :MonoBehaviour
    13. {
    14. public static CallAndroidOrIos instance;
    15. private void Awake()
    16. {
    17. instance = this;
    18. }
    19. #region 关于IOS的操作
    20. ///
    21. /// 模拟 安卓或iOS ,DLL的类;
    22. ///
    23. public class NativeAPI
    24. {
    25. #if UNITY_EDITOR || UNITY_ANDROID
    26. ///
    27. /// 获取 IOS返回的 json数据 :baseUrl、mac_id、gameType、jwt
    28. ///
    29. public static string unityCallGetInitData()
    30. {
    31. return "";
    32. }
    33. ///
    34. /// 让IOS 呼叫重新登录
    35. ///
    36. ///
    37. public static void unityCallJumpLogin()
    38. {
    39. Debug.Log("呼叫 ios重新登录");
    40. }
    41. ///
    42. /// IOS充值的返回
    43. ///
    44. public static void unityCallJumpToRecharge(string gameStatus, string
    45. receOBName, string methodName)
    46. {
    47. }
    48. ///
    49. /// 关闭当前 IOS活动页
    50. ///
    51. public static void unityCallCloseVC()
    52. {
    53. }
    54. ///
    55. /// 隐私按钮
    56. ///
    57. public static void onPrivacyButton()
    58. {
    59. }
    60. ///
    61. /// 储值被点击
    62. ///
    63. public static string storedValue()
    64. {
    65. return "";
    66. }
    67. ///
    68. /// 切换G
    69. ///
    70. ///
    71. public static string ChangeGame()
    72. {
    73. return "";
    74. }
    75. #elif UNITY_IOS || UNITY_TVOS
    76. //定义对应.mm脚本的 调用接口-----------
    77. [DllImport("__Internal")]
    78. public static extern string unityCallGetInitData();
    79. [DllImport("__Internal")]
    80. public static extern void unityCallJumpLogin();
    81. [DllImport("__Internal")]
    82. public static extern void unityCallJumpToRecharge(string gameStatus, string
    83. receOBName, string methodName);
    84. [DllImport("__Internal")]
    85. public static extern void unityCallCloseVC();
    86. [DllImport("__Internal")]
    87. public static extern void onPrivacyButton();
    88. [DllImport("__Internal")]
    89. public static extern string storedValue();
    90. [DllImport("__Internal")]
    91. public static extern string ChangeGame();
    92. #endif
    93. }
    94. #endregion
    95. ///
    96. /// 被安卓调用过来的 统一函数名称 Public void Give_AnCall(string value)
    97. ///
    98. public const string Give_AnCall = "Give_AnCall";
    99. ///
    100. /// 当处于同一个物体的时候;用来区分函数名称
    101. ///
    102. public const string Give_AnCall2 = "Give_AnCall2";
    103. ///
    104. /// 关闭app当前页面
    105. ///
    106. public void unityCallCloseActivity()
    107. {
    108. if (Application.platform ==RuntimePlatform.IPhonePlayer|| Application.platform==RuntimePlatform.OSXEditor)
    109. {
    110. Debug.Log("IOS 关闭当前Activity----------");
    111. NativeAPI.unityCallCloseVC();//关闭Ios 活动页
    112. }
    113. else
    114. {
    115. Debug.Log("关闭当前Activity----------");
    116. AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
    117. AndroidJavaObject s_ActivityContext = activityClass.GetStatic("currentActivity");//可以调用非静态
    118. s_ActivityContext.Call("unityCallCloseActivity");//关闭页面
    119. }
    120. }
    121. ///
    122. /// 获取 玩家进入哪个游戏类型 [ 0是推币机 1是连连看 -1是未知类型]
    123. /// 并且获取到 baseURL mac_id jwt gameType
    124. ///
    125. public IEnumerator unityCallGetInitData(Actionstring> callBack)
    126. {
    127. int Time = 0;
    128. string strData="";
    129. string Tips="";
    130. try
    131. {
    132. if (Application.platform ==RuntimePlatform.IPhonePlayer)
    133. {
    134. strData = NativeAPI.unityCallGetInitData();
    135. }
    136. else
    137. {
    138. AndroidJavaClass activityClass;
    139. AndroidJavaObject s_ActivityContext;
    140. activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); //只能调用静态
    141. s_ActivityContext = activityClass.GetStatic("currentActivity"); //可以调用非静态
    142. strData = s_ActivityContext.Call<string>("unityCallGetInitData");
    143. }
    144. }
    145. catch (Exception e)
    146. {
    147. Tips = "Error:" + e;
    148. if (callBack != null)
    149. {
    150. callBack(null, Tips);
    151. }
    152. //跳出协程的执行
    153. yield break;
    154. }
    155. while (string.IsNullOrEmpty(strData))//等待回调
    156. {
    157. yield return new WaitForSeconds(1);
    158. Time++;
    159. if (Time >= 10)
    160. {
    161. break; //跳出循环
    162. }
    163. }
    164. //还是没有 收到安卓返回数据
    165. if (string.IsNullOrEmpty(strData))
    166. {
    167. if (callBack!=null)
    168. {
    169. Tips = "Get the Android Or IOS data timeout";//获取安卓数据超时
    170. callBack(null, Tips);
    171. }
    172. }
    173. else
    174. {
    175. JsonData jsonData = JsonMapper.ToObject(strData);
    176. if (callBack!=null)
    177. {
    178. callBack(jsonData, "successful");
    179. }
    180. }
    181. }
    182. ///
    183. /// 跳转到 充值界面 (1.当前游戏场景名称 ,2.物体名称 ,3.安卓返回参数到 哪个函数接收)
    184. ///
    185. public void unityCallJumpToRecharge(string ScreenName, string receOBName, string methodName)
    186. {
    187. if (Application.platform ==RuntimePlatform.IPhonePlayer)
    188. {
    189. NativeAPI.unityCallJumpToRecharge(ScreenName,receOBName,methodName);//跳转到 充值
    190. }
    191. else
    192. {
    193. Debug.Log("呼叫 安卓跳转充值!----------------");
    194. AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
    195. AndroidJavaObject s_ActivityContext = activityClass.GetStatic("currentActivity");//可以调用非静态
    196. s_ActivityContext.Call("unityCallJumpToRecharge",ScreenName,receOBName,methodName);
    197. }
    198. }
    199. ///
    200. /// 跳转到 重新登录界面
    201. ///
    202. public void unityCallJumpLogin()
    203. {
    204. if (Application.platform ==RuntimePlatform.IPhonePlayer)
    205. {
    206. NativeAPI.unityCallJumpLogin();//IOS 进入 重新登录页面
    207. }
    208. else
    209. {
    210. Debug.Log("跳转到重新登录的Activity--------------");
    211. AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
    212. AndroidJavaObject s_ActivityContext = activityClass.GetStatic("currentActivity");//可以调用非静态
    213. s_ActivityContext.Call("unityCallJumpLogin");
    214. activityClass = null;
    215. s_ActivityContext = null;
    216. }
    217. }
    218. ///
    219. /// 显示UnityLog到 安卓调试窗
    220. ///
    221. public void unityCallPrintLog(string log)
    222. {
    223. if (Application.platform ==RuntimePlatform.IPhonePlayer)
    224. {
    225. //TODO
    226. }
    227. else
    228. {
    229. AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
    230. AndroidJavaObject s_ActivityContext = activityClass.GetStatic("currentActivity");//可以调用非静态
    231. s_ActivityContext.Call("unityCallPrintLog",log);
    232. }
    233. }
    234. /// 打开隐私按钮
    235. public void onPrivacyButton()
    236. {
    237. if (Application.platform ==RuntimePlatform.IPhonePlayer)
    238. {
    239. NativeAPI.onPrivacyButton();
    240. }
    241. else
    242. {
    243. Debug.Log("打开隐私按钮");
    244. }
    245. }
    246. /// 打开储值
    247. public void storedValue()
    248. {
    249. if (Application.platform == RuntimePlatform.IPhonePlayer)
    250. {
    251. Debug.Log("ios打开储值:"+NativeAPI.storedValue());
    252. }
    253. else
    254. {
    255. Debug.Log("打开储值按钮");
    256. if (Application.platform == RuntimePlatform.WindowsEditor)
    257. {
    258. //LogoGM.instance.HeroInfo_PlayCount(5);//默认给+5个
    259. //LogoGM.instance.HeroInfoSave();//PC端 增加可玩次数保存
    260. }
    261. }
    262. }
    263. /// 查询是否切换游戏
    264. ///
    265. public string ChangeGame()
    266. {
    267. if (Application.platform == RuntimePlatform.IPhonePlayer)
    268. {
    269. return "IOS查询是否切换" + NativeAPI.ChangeGame();
    270. }
    271. else
    272. {
    273. return "PC查询游戏切换";
    274. }
    275. }
    276. ///
    277. /// IOS调用Unity的方法:UnitySendMessage("物体名", "函数名", "回调字符串");
    278. ///
    279. ///
    280. public void ReceIosMsg(string msg)
    281. {
    282. Debug.Log("IOS 回调的信息");
    283. /*
    284. //根据游戏的场景划分 确定回调逻辑
    285. int scenceId = SceneManager.GetActiveScene().buildIndex;
    286. if (string.IsNullOrEmpty(msg))
    287. {
    288. Debug.Log(scenceId+"场景 收到IOS消息为空字符");
    289. }
    290. else
    291. {
    292. string strType;
    293. string strCode;
    294. //字符串转json对象
    295. JsonData jsonData = JsonMapper.ToObject(msg);
    296. switch (scenceId)
    297. {
    298. case 0://场景0
    299. try
    300. {
    301. strType = jsonData["type"].ToString();
    302. strCode = jsonData["str"].ToString();
    303. if (strType=="ChuShiHua")
    304. {
    305. int code = int.Parse(strCode);
    306. if (code==0)//非游戏 静止在这个界面
    307. {
    308. Debug.Log(scenceId+"场景收到非游戏Code,等待跳转");
    309. //SetOrientationPortrait();//设置为竖屏
    310. mSpr.gameObject.SetActive(false);
    311. TestUpsideDown(1);
    312. }
    313. else
    314. {
    315. Debug.Log("正常游戏");
    316. toMenu();
    317. }
    318. }
    319. else
    320. {
    321. Debug.Log(scenceId+"场景 收到的不是初始化字段:"+strType);
    322. }
    323. }
    324. catch (Exception e)
    325. {
    326. Debug.LogError(string.Format("{0} 场景 解析数据出错 原数据:{1}",scenceId,msg));
    327. }
    328. break;
    329. case 1://场景1
    330. try
    331. {
    332. strType = jsonData["type"].ToString();
    333. strCode = jsonData["str"].ToString();
    334. if (strType == "APPStore_Scuess")
    335. {
    336. //TODO 储值成功 添加可玩次数
    337. Debug.Log("储值成功");
    338. HeroInfo_PlayCount(9999);
    339. HeroInfoSave();//场景1的储值
    340. }
    341. else
    342. {
    343. Debug.Log(scenceId+"场景 收到的不是储值字段:"+strType);
    344. }
    345. }
    346. catch (Exception e)
    347. {
    348. Debug.LogError(string.Format("{0} 场景 解析数据出错 原数据:{1}",scenceId,msg));
    349. }
    350. break;
    351. }
    352. }
    353. */
    354. }
    355. }

  • 相关阅读:
    抢票攻略来了!疫情后的首届云栖大会
    python jieba分词,一次性添加多个词
    Map集合继承结构
    P3384 【模板】轻重链剖分/树链剖分
    Unity实现设计模式——模板方法模式
    Java 开发必备:腾讯架构师解析 Java 并发编程从入门到精通
    java递归
    VR全景拍摄发展如何?在各行业应用中有优势吗?
    PM2管理器无法使用解决方法
    通信设备为什么需要接地阻?
  • 原文地址:https://blog.csdn.net/weixin_39114763/article/details/134178642