iOS Unity 交互(系列四)Unity调用iOS SDK

前言:

这次是iOS和Unity交互。过程没有预想的那么顺利,也踩了一些坑,做个笔记。

要做的事情就是实现 iOS 和 Unity 交互,互相调用函数,传值。

传值系列

iOS Unity 交互(系列一)Unity调用iOS函数 Unity给iOS传值
iOS Unity 交互(系列二)iOS调用Unity函数 iOS给Unity传值
iOS Unity 交互(系列三)iOS Unity互传参数与完整示例代码
iOS Unity 交互(系列四)Unity调用iOS SDK

需要的工具

1、苹果电脑安装:Xcode,Unity,VSCode,开发工具安装最新的就行。

2、苹果手机真机,用于调试。

实现场景

A公司给我们提供了他们自己生产的蓝牙设备,并且提供了iOS SDK,我们把他们提供的iOS SDK放入自己的Xcode项目里面,调用SDK,就能连接他们的蓝牙设备,并持续获取蓝牙设备发送过来的值。

现在,要把 “连接蓝牙设备 - 获取设备值” 这套逻辑放到Unity项目里,从Unity里面生成Xcode项目,然后把这个Xcode项目运行到手机上。

所以,要做的事情就是,写代码暴露A公司的SDK,以供Unity项目使用,我们充当一个桥接者的角色。

操作流程

在前3篇文章里逐步讲解了 iOS 和 Unity是如何交互,互相发送消息,以及工程的创建,调试思路。本篇在此基础之上,直接写代码,不做其他细节讲述。

1、创建一个Unity项目,拖入一个物体,然后写个脚本挂载到物体上。
2、创建一个Xcode项目,导入A公司SDK,自己代码调试蓝牙成功之后,再把你的代码 改写到一个.mm文件里面,备用。
3、把A公司SDK和你的.mm文件一起放到Unity项目的 Asset/Plugins/iOS 目录下面。
4、生成Xcode项目,运行。

第1步

\color{red}{注意:} 这一步使用到了上篇文章里的工程。如果不清楚项目是怎么来的,看一下上一篇 iOS Unity 交互(系列一)Unity调用iOS函数 Unity给iOS传值

图片中的完整代码在文章末尾


1.png

第2步 写代码

2.png

图片中的示例代码:

C# 脚本代码 jiaoben.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;

public class Jiaoben : MonoBehaviour
{
    //定义函数 初始化
    [DllImport("__Internal")]
    private static extern void CadenceInitWith(string name);
    //定义函数 开始连接
    [DllImport("__Internal")]
    private static extern void CadenceConnectWith(string name);
    //定义函数 断开连接
    [DllImport("__Internal")]
    private static extern void CadenceDisconnect();
    //定义函数 销毁OC对象
    [DllImport("__Internal")]
    private static extern void CadenceDestory();

    void Start(){}

    void Update(){}

    void OnGUI() {
        if (GUI.Button(new Rect(150, 200, 300, 150), "点击初始化")) {
            CadenceInitWith("YourLanYaObject"); //传入挂载的游戏物体名字
        }

        if (GUI.Button(new Rect(150, 400, 300, 150), "点击开始连接蓝牙")) {
            //#if UNITY_IPHONE || UNITY_IOS
            //连接蓝牙设备,这里为了方便,直接连接指定的蓝牙设备UUID.
            CadenceConnectWith("0C95B9DA-5033-C3D8-FE99-B176D93B70D7");
            //#else
            //#endif
        }

        if (GUI.Button(new Rect(150, 600, 300, 150), "点击断开蓝牙")) {
            CadenceDisconnect(); //断开连接
        }

        if (GUI.Button(new Rect(150, 800, 300, 150), "点击销毁物体")) {
            CadenceDestory(); //在物体销毁的时候 把OC对象也销毁
            GameObject.Destroy(this.gameObject, 0.0f);
        }
    }

    private void OnDestroy() {
        Debug.Log("C# 对象: " + this.gameObject + " 已经销毁");
    }
    //来自OC的函数
    public void CadenceDataCall(string data) {
        Debug.Log("C# 正在接受 踏频器蓝牙数据:" + data); //收到蓝牙设备发送过来的数据
    }
    //来自OC的函数
    public void CadenceDestoryCall(string obj){
        //OC对象销毁时发送过来的数据,实际这个函数不会调用,因为GameObject此时已经为空.
        Debug.Log("C# 对象: " + obj + " 已经销毁"); 
    }
}

OC .mm文件代码 Unity_iOS_Connect.mm

//
//  Unity_iOS_Connect.m
//
//
//  Created by 程序猿 on 2020/2/31.
//

#import <Foundation/Foundation.h>
#import <FitBleKit/FitBleKit.h>
#import <UIKit/UIKit.h>

@interface iOSCadencer : NSObject <FBKApiBsaeDataSource, FBKApiCadenceDelegate>
///初始化对象名字
@property (nonatomic, strong) NSString *object;
///设备
@property (nonatomic, strong) FBKApiCadence *cadenceApi;
///设备id
@property (nonatomic, strong) NSString *uuid;
///允许日志 默认开启
@property (nonatomic) BOOL logEnable;
///出错状态信息
@property (nonatomic, strong) NSDictionary *dicerror;

@end


@implementation iOSCadencer

- (void)dealloc {
    //代码到这里,Unity里面传过来的 'GameObject' 对象已经销毁, self.object打印为空, 即便是给Unity发消息,它也是收不到的.
    //UnitySendMessage([self.object UTF8String], "CadenceDestoryCall", [@"对象销毁" UTF8String]);
    NSLog(@"♻️♻️♻️♻️ iOS实例对象:iOSCadencer已经销毁, Unity对象:%@", self.object);
}

- (instancetype)initWithObject:(NSString *)object {
    if (self = [super init]) {
        _logEnable = YES;
        self.object = object;
        self.cadenceApi = [[FBKApiCadence alloc] init];
        self.cadenceApi.delegate = self;
        self.cadenceApi.dataSource = self;
        self.cadenceApi.whellDiameter = 1.6;
    }
    return self;
}

- (void)setLogEnable:(BOOL)logEnable {
    _logEnable = logEnable;
    if (logEnable) {
        NSLog(@"🌤🌤🌤 日志开启");
    } else {
        NSLog(@"🌤🌤🌤 日志关闭");
    }
}

- (void)lll:(NSString *)lll {
    if (self.logEnable) {
        NSLog(@"🌤🌤🌤 %@",lll);
    }
}

- (NSDictionary *)dicerror {
    if (!_dicerror) {
        _dicerror = @{
            @(DeviceBleClosed):@"未开启蓝牙",
            @(DeviceBleIsOpen):@"蓝牙可用",
            @(DeviceBleSearching):@"搜索中...",
            @(DeviceBleConnecting):@"连接中...",
            @(DeviceBleConnected):@"已连接",
            @(DeviceBleSynchronization):@"同步中...",
            @(DeviceBleSyncOver):@"同步完成",
            @(DeviceBleSyncFailed):@"同步失败",
            @(DeviceBleDisconnecting):@"断开中...",
            @(DeviceBleDisconneced):@"已断开",
            @(DeviceBleReconnect):@"重连中...",
        };
    }
    return _dicerror;
}


// 蓝牙连接状态
- (void)bleConnectStatus:(DeviceBleStatus)status andDevice:(id)bleDevice {
    //FBKApiCadence *cadenceApi = (FBKApiCadence *)bleDevice;
    
    if (status == DeviceBleIsOpen) { //在手机蓝牙开启情况下,自动去连接踏频器设备
        [self lll:@"手机蓝牙已经开启"];
        if (self.uuid) {
            [self lll:@"重新连接对方蓝牙中..."];
            [self.cadenceApi startConnectBleApi:self.uuid andIdType:DeviceIdUUID];
        } else {
            [self lll:@"对方蓝牙设备UUID 为空, 请传入 UUID !"];
        }
    } else {
        NSString *s = [NSString stringWithFormat:@"蓝牙连接状态更新, 设备状态码: %d, %@", status, self.dicerror[@(status)]];
        [self lll:s];
    }
}

// 踏频数据
- (void)getCadence:(double)cadence andDevice:(id)bleDevice {
    
    //打印日志
    NSString *s = [NSString stringWithFormat:@"获取到踏频器数据 cadence (times/min) : %.2f ",cadence];
    [self lll:s];
    
    //发送数据
    NSString *data = [NSString stringWithFormat:@"%f",cadence];
    UnitySendMessage([self.object UTF8String], "CadenceDataCall", [data UTF8String]);
}

// 蓝牙连接错误信息
- (void)bleConnectError:(id)error andDevice:(id)bleDevice {
    NSString *e = [NSString stringWithFormat:@"蓝牙连接错误! \n原因:%@ \n设备%@", error, bleDevice];
    [self lll:e];
}

// 获取电量
- (void)devicePower:(NSString *)power andDevice:(id)bleDevice{}
// 获取固件版本号
- (void)deviceFirmware:(NSString *)version andDevice:(id)bleDevice{}
// 获取硬件版本号
- (void)deviceHardware:(NSString *)version andDevice:(id)bleDevice{}
// 获取软件版本号
- (void)deviceSoftware:(NSString *)version andDevice:(id)bleDevice{}
// 获取私有版本号
- (void)privateVersion:(NSDictionary *)versionMap andDevice:(id)bleDevice{}
// 获取私有MAC地址
- (void)privateMacAddress:(NSDictionary *)macMap andDevice:(id)bleDevice{}
// 速度距离数据
- (void)getSpeed:(double)speed withDistance:(double)distance andDevice:(id)bleDevice{}

@end


iOSCadencer *view = nil;

#ifdef __cplusplus
extern "C" {
#endif

///初始化
extern void CadenceInitWith(const char *objName) {
    NSString *s = [NSString stringWithUTF8String:objName];
    if (view == nil) {
        view = [[iOSCadencer alloc] initWithObject:s];
    }
}

///销毁
extern void CadenceDestory() {
    view.object = nil;
    view.cadenceApi = nil;
    view.uuid = nil;
    view = nil;
}

///开始连接踏频器
extern void CadenceConnectWith(const char *uuid) {
    NSString *s = [NSString stringWithUTF8String:uuid];
    [view lll:[NSString stringWithFormat:@"开始连接设备号: %@", s]];
    view.uuid = s;
    [view.cadenceApi startConnectBleApi:view.uuid andIdType:DeviceIdUUID];
}

///断开踏频器
extern void CadenceDisconnect() {
    [view.cadenceApi disconnectBleApi];
    [view lll:@"断开蓝牙连接"];
}

///是否打印日志
extern void CadenceLogEnable(bool open) {
    if (view == nil) {
        NSLog(@"先调用 CadenceInitWith 方法进行初始化!");
    } else {
        view.logEnable = open;
    }
}

#ifdef __cplusplus
}
#endif

结语

终究是有一天碰上了Unity。 这次iOS和Unity交互也花了点时间,网上的其他玩家写的文章猛一看让我有点摸不着头脑,还得是自己去试,最终发现,玩来玩去,就两个文件,一个脚本.cs 一个iOS的 .mm。

感谢以下iOS/Unity玩家的文章:
Unity3D与iOS的交互

<iOS和Unity交互>之参数传递
Unity与iOS交互
unity 与oc交互
Unity平台调用IOS
Unity和OC简单交互(方法互调)
Unity-IOS交互整理
iOS与Unity交互笔记之参数传递
iOS 和 Unity之间参数传递的方法
iOS与Unity交互笔记之参数传递
iOS和Unity交互之参数传递
Unity3d与iOS交互开发—接入平台SDK必备技能
iOS 和 Unity 交互之参数传递

报错解决:
malloc: *** error for object 0x1018ad6a0: pointer being freed was not allocated

Unity 调用oc报错:malloc: *** error for object 0x1ecc0eb0: pointer being freed was not allocated
malloc: *** error for object 0x1ecc0eb0: pointer being freed was not allocated
Unity3D中C#调用iOS的静态库(*.a)
pointer being freed was not allocated

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容