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

news2025/1/22 16:49:05

本文适合的情况如下:

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;
            }
        }
	    */
        
		
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32:I²C通信原理概要

一、IIC通信原理 IIC通信和串口通信有一定的相似之处&#xff0c;都有一根共地线和两根数据线。但是传递外部信息&#xff0c;串口有两根数据线可以进行双向通信&#xff0c;也就是全双工通信。而在IIC通信下&#xff0c;其中一条数据线是用于提供同步时钟脉冲的时钟线(SCL)&am…

k8s系列文章一:安装指南

# 临时关闭分区 swapoff -a 前言 k8s是docker的升级版&#xff0c;可用于docker集群配置管理微服务 一、更新ubuntu系统版本 sudo apt update sudo apt upgrade二、添加GPG密钥(阿里源) 尽管我不知道gpg是个什么东西&#xff0c;反正跟着做就完了 curl https://mirrors.a…

PostgreSQL 进阶 - 模式匹配,过滤敏感数据,数据清理

1. 模式匹配 SELECT phone_number FROM customers;使用正则表达式替换所有非数字字符 这样可以清理和标准化电话号码数据&#xff0c;去除任何非数字字符&#xff0c;只保留数字 UPDATE customers SET phone_number REGEXP_REPLACE(phone_number, [^0-9], , g) WHERE phone…

NProgress顶部进度条的用法

大家打开一个网页的时候&#xff0c;会看到一个进度条&#xff0c;然后加载完成后进度条就消失了。这个呢&#xff0c;就是一个第三方的进度条库&#xff0c;叫做nprogress. 1.首先安装nprogress(咱直接用npm安装了) : npm install --save nprogress 2.然后在 router/index.j…

高德Go生态建设与研发实践

序 高德在构建Go生态演化过程中&#xff0c;已经实现了QPS从0到峰值千万的飞跃&#xff0c;本篇文章主要介绍在此过程中积累的一些技术决策及性能优化和重构经验。阅读本文读者会有以下3点收获&#xff1a; 1.高德Go生态发展历程及现状分析 2.高德云原生Serverless落地情况&…

MobaXterm使用VNC远程显示和控制ubuntu桌面

目录 1 在ubuntu中安装vnc 2 设置ubuntu远程连接 3 MobaXterm中连接ubuntu的vnc 1 在ubuntu中安装vnc 参考&#xff1a;Ubuntu18.04~Ubuntu22.04安装并配置VNC_ubuntu安装vnc-CSDN博客 大体流程就是在ubuntu中安装vnc&#xff0c;设置密码&#xff0c;然后配置服务&#x…

Android开发适不适合做车载开发?

众所周知&#xff0c;今年的互联网行业就业率并不是很好&#xff0c;像“开猿截流&#xff0c;公司倒闭”等事件时有发生&#xff0c;感觉市场对于人才的需求量降低了&#xff0c;给原本不景气的Android 开发行业增添了不少的难度。 随着新能源汽车行业的脱颖而出&#xff0c;…

网络安全策略制定和执行: 提供制定全面的网络安全策略的步骤和指南,以确保组织的整体安全性。

网络安全一直是IT领域的一个关键挑战。随着威胁的不断演变和增强&#xff0c;制定和执行全面的网络安全策略变得至关重要。本文将为您提供一系列步骤和指南&#xff0c;帮助您确保组织的整体安全性。 第一章&#xff1a;明确安全需求和目标 在开始制定网络安全策略之前&#…

数据抽取+dataworks的使用+ADB的应用

一&#xff0c;大数据处理之数据抽取 1&#xff0c;什么是数据抽取 在大数据领域中&#xff0c;数据抽取是指从原始数据源中提取所需的数据子集或特定数据项的过程&#xff0c; 数据抽取是数据预处理的重要步骤&#xff0c;它为后续的数据分析和建模提供了基础。 2&#xff…

Win10强制卸载更新补丁的三种方法

在Win10电脑中用户发现更新补丁出现问题了&#xff0c;想通过卸载补丁来解决问题。但是&#xff0c;许多新手用户对于强制卸载Win10系统的更新补丁的方法不是很了解&#xff0c;那么下面小编就给大家介绍关于Win10电脑内强制卸载更新补丁的简单方法吧。 方法一&#xff1a;通过…

MongoDB系例全教程

一、系列文章目录 一、MongoDB安装教程—官方原版 二、MongoDB 使用教程(配置、管理、监控)_linux mongodb 监控 三、MongoDB 基于角色的访问控制 四、MongoDB用户管理 五、MongoDB基础知识详解 六、MongoDB—Indexs 七、MongoDB事务详解 八、MongoDB分片教程 九、Mo…

Docker的安装、基础命令与项目部署

文章目录 前言一、docker安装与MySQL部署1.Linux环境下docker的安装&#xff08;1&#xff09;基于CentOS7&#xff08;2&#xff09;基于Ubuntu 二、docker基础1.常见命令&#xff08;1&#xff09;快速创建一个mysql容器&#xff08;MySQL得一键安装&#xff09;。&#xff0…

【ubuntu】搭建lamp架构

一、准备工作 1、更新源 apt-get updateapt #就是一个管理包的工具&#xff0c;理解为centos中的yum update #表示让apt执行更新的操作&#xff0c;更新的内容为软件列表。#为什么要更新软件列表&#xff1f; 就时本地会隔断时间进行同步镜像站的资源包&#xff0c;但是我…

如何滴水不漏的学完C语言?

如何滴水不漏的学完C语言&#xff1f; 学习C语言需要掌握的知识点确实非常广泛。如果你觉得学校教学中所涉及的内容有所欠缺&#xff0c;可以有很多其他方式进行补充学习。最近很多小伙伴找我&#xff0c;说想要一些C语言资料&#xff0c;然后我根据自己从业十年经验&#xff…

GPT引发智能AI时代潮流

最近GPT概念爆火&#xff0c;许多行业开始竞相发展AI &#xff0c;工作就业也将面临跳转&#xff0c;目前测试就业形势就分为了两大类&#xff0c;一类是测试行业如功能、性能、自动化综合性人才就业技能需求&#xff0c;另一类便是AI测试行业的需求普遍增长&#xff0c;原本由…

使用IO完成端口实现简单回显服务器

说明 使用IO完成端口实现简单回显服务器&#xff0c;因为是测试用的&#xff0c;所以代码很粗糙。 提醒 使用的是ReadFile、WriteFile来实现Overlapped IO&#xff0c;正式场合应该用WSARecv、WSASend&#xff0c;原因&#xff1a;来自《Windows网络编程技术》 8.2.5节 在这里…

葡萄酒中的酒精含量是多少?

当你喝完一杯加利福尼亚赤霞珠和你的朋友在一个周五的晚上出游&#xff0c;你不禁会注意到&#xff0c;这只玻璃杯能让你感觉有点不同于你前几个周末喝的意大利灰皮诺葡萄酒。出于好奇&#xff0c;你查看了一下ABV&#xff0c;发现这个数字高达14.5%&#xff01;难怪&#xff0…

requires SDK version >=3.0.1 <4.0.0, version solving failed

这个很明显是FLUTTER SDK不匹配的问题&#xff0c;需要更新flutter SDK&#xff0c;最简单的办法&#xff0c;在flutter官网的页面直接下载最新的&#xff0c;然后替换之前旧版本的flutter 官网&#xff1a; 在 Windows 操作系统上安装和配置 Flutter 开发环境 - Flutter 中文…

C++并发编程实战——07.设计无锁的并发数据结构

文章目录 设计无锁的并发数据结构定义及意义无阻塞数据结构无锁数据结构无等待数据结构无锁结构的利弊 无锁数据结构的例子无锁线程安全栈使用风险指针检测不可回收的节点使用引用计数无锁栈上的内存模型实现一个无锁的线程安全队列 设计无锁数据结构的指导建议 设计无锁的并发…

windwos10搭建我的世界服务器,并通过内网穿透实现联机游戏Minecraft

文章目录 1. Java环境搭建2.安装我的世界Minecraft服务3. 启动我的世界服务4.局域网测试连接我的世界服务器5. 安装cpolar内网穿透6. 创建隧道映射内网端口7. 测试公网远程联机8. 配置固定TCP端口地址8.1 保留一个固定tcp地址8.2 配置固定tcp地址 9. 使用固定公网地址远程联机 …