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代码内容:

#import <Foundation/Foundation.h>
#import "NativeCallProxy.h"

//固定写法
@implementation FrameworkLibAPI

id<NativeCallsProtocol> api = NULL;
+(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi
{
    api = aApi;
}
//固定写法结束
@end

//固定写法
extern "C" {
//void showHostMainWindow(const char* color) { return [api showHostMainWindow:[NSString stringWithUTF8String:color]]; };


//返回字符串的1个函数
const char*  unityCallGetInitData(){

    NSString* str=[api unityCallGetInitData];
    char* ret = nullptr;
//    ret = (char*)malloc([str length]+1);
//    memcpy(ret, [str UTF8String], ([str length])+1);

        ret = (char*)malloc([str length]);
        memcpy(ret, [str UTF8String], [str length]);
        return ret;    
};

//无返回的1个函数
void unityCallJumpLogin(){
  return  [api unityCallJumpLogin];
};

//无返回、传入字符串的函数
void unityCallJumpToRecharge(const char* gameStatus,const char* receOBName,const char* methodName){
    return [api unityCallJumpToRecharge:[NSString stringWithUTF8String:gameStatus] :[NSString stringWithUTF8String:receOBName] :[NSString stringWithUTF8String:methodName]];
    
};



void unityCallCloseVC(){
    return [api unityCallCloseVC];
};

//隱私按鈕
void onPrivacyButton(){
    return [api onPrivacyButton];
}

//儲值按鈕
const char* storedValue(){

    NSString* str=[api storedValue];
    char* ret = nullptr; 

    ret = (char*)malloc([str length]);
    memcpy(ret, [str UTF8String], [str length]);
    return ret;
}

//切换游戏
const char* ChangeGame(){

    NSString* str=[api ChangeGame];
    char* ret = nullptr;

    ret = (char*)malloc([str length]);
    memcpy(ret, [str UTF8String], [str length]);
    return ret;

}



}

//extern "C" {
//
//}

NativeCallProxy.h代码内容

// [!] important set UnityFramework in Target Membership for this file
// [!]           and set Public header visibility

//固定写法
#import <Foundation/Foundation.h>
// NativeCallsProtocol defines protocol with methods you want to be called from managed
@protocol NativeCallsProtocol
@required


// other methods 自定义方法
/**
 获取初始json数据:baseUrl、mac_id、gameType、jwt 【对应mm文件的自定义函数名】
 */
-(NSString*)unityCallGetInitData;
/**
 重新登录	【对应mm文件的自定义函数名的接口】
 */
-(void)unityCallJumpLogin;
/**
 跳充值页前保存数据,充值页返回后把保存的数据发信息给unity 【对应mm文件的自定义函数名的接口】
 */
-(void)unityCallJumpToRecharge:(NSString*) gameStatus :(NSString*) receOBName :(NSString*) methodName;

//ios——关闭vc 【对应mm文件的自定义函数名的接口】
-(void)unityCallCloseVC;

//隱私按鈕 【对应mm文件的自定义函数名的接口】
-(void)onPrivacyButton;

//储值按钮 【对应mm文件的自定义函数名的接口】
-(NSString*)storedValue;

//ch Game 【对应mm文件的自定义函数名的接口】
-(NSString*)ChangeGame;

//自定义方法结束
@end

//以下为固定写法
__attribute__ ((visibility("default")))
@interface FrameworkLibAPI : NSObject
// call it any time after UnityFrameworkLoad to set object implementing NativeCallsProtocol methods
+(void) registerAPIforNativeCalls:(id<NativeCallsProtocol>) aApi;

@end


勾选iOS平台

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

using System;
using System.Collections;
using System.Collections.Generic;
using LitJson;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using UnityEngine.SceneManagement; //引入  这个命名空间,让unity可以使用 Assets/Plugins/iOS 或 Android/ 这里的dll文件

/// <summary>
/// 呼叫安卓或 IOS 工具类
/// </summary>
public class CallAndroidOrIos :MonoBehaviour
{

    public static CallAndroidOrIos instance;
    private void Awake()
    {
        instance = this;
    }

    #region  关于IOS的操作

    
 /// <summary>
 /// 模拟 安卓或iOS ,DLL的类;
 /// </summary>
    public class NativeAPI
   {
       
#if UNITY_EDITOR || UNITY_ANDROID

       /// <summary>
       /// 获取 IOS返回的 json数据 :baseUrl、mac_id、gameType、jwt
       /// </summary> 
       public static string unityCallGetInitData()
       {
           return "";
       }

       /// <summary>
       /// 让IOS 呼叫重新登录
       /// </summary>
       /// <returns></returns>
       public static void unityCallJumpLogin()
       {
           Debug.Log("呼叫 ios重新登录");
       }


       /// <summary>
       /// IOS充值的返回 
       /// </summary> 
       public static void unityCallJumpToRecharge(string gameStatus, string
           receOBName, string methodName)
       {
           
       }


       /// <summary>
       /// 关闭当前 IOS活动页
       /// </summary>
       public static  void unityCallCloseVC()
       {
           
       }

       /// <summary>
       /// 隐私按钮
       /// </summary>
       public static void onPrivacyButton()
       {
           
           
       }

       /// <summary>
       /// 储值被点击
       /// </summary>
       public static string storedValue()
       {
           return "";
       }
     
       /// <summary>
       /// 切换G
       /// </summary>
       /// <returns></returns>
       public static string ChangeGame()
       {
           return "";
       }

    
     
  
#elif UNITY_IOS || UNITY_TVOS 

       //定义对应.mm脚本的 调用接口-----------
       [DllImport("__Internal")]
       public static extern string unityCallGetInitData();

       [DllImport("__Internal")]
       public static extern void unityCallJumpLogin();

      [DllImport("__Internal")]
       public static extern void unityCallJumpToRecharge(string gameStatus, string
           receOBName, string methodName);
       
        [DllImport("__Internal")]
        public static extern void unityCallCloseVC();

       [DllImport("__Internal")]
       public static extern void onPrivacyButton();

       [DllImport("__Internal")]
       public static extern string storedValue();
       
       [DllImport("__Internal")]
       public static extern string ChangeGame();
#endif

   }
    

    #endregion
    
    

    /// <summary>
    /// 被安卓调用过来的 统一函数名称  Public void Give_AnCall(string value)
    /// </summary>
    public const string Give_AnCall = "Give_AnCall";

    /// <summary>
    /// 当处于同一个物体的时候;用来区分函数名称
    /// </summary>
    public const string Give_AnCall2 = "Give_AnCall2";
    
    /// <summary>
    /// 关闭app当前页面
    /// </summary>
    public void unityCallCloseActivity()
    {

        if (Application.platform ==RuntimePlatform.IPhonePlayer|| Application.platform==RuntimePlatform.OSXEditor)
        {
            Debug.Log("IOS 关闭当前Activity----------");
            NativeAPI.unityCallCloseVC();//关闭Ios 活动页
        }
        else
        {
            Debug.Log("关闭当前Activity----------");
            AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
            AndroidJavaObject  s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");//可以调用非静态
            s_ActivityContext.Call("unityCallCloseActivity");//关闭页面 
        }
        
     
    }



    /// <summary>
    /// 获取 玩家进入哪个游戏类型 [ 0是推币机 1是连连看 -1是未知类型]
    /// 并且获取到 baseURL mac_id jwt gameType
    /// </summary> 
    public IEnumerator unityCallGetInitData(Action<JsonData,string> callBack)
    {
        int Time = 0;
        string strData="";
        string Tips="";
  
        try
        {
            if (Application.platform ==RuntimePlatform.IPhonePlayer)
            {
                strData = NativeAPI.unityCallGetInitData();
            }
            else
            {
                AndroidJavaClass activityClass;
                AndroidJavaObject s_ActivityContext;
                activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); //只能调用静态
                s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity"); //可以调用非静态
                strData = s_ActivityContext.Call<string>("unityCallGetInitData");
            }

        }
        catch (Exception e)
        {
            Tips = "Error:" + e;
            if (callBack != null)
            {
                callBack(null, Tips);
            }
 
            //跳出协程的执行
            yield break;
        }
        
        while (string.IsNullOrEmpty(strData))//等待回调
        {
            yield return new WaitForSeconds(1);
            Time++;
            if (Time >= 10)
            {
                break; //跳出循环
            }
        }

        //还是没有 收到安卓返回数据  
        if (string.IsNullOrEmpty(strData))
        {
             
            if (callBack!=null)
            {
                Tips = "Get the Android Or IOS data timeout";//获取安卓数据超时
                callBack(null, Tips);
            } 
        }
        else
        {
            JsonData jsonData = JsonMapper.ToObject(strData);
            if (callBack!=null)
            {
                callBack(jsonData, "successful"); 
            }
        }
        
    }




    /// <summary>
    /// 跳转到 充值界面 (1.当前游戏场景名称 ,2.物体名称 ,3.安卓返回参数到 哪个函数接收) 
    /// </summary> 
    public void unityCallJumpToRecharge(string ScreenName, string receOBName, string methodName)
    {

        if (Application.platform ==RuntimePlatform.IPhonePlayer)
        {
            NativeAPI.unityCallJumpToRecharge(ScreenName,receOBName,methodName);//跳转到 充值
        }
        else
        {
            Debug.Log("呼叫 安卓跳转充值!----------------");
            AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
            AndroidJavaObject  s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");//可以调用非静态
            s_ActivityContext.Call("unityCallJumpToRecharge",ScreenName,receOBName,methodName); 
        }
     
    }



    /// <summary>
    /// 跳转到 重新登录界面
    /// </summary>
    public void unityCallJumpLogin()
    {
        if (Application.platform ==RuntimePlatform.IPhonePlayer)
        {
            NativeAPI.unityCallJumpLogin();//IOS 进入 重新登录页面
        }
        else
        {
            Debug.Log("跳转到重新登录的Activity--------------");
            AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
            AndroidJavaObject  s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");//可以调用非静态
            s_ActivityContext.Call("unityCallJumpLogin");
            activityClass = null;
            s_ActivityContext = null;
        }
    }



    /// <summary>
    /// 显示UnityLog到 安卓调试窗
    /// </summary>
    public void unityCallPrintLog(string log)
    {
        if (Application.platform ==RuntimePlatform.IPhonePlayer)
        {
            //TODO 
        }
        else
        {
            AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");//只能调用静态
            AndroidJavaObject  s_ActivityContext = activityClass.GetStatic<AndroidJavaObject>("currentActivity");//可以调用非静态
            s_ActivityContext.Call("unityCallPrintLog",log);
        }
        
     
 
    }


    /// <summary> 打开隐私按钮 </summary>
    public void onPrivacyButton()
    {

        if (Application.platform ==RuntimePlatform.IPhonePlayer)
        {
            NativeAPI.onPrivacyButton();
        }
        else
        {
            Debug.Log("打开隐私按钮");
        }
    }

    /// <summary> 打开储值 </summary>
    public void storedValue()
    {
        if (Application.platform == RuntimePlatform.IPhonePlayer)
        {
           Debug.Log("ios打开储值:"+NativeAPI.storedValue()); 
        }
        else
        {
            Debug.Log("打开储值按钮");
            if (Application.platform == RuntimePlatform.WindowsEditor)
            {
                //LogoGM.instance.HeroInfo_PlayCount(5);//默认给+5个
                //LogoGM.instance.HeroInfoSave();//PC端 增加可玩次数保存
                
            }
        }
    }

    /// <summary> 查询是否切换游戏 </summary>
    /// <returns></returns>
    public string ChangeGame()
    {
        if (Application.platform == RuntimePlatform.IPhonePlayer)
        {
            return "IOS查询是否切换" + NativeAPI.ChangeGame();
        }
        else
        {
            return "PC查询游戏切换";
        }
    }
    
    
    
    
    /// <summary>
    /// IOS调用Unity的方法:UnitySendMessage("物体名", "函数名", "回调字符串");
    /// </summary>
    /// <param name="msg"></param>
    public void ReceIosMsg(string msg)
    {
        Debug.Log("IOS 回调的信息");
        
        /*
        //根据游戏的场景划分 确定回调逻辑
        int scenceId = SceneManager.GetActiveScene().buildIndex;
        if (string.IsNullOrEmpty(msg))
        {
            Debug.Log(scenceId+"场景 收到IOS消息为空字符");
        }
        else
        {
            string strType;
            string strCode;
            //字符串转json对象
            JsonData jsonData = JsonMapper.ToObject(msg);
            switch (scenceId)
            {
                case 0://场景0
				
                    try
                    {
                        strType = jsonData["type"].ToString();
                        strCode = jsonData["str"].ToString();
                        if (strType=="ChuShiHua")
                        {
                            int code = int.Parse(strCode);
                            if (code==0)//非游戏 静止在这个界面
                            {
                                Debug.Log(scenceId+"场景收到非游戏Code,等待跳转");
                                //SetOrientationPortrait();//设置为竖屏
                                mSpr.gameObject.SetActive(false);
                                TestUpsideDown(1);
								 
							 
                            }
                            else
                            {
                                Debug.Log("正常游戏");
                                toMenu();
                            }
                        }
                        else
                        {
                            Debug.Log(scenceId+"场景 收到的不是初始化字段:"+strType);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(string.Format("{0} 场景 解析数据出错 原数据:{1}",scenceId,msg));
                    }
                    break;
                case 1://场景1
                    try
                    {
                        strType = jsonData["type"].ToString();
                        strCode = jsonData["str"].ToString();
                        if (strType == "APPStore_Scuess")
                        {
                            //TODO 储值成功 添加可玩次数
                            Debug.Log("储值成功");
                            HeroInfo_PlayCount(9999);
                            HeroInfoSave();//场景1的储值
                        }
                        else
                        {
                            Debug.Log(scenceId+"场景 收到的不是储值字段:"+strType);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(string.Format("{0} 场景 解析数据出错 原数据:{1}",scenceId,msg));
                    }
                    break;
            }
        }
	    */
        
		
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/116020.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring 中 BeanFactory 和 FactoryBean 有何区别?

这也是 Spring 面试时一道经典的面试问题&#xff0c;今天我们来聊一聊这个话题。 其实从名字上就能看出来个一二&#xff0c;BeanFactory 是 Factory 而 FactoryBean 是一个 Bean&#xff0c;我们先来看下总结&#xff1a; BeanFactory 是 Spring 框架的核心接口之一&#xf…

WPF布局控件之WrapPanel布局

前言&#xff1a;博主文章仅用于学习、研究和交流目的&#xff0c;不足和错误之处在所难免&#xff0c;希望大家能够批评指出&#xff0c;博主核实后马上更改。 概述&#xff1a; 后续排序按照从上至下或从右至左的顺序进行&#xff0c;具体取决于方向属性的值。WrapPanel 位…

ssh免密登录

单机 1 生成密钥 执行 ssh-keygen -t rsa &#xff08; 其中 rsa 是非对称算法&#xff09; 一路回车到底&#xff0c;生成密钥 且生成之后会在用户的根目录生成一个 “.ssh”的文件夹 2 添加公钥到 将 公钥内容追加到 authorized_keys 中&#xff1a; cat ~/.ssh/id_rsa.pub …

生成m3u8视频:批量剪辑与分割的完美结合

在视频处理领域&#xff0c;m3u8视频格式的出现为高效处理和优化视频内容提供了新的可能。尤其在批量剪辑和分割视频的过程中&#xff0c;掌握m3u8视频的生成技巧&#xff0c;意味着更高效的工作流程和更出色的创作效果。现在一起来看看云炫AI智剪如何生成m3u8视频的操作吧。 步…

RK-3399pro 萤火虫firefly 官方unbuntu 固件系统安装搜狗中文输入法

RK-3399pro 萤火虫firefly 官方unbuntu 固件系统安装搜狗输入法&#xff08;适用于所有基于Ubuntu的UI桌面系统&#xff09; 一、添加中文语言支持输入法平台fcitx 1.安装fcitx sudo apt-get install fcitx 2.然后设置fcitx为开机自启动 sudo cp /usr/share/applications/fc…

【EI会议征稿】第三届电子信息工程、大数据与计算机技术国际学术会议(EIBDCT 2024)

第三届电子信息工程、大数据与计算机技术国际学术会议&#xff08;EIBDCT 2024&#xff09; 2024 3rd International Conference on Electronic Information Engineering, Big Data and Computer Technology 第三届电子信息工程、大数据与计算机技术国际学术会议&#xff08;…

React 底层 Fiber 架构 简单理解

一、 背景 JS 是引擎是单线程运行的&#xff1b;严格来说&#xff0c;JS 引擎和页面渲染引擎在同一渲染线程&#xff0c;两者互斥。那么就会遇到这样的一种情况&#xff1a;当前面一个任务长期霸占CPU&#xff0c;后面啥事也干不了&#xff0c;浏览器卡死&#xff0c;造成极差…

WebSocket Day 01:入门案例

前言 欢迎来到WebSocket入门案例系列的第一天&#xff01;在今天的博客中&#xff0c;我们将一起探索WebSocket的基础知识和使用方法。本系列将以一个简单的入门案例为基础&#xff0c;带领您逐步了解WebSocket的原理和用法。 一、什么是 WebSocket ? WebSocket是一种在Web应…

数据结构之队的实现

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

Linux Framebuffer驱动框架、接口实现和使用

Linux 驱动-Frame Buffer代码分析 Framebufferfbmem.c部分代码分析初始化 Framebuffer 对于驱动开发人员来说&#xff0c;其实只需要针对具体的硬件平台SOC和具体的LCD&#xff08;通过焊接连接到该SOC引脚上的LCD&#xff09;来进行第一部分的寄存器编程&#xff08;红色部分&…

【音视频 | opus】opus编码的Ogg封装文件详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

项目实战之安装依赖npm install

文章目录 nvmdeasync包和node-gyp报错deasync包node-gyp报错 前言&#xff1a;有些人看着还活着其实已经凉了好一会儿了。 初拿到项目 初拿到项目肯定是先看配置 package.json的啦&#xff0c;看看都需要安装什么依赖&#xff0c;然后 npm install,OK结束 皆大欢喜。 ————…

Java字符串常用函数 详解5000字 (刷题向 / 应用向)

1.直接定义字符串 直接定义字符串是指使用双引号表示字符串中的内容&#xff0c;例如"Hello Java"、"Java 编程"等。具体方法是用字符串常量直接初始化一个 String 对象&#xff0c;示例如下&#xff1a; 1. String str"Hello Java"; 或者 …

第19期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

【C++】特殊类设计

文章目录 一、设计一个类&#xff0c;不能被拷贝二、设计一个类&#xff0c;不能被继承三、设计一个类&#xff0c;只能在栈上创建对象四、设计一个类&#xff0c;只能在堆上创建对象五、设计一个类&#xff0c;只能创建一个对象(单例模式) 在某些特殊的场景下&#xff0c;我们…

使用Gorm进行高级查询

深入探讨GORM的高级查询功能&#xff0c;轻松实现Go中的数据检索 高效的数据检索是每个应用程序性能的核心。GORM&#xff0c;强大的Go对象关系映射库&#xff0c;不仅扩展到基本的CRUD操作&#xff0c;还提供了高级的查询功能。本文是您掌握使用GORM进行高级查询的综合指南。…

【雷达原理】雷达杂波抑制方法

目录 一、杂波及其特点 1.1 什么是杂波&#xff1f; 1.2 杂波的频谱特性 二、动目标显示(MTI)技术 2.1 对消原理 2.2 数字对消器设计 三、MATLAB仿真 3.1 对消效果验证 3.2 代码 一、杂波及其特点 1.1 什么是杂波&#xff1f; 杂波是相对目标回波而言的&#xff0c;…

IDEA中如何移除未使用的import

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是全栈工…

链栈的练习

链栈练习 相关内容&#xff1a;栈的链式存储结构&#xff08;链栈&#xff09; //链栈的初始化、判空、入栈、出栈、读取栈顶元素 //链栈的结点&#xff1a;数据域、指针域 #include<stdio.h> #include<stdlib.h> typedef int Status; #define OK 1 #define ERRO…

算法通关村第五关-白银挑战队列经典问题

大家好我是苏麟 , 今天带来几道经典小题 . 大纲 两数之和 两数之和 相信大家对这道题还是很眼熟的 , 打开LeetCode第一道题就是它 , 对它可真的又爱又恨 , 很多新手朋友们想刷LeetCode但又不知道从哪开始就打开了第一题 , 结果就对算法失去了信心 . 这道题找对方法还是很容易…