// // IOSPlatformSDK.m // OYGameSKD // // Created by duowan123 on 2021/1/21. // Copyright © 2021 jiketeam. All rights reserved. #import "IOSPlatformSDK.h" #import "SearchDeviceViewController.h" #import "PopupView.h" #import "HTTPDataProcession.h" #import "UserFriendsModel.h" #import "BTDataProcess.h" #import "AlgorithmTool.h" #pragma mark --- ios call unity C语言指针start /** * left: 左鞋动作代码 * right: 右鞋动作代码 * 对应 enum CMD_MOTION */ typedef void (* MotionHandler) (int cusid, int left, int right);//运动信息 /** * 踏步状态,频率 * leftStatus: 左鞋(0:停止, 1:慢,2:快) * rightStatus: 右鞋(0:停止, 1:慢,2:快) * leftFrag: 左鞋(每分钟步频) * rightFrag: 右鞋(每分钟步频) */ typedef void (* StepHandler)(int cusid, int leftStatus, int rightStatus, int leftFrag, int rightFrag);//步频 /** * 返回蓝牙设备的基本信息,用于界面上显示当前的设备状况 * * name: 设备名称 * status: 设备状态 @see enum BLE_STATE * electricity: 设备电量 取值范围[0 - 100] 对比左右鞋 handler较低的电量 */ typedef void (* DeviceHandler)(int cusid, const char *name, const char *address, int status, int electricity);//设备信息 app 跳转的时候传过来的 /** * 调用api GetUserFriends() 的回调 * code: 请求结果代码 0: 成功, 非0: 根据业务处理 * json: 好友列表数据 , json 数组 * */ typedef void (* UserFriendsHandler)(int code, const char * json);//好友列表 unity call ios 异步请求数据后 ios call unity /** * 调用api GetUserFriends() 的回调 * code: 请求结果代码 0: 成功, 非0: 根据业务处理 * json: 好友列表数据 , json 数组 */ typedef void (* InviteFriendHandler)(int code, char * userJson, char * inviteInfo);// /** *游戏首页交互动作的回调 * cusid: 主副设备 * code: 交互代码 */ typedef void (* ActionHandler) (int cusid, int code); ActionHandler actionHandler; MotionHandler motionHandler; StepHandler stepHandler; DeviceHandler deviceHandler; UserFriendsHandler userFriendsHandler; InviteFriendHandler inviteFriendHandler; #pragma mark --- ios call unity C语言指针end @interface IOSPlatformSDK () //app跳转 用户信息 #define IOSSDK_USERINFO @"iosSdk_userInfo" //缓存的步频数据 #define IOSSDK_BLESDKDATA @"iosSdk_bleSdk_frequency" //custom System UserDefaults #define IOS_NSUSERDEFAULT [NSUserDefaults standardUserDefaults] #define CUS_NOTIFICATIONCENTER [NSNotificationCenter defaultCenter] //计算游戏启动时间 @property(nonatomic,strong)NSDate * firstTime; //游戏持续时间 @property(nonatomic,assign)NSTimeInterval durationTime; @end @implementation IOSPlatformSDK #pragma mark ============================>> SDK 单例 static IOSPlatformSDK * instance; +(instancetype)sharedInstance{ return [[self alloc] init]; } + (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance; } - (instancetype)init{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super init]; [self initNotification]; }); return instance; } #pragma mark ============================>> private 各类通知处理事件 -(void)initNotification{ //监听各类通知action [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(gameStartInitData) name:@"gameStartInitData" object:nil]; [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(gameEndInitData:) name:@"gameEndInitData" object:nil]; [CUS_NOTIFICATIONCENTER addObserver:self selector:@selector(searchBLEAction:) name:@"searchBLEAction" object:nil]; //监听 app group 消息 ==>> 好友邀请信息 CFStringRef invite = (__bridge CFStringRef)@"INVITE_NOTIFICATION"; CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, inviteFriends, invite, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); } //趣动app:广播的通知回调 void inviteFriends (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo){ //group.com.Oujia.AppAndGame 沙河数据 NSUserDefaults* userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.Oujia.AppAndGame"]; NSString * jsonString = [userDefault objectForKey:@"invite"]; NSLog(@"监听到好友邀请信息 invite jsonString = %@",jsonString); NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *invite = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]; // NSDictionary * // inviteFriendHandler(); // [PopupView showCusHUDA:@"获取到用户信息失败,请从趣动App重新启动"]; } //通知开始游戏 -(void)gameStartInitData{ NSLog(@"unity call ios 通知开始游戏 --> IOSPlatformSDK"); if ([self existUserInfo]){ self.firstTime = [NSDate date]; } } //通知结束游戏 -(void)gameEndInitData:(NSNotification*)notification{ NSLog(@"unity call ios 通知结束游戏 --> IOSPlatformSDK"); } //通知弹出蓝牙搜索框 -(void)searchBLEAction:(NSNotification*)notification{ NSLog(@"unity call ios 通知游戏弹窗 --> IOSPlatformSDK"); if ([instance existUserInfo]==NO){ return; } /***************************初始化蓝牙数据处理工具***************************/ BTDataProcess* bleInstance = [BTDataProcess sharedInstance]; //主副设备类型 NSDictionary * notificationDict = notification.userInfo; NSNumber * typeNumber = [notificationDict objectForKey:@"type"]; int type = [typeNumber intValue]; bleInstance.deviceType = (DEVICE_TYPE)type; //游戏类型id NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; NSString * game_id = jsonDict[@"game_id"]; bleInstance.game_id = [game_id intValue]; bleInstance.macAddress = @""; NSLog(@"blueTootTool == %p type == %ld game_id = %d mac = %@",bleInstance,(long)bleInstance.deviceType,bleInstance.game_id,bleInstance.macAddress); //初始化蓝牙CBCentralManager [bleInstance initCBCentralManager]; //缓存动作数据 bleInstance.allMotionCountBLock = ^(int jump_count, int crouch_count, int step_count){ // NSLog(@"jump_count = %d crouch_count = %d step_count = %d ",jump_count,crouch_count,step_count); [self cacheAllMotionCount:jump_count crouchCount:crouch_count stepCount:step_count]; }; /***************************搜索蓝牙外设弹窗***************************/ SearchDeviceViewController * searchVC = [SearchDeviceViewController new]; [self addTOwindow:searchVC]; //searchVC搜索蓝牙Button searchVC.searchDeviceTask = ^{ [bleInstance initCBCentralManager]; }; //BTDataProces搜索到的蓝牙外设-->searchVC bleInstance.deviceArrBLock = ^(NSMutableArray * arr){ //蓝牙数据回调 刷新popView数据 searchVC.deviceArray = arr; [searchVC.tableView reloadData]; [searchVC hideAnimation]; }; //点击searchVC的tableViewCell链接选中的蓝牙 searchVC.connectDeviceBlock = ^(CBPeripheral * peripheral){ bleInstance.deviceType = (DEVICE_TYPE)type; [bleInstance connectPeripheral:peripheral deviceType: bleInstance.deviceType]; }; } #pragma mark ============================>> private 全局弹窗 -(void)addTOwindow:(UIViewController*)searchVC{ UIViewController *rootVC = [[UIApplication sharedApplication].delegate window].rootViewController; UIViewController *parent = rootVC; while ((parent = rootVC.presentedViewController) != nil ){ rootVC = parent; } while ([rootVC isKindOfClass:[UINavigationController class]]){ rootVC = [(UINavigationController *)rootVC topViewController]; } [rootVC addChildViewController:searchVC]; [rootVC.view addSubview:searchVC.view]; } #pragma mark ============================>> private 缓存处理 //是否已经缓存到用户信息 -(BOOL)existUserInfo{ NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; if (jsonDict==nil){ [PopupView showCusHUDA:@"获取到用户信息失败,请从趣动App重新启动"]; return NO; }else{ return YES; } } //玩游戏过程中缓存 动作数据 和时间戳 -(void)cacheAllMotionCount:(int)jump_count crouchCount:(int)crouch_count stepCount:(int)step_count{ //游戏持续时间 self.durationTime = [[NSDate date] timeIntervalSinceDate:self.firstTime];//游戏总共经历的时长 秒数 //缓存pilst文件 NSMutableDictionary * bleDict = [NSMutableDictionary new]; [bleDict setObject:[NSNumber numberWithInt:self.durationTime] forKey:@"duration"]; [bleDict setObject:[NSNumber numberWithInt:jump_count] forKey:@"jump"]; [bleDict setObject:[NSNumber numberWithInt:crouch_count] forKey:@"crouch"]; [bleDict setObject:[NSNumber numberWithInt:step_count] forKey:@"step"]; NSDictionary * dict = [NSDictionary dictionaryWithDictionary:bleDict]; //归档缓存 [IOS_NSUSERDEFAULT setObject:dict forKey:IOSSDK_BLESDKDATA]; [IOS_NSUSERDEFAULT synchronize]; } #pragma mark ============================>> public method //app跳转直链 -(void)startWithUrl:(NSURL*)url{ if (url==nil){ return; } NSString * urlString = [url absoluteString]; NSArray * stringArr = [urlString componentsSeparatedByString:@"//"]; NSString * arr2 = stringArr[1]; NSString* finsalString = [arr2 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // NSLog(@"转码的urlstring ========= %@",finsalString); NSDictionary * dict = [AlgorithmTool dictionaryWithJsonString:finsalString]; NSString * game_id = dict[@"game_id"]; NSLog(@"Schemes_Url转码的dict ========= %@",dict); if (dict!=nil){ //缓存用户信息数据 [IOS_NSUSERDEFAULT setObject:dict forKey:IOSSDK_USERINFO]; [IOS_NSUSERDEFAULT synchronize]; //根据传过来的 开始搜索并链接蓝牙 mac地址其实是服务号/特征号 BTDataProcess* bleInstance = [BTDataProcess sharedInstance]; bleInstance.deviceType = DEVICETYPE_MAIN; bleInstance.game_id = [game_id intValue]; bleInstance.macAddress = [dict objectForKey:@"mac"]; NSLog(@"blueTootTool == %p type == %ld game_id = %d mac = %@",bleInstance,(long)bleInstance.deviceType,bleInstance.game_id,bleInstance.macAddress); [bleInstance initCBCentralManager]; //数据存入plist bleInstance.allMotionCountBLock = ^(int jump_count, int crouch_count, int step_count){ // NSLog(@"jump_count = %d crouch_count = %d step_count = %d ",jump_count,crouch_count,step_count); [self cacheAllMotionCount:jump_count crouchCount:crouch_count stepCount:step_count]; }; } } /********************************* ios_sdk call unity start *********************************/ -(void)bridgingMotionAction:(int)cusid left:(int)left right:(int)right{ if (motionHandler!=nil){ // NSLog(@"bridging motionHandler 指针内存地址 ===>> %p",&motionHandler); motionHandler(cusid,left,right); } } -(void)bridgingStepAction:(int)cusid leftStatus:(int)leftStatus rightStatus:(int)rightStatus leftFrag:(int)leftFrag rightFrag:(int)rightFrag{ // NSLog(@"bridging stepHandler 指针内存地址 ===>> %p",&stepHandler); stepHandler(cusid,leftStatus,rightStatus,leftFrag,rightFrag); } -(void)bridgingDeviceAction:(int)cusid name:(NSString*)name address:(NSString*)address status:(int)status electricity:(int)electricity{ // NSLog(@"bridging deviceHandler 指针内存地址 ===>> %p",&deviceHandler); NSLog(@"deviceHandler ===>> %d %@ %@ %d %d",cusid,name,address,status,electricity); // const char * charName =[name UTF8String]; const char * charAddress =[address UTF8String]; deviceHandler(cusid,charName,charAddress,status,electricity); } -(void)bridgingFriensList:(int)code json:(NSString*)json{ } -(void)bridgingInviteFriend:(NSString*)userJson inviteInfo:(NSString*)inviteInfo{ } -(void)bridgingInteraction:(int)cusid code:(int)code{ actionHandler(cusid,code); } /********************************* ios_sdk call unity end *********************************/ @end #pragma mark ============================>> private 以下为C++和调用c代码 /** * c语言字符串指针malloc */ char* MakeStringCopy(const char* string){ if (string == NULL) return NULL; char* res = (char*)malloc(strlen(string) + 1); strcpy(res, string); return res; } /** * 主动接收c++函数指针 */ extern "C" {void PointerMotionHandler(MotionHandler handler){ motionHandler = handler; // NSLog(@"PutIntPtr===>>motionHandler"); NSLog(@"PutIntPtr MotionHandler 指针内存地址 ===>> %p",&motionHandler); } } extern "C" { void PointerStepHandler(StepHandler handler){ stepHandler = handler; NSLog(@"PutIntPtr StepHandler 指针内存地址 ===>> %p",&stepHandler); } } extern "C" {void PointerDeviceHandler(DeviceHandler handler){ deviceHandler = handler; NSLog(@"PutIntPtr DeviceHandler 指针内存地址 ===>> %p",&deviceHandler); } } extern "C" {void PointerUserFriendsHandler(UserFriendsHandler handler){ userFriendsHandler = handler; NSLog(@"PutIntPtr UserFsHandler 指针内存地址 ===>> %p",&userFriendsHandler); } } extern "C" {void PointerInviteFriendHandler(InviteFriendHandler handler){ inviteFriendHandler = handler; NSLog(@"PutIntPtr UserFsHandler 指针内存地址 ===>> %p",&inviteFriendHandler); } } extern "C" {void PointerActionHandler(ActionHandler handler){ actionHandler = handler; NSLog(@"PutIntPtr UserFsHandler 指针内存地址 ===>> %p",&actionHandler); } } /** * 游戏开始 */ extern "C" { void GameStart(){ [CUS_NOTIFICATIONCENTER postNotificationName:@"gameStartInitData" object:nil]; } } /** * 游戏结束 * level: level * score: score * record: record * mode: mode * opponentId: opponentId */ extern "C" { void GameEnd(int level, double score, int record, int mode, int opponentId){ [CUS_NOTIFICATIONCENTER postNotificationName:@"gameEndInitData" object:nil]; if ([instance existUserInfo]==NO){ return; } NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; NSString * token = [jsonDict objectForKey:@"token"]; NSMutableDictionary * sportRecord = [NSMutableDictionary new]; //预留字段 NSString * game_id = jsonDict[@"game_id"]; [sportRecord setObject:game_id forKey:@"game_id"];//游戏类型 [sportRecord setObject:@0 forKey:@"consume"];//卡路里默认0 [sportRecord setObject:@0 forKey:@"screen"];//默认0 [sportRecord setObject:@0 forKey:@"distance"];//废弃 [sportRecord setObject:@0 forKey:@"is_cancel"];//是否正常停止游戏 正常0 不正常1 //unity回调数据 // NSLog(@"游戏结束===>> level = %d score = %f record = %d model = %d opponentid = %d",level,score,record,mode,opponentId);//上传数据给服务器 [sportRecord setObject:[NSNumber numberWithInt:level] forKey:@"level"]; [sportRecord setObject:[NSNumber numberWithDouble:score] forKey:@"score"]; [sportRecord setObject:[NSNumber numberWithInt:record] forKey:@"record"]; [sportRecord setObject:[NSNumber numberWithInt:mode] forKey:@"mode"]; [sportRecord setObject:[NSNumber numberWithInt:opponentId] forKey:@"opponentId"]; //从缓存取出frequencyDicr数据 NSDictionary * frequencyDicr = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_BLESDKDATA]; if (frequencyDicr!=nil){ [sportRecord addEntriesFromDictionary:frequencyDicr]; }else{ [sportRecord setObject:@0 forKey:@"jump"]; [sportRecord setObject:@0 forKey:@"crouch"]; [sportRecord setObject:@0 forKey:@"step"]; [sportRecord setObject:@0 forKey:@"duration"]; } NSLog(@"sportRecord ===== %@",sportRecord); NSString * urlString = @"http://test-shoes-api.hiyd.com/gameRecord/addGame"; [HTTPDataProcession uploadHTTPDataWithUrlString:urlString withParams:sportRecord token:@"shoes:60878238702ee6.23331117vZi" success:^(id data) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; if (dic!=nil){ int code = [dic[@"code"] intValue]; if (code==0){// NSLog(@"请求成功 %@",dic); }else{ NSLog(@"请求错误 =========== >>> %@",dic[@"msg"]); } } }fail:^(id errorDes){ NSLog(@"请求失败 =========== >>> %@",errorDes); }]; } } /** * 搜索设备 * type: 设备ID */ extern "C" { void SearchDevice(int type){ NSLog(@"SearchDevice===>> %d",type); //unity 有两个search按钮 type 0是主设备 1是副设备 NSMutableDictionary * obj = [NSMutableDictionary new]; if (type==0){ [obj setObject:@0 forKey:@"type"]; }else if (type==1){ [obj setObject:@1 forKey:@"type"]; } NSDictionary * dict = [NSDictionary dictionaryWithDictionary:obj]; [CUS_NOTIFICATIONCENTER postNotificationName:@"searchBLEAction" object:nil userInfo:dict]; } } /** * 链接设备 * type: 设备ID */ extern "C" { void ConnectDevice(int type){ NSLog(@"ConnectDevice===>> %d",type); //unity 有两个connect按钮 type 0是第一个玩家 1是第二个玩家 } } /** * 震动 * type: 设备ID * duration: 时长 ms [100 .. 1000] */ extern "C" { void Vibrate(int type,int duration){ NSLog(@"vibrate==========================================>>"); //unity 有两个search按钮 type 0是主设备 1是副设备 NSMutableDictionary * obj = [NSMutableDictionary new]; if (type==0){ [obj setObject:@0 forKey:@"type"]; }else if (type==1){ [obj setObject:@1 forKey:@"type"]; } [obj setObject:@(duration) forKey:@"duration"]; NSDictionary * dict = [NSDictionary dictionaryWithDictionary:obj]; [CUS_NOTIFICATIONCENTER postNotificationName:@"vibrationAction" object:nil userInfo:dict]; } } /** * unity获取用户好友 -->> ios端根据用户token值请求http好友列表 ios将列表json的数组数据传给unity */ extern "C" { void GetUserFriends(){ if ([instance existUserInfo]==NO){ return; } NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; NSString * token = [jsonDict objectForKey:@"token"]; [HTTPDataProcession getHTTPDataProcession:@"shoes:6089115f625652.21959380F4K" success:^(id data){ // 网络访问成功 // NSLog(@"data=%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); NSError * error; NSDictionary *dictionary =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error]; // NSLog(@"gameFriends dictionary === %@ ",dictionary); if (!error){ int code = [dictionary[@"code"] intValue]; if (code==0){// NSDictionary * dict = dictionary[@"data"]; NSArray * arr = dict[@"list"]; NSMutableArray * userArr = [NSMutableArray new];//存储数据 for (NSDictionary * object in arr){ NSDictionary * socialInfo = object[@"socialInfo"]; // NSDictionary * dataDict = data[@"data"]; // NSString * token = dataDict[@"token"]; // NSLog(@"获取token值成功"); UserFriendsModel * model = [UserFriendsModel new]; model.code = 0; [model setValuesForKeysWithDictionary:socialInfo]; // NSLog(@"model =========== >>> %d %@ %@ %d %@ %@",model.trans_id,model.nickname,model.city,model.gender,model.status,model.avatar); NSMutableDictionary * gameFriends = [NSMutableDictionary new]; [gameFriends setValue:model.status forKey:@"status"]; NSMutableDictionary * userInfo = [NSMutableDictionary new]; [userInfo setObject:[NSNumber numberWithInt:model.trans_id] forKey:@"id"]; [userInfo setValue:model.nickname forKey:@"name"]; [userInfo setObject:[NSNumber numberWithInt:model.gender] forKey:@"gender"]; [userInfo setValue:model.city forKey:@"city"]; [userInfo setValue:model.avatar forKey:@"avatar"]; [gameFriends setValue:userInfo forKey:@"user"]; // NSLog(@"gameFriends =========== >>>%@",gameFriends); [userArr addObject:gameFriends]; } NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userArr options:NSJSONWritingPrettyPrinted error:nil]; NSString * jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSLog(@"iOS call unity ==>> gameFriends jsonString === %@ ",jsonString); const char * cusChar =[jsonString UTF8String]; userFriendsHandler(code,cusChar);//异步回调 } } }fail:^(id errorDes){ }]; } } /** * unity获取用户信息 -->> 解析从Spotr App的传过来的RL_Seme ios返回json数据给游戏 */ extern "C" { char * GetUserInfoJson(){ if ([instance existUserInfo]==NO){ return nil; }else{ NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; NSDictionary * user = jsonDict[@"user"]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:user options:NSJSONWritingPrettyPrinted error:nil]; NSString * userJsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; const char * cuschart =[userJsonString UTF8String]; NSLog(@"unity 获取用户信息 GetUserInfoJson ===>> %@",userJsonString); //要先copy 不然c#调用free的时候回闪退 return MakeStringCopy(cuschart); } } } /** * unity可以主动断开链接 * type: 设备类型 0: 主, 1: 副 */ extern "C" { void DisConnectDevice(int type){ NSLog(@"disConnectDevice===>>"); } } /** * 主动邀请好友 * friendid: 游戏好友ID */ extern "C" { void InviteFriend(int friendid,char * info){ NSLog(@"InviteFriend===>>"); } } /** * unity界面准备好后可以申请邀请信息,回调在inviteFriendHandler * friendid: 游戏好友ID */ extern "C" { char * GetInviteInfo(){ NSLog(@"getInviteInfo===>>"); if ([instance existUserInfo]==NO){ return nil; }else{ NSDictionary * jsonDict = [IOS_NSUSERDEFAULT objectForKey:IOSSDK_USERINFO]; NSDictionary * invite = jsonDict[@"invite"]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:invite options:NSJSONWritingPrettyPrinted error:nil]; NSString * inviteJsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; const char * cuschart =[inviteJsonString UTF8String]; NSLog(@"unity 获取用户信息 GetUserInfoJson ===>> %@",inviteJsonString); //要先copy 不然c#调用free的时候闪退 return MakeStringCopy(cuschart); } } } /** * 投屏 */ extern "C" { void ScreenProjection(){ NSLog(@"ScreenProjection===>>"); } }