寻宝游戏 - 利用iBeacon特性设计的iOS线下寻宝游戏 - 物联网小游戏

作者简介

科科香,程序员
方向:IoT,方案集成,喜好各种新鲜东东
转载请注明出处

iBeacon简介

iBeacon(下面简称Beacon)是Apple在2013年9月发布的基于iOS7(及以上)的新功能。其工作方式是,配备有低功耗蓝牙(BLE)通信功能的使用BLE技术向周围发送自己特有的ID,接收到该ID的应用软件会根据该ID采取一些行动。

图0

WWDC 14 之后,对 iBeacon 加大了基数支持和对其用于室内地图的应用有个更明确的规划。苹果公司公布了 iBeacon for DevelopersMaps for Developers等专题页面。

想要了解iBeacon的更多信息,见维基百科

游戏简介

将iBeacon设备作为需要寻找的宝物,隐藏于各个地方,玩家需要安装上开发的app,带着手机找寻宝物,集齐宝物,获取奖励,游戏演示视频

Here We Go!

介绍完了背景,进入正题,如何制作这款游戏?

首先,你需要购入Beacon设备(这个游戏是寻找龙珠主题,所以买了7个,什么牌子不说了,有广告嫌疑)。淘宝上可以搜到很多Beacon卖家。

Beacon准备好之后,开始开发,这款游戏只做了iOS版本。

创建项目

在Xcode中创建项目,选择Simple View项目。输入名称dragonball-safari和组织名称。


图1

导入CoreLocation.framework。


�图2

创建好项目并添加完库之后,需要删除ViewController.h和ViewController.m两个文件。并且添加几个新的项目文件:

  • TabBarViewController.h
  • TabBarViewController.m
  • SafariViewController.h
  • SafariViewController.m
  • PickerViewController.h
  • PickerViewController.m

整个项目结构如下:


�图3

在模拟器上运行项目,效果如下。因为现在没有添加任何的视图元素,所以是一个黑屏。

界面

本文主要介绍如何应用iBeacon技术,界面工作在此不做介绍。下图是最后需要展示的界面。


图4

图5

上述的所有源代码在此下载

寻找龙珠

下载好上述的项目工程,并打开项目运行,你会得到上面展示的界面。下面,我们开始添加iBeacon的部分,让整个游戏开始正常运行。

开始之前,有几个地方先说明一下。

硬件准备

我用的是BrightBeacon的产品(好吧,还是说了厂家,不过选择其他厂家产品也没有影响,Beacon的接口和属性都是通用的,这是因为Apple做了规范),一共买了7个,需要把7个Beacon的Minor值设置成从1到7,分别代表七颗龙珠。否则无法进行游戏。

SDK

因为用的BrightBeacon的产品,所以直接用的他们提供的SDK

下载并将他的SDK和include文件引入项目工程中,如下图。


图6

准备工作做完之后,下面开始进行编码。

打开AppDelegate.m文件,引入BRTBeaconSDK.h文件和UserDefaultTool.h文件。自定义一个用于SDK的App Key。这个key需要在BrightBeacon的官方网站上申请。

#import "BRTBeaconSDK.h"
#import "UserDefaultTool.h"

之后注册app,并进行初始化工作。UserDefaultTool这个文件是预先写好的,用于控制龙珠的查找和初始化工作。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 
    [BRTBeaconSDK registerApp:BRT_SDK_KEY onCompletion:^(NSError *error) {
        NSLog(@"%@", error.description);
    }];
    
    [UserDefaultTool initUserDefaultData];

    return YES;
}

完成这一步之后,运行一下。在模拟器中,App会告知用户需要访问用户的地理位置信息,如果没有打开蓝牙的话,也会提示用户打开蓝牙。是不是很有意思:)


图7

注册好应用之后,下面开始监测Beacon吧。

在SafariViewController.m里面,依次加入这些方法。

- (void)startToFindBeacons{
    [BRTBeaconSDK startRangingWithUuids:@[[[NSUUID alloc] initWithUUIDString:DEFAULT_UUID]] onCompletion:^(NSArray *beacons, BRTBeaconRegion *region, NSError *error){
        if (!error) {
            [self reloadData:beacons];
        }
    }];
}

- (void)reloadData:(NSArray *)beacons{
    self.beacons = beacons;
    [self.dragonBallTableView reloadData];
    [self setTopViewData];
    [self checkFoundBall];
}

// 监控选择龙珠的距离
-(void)setTopViewData
{
    self.topViewBeacon = nil;
    
    if (self.beacons.count > 0) {
        for (BRTBeacon * beacon in self.beacons) {
            if ([beacon.minor longValue] == self.nowRow + 1) {
                self.topViewBeacon = beacon;
            }
        }
        
    }
    [self setTopViewDistance];
}

- (void)setTopViewDistance{
    if (self.topViewBeacon == nil) {
        self.distanceLaber.text = @"?  ?  ?";
    }else
    {
        self.distanceLaber.text = ([self.topViewBeacon.distance floatValue] > 10) ? @">10m" : [NSString stringWithFormat:@"%.1f m",[self.topViewBeacon.distance floatValue]];
    }
}

// 找到龙珠
-(void)checkFoundBall
{
    for (BRTBeacon* beacon in self.beacons) {
        if ([beacon.distance floatValue] < 0.3) {
            NSMutableArray* array = [UserDefaultTool nowFoundBallsMutableArray];
            
            if ([[array objectAtIndex:[beacon.minor intValue] - 1] isEqual:@(0)]) {
                [array setObject:@(1) atIndexedSubscript:[beacon.minor intValue] - 1];
                [UserDefaultTool setUserDefaultDataWithArray:array];
                [self AfterFindABallCallBack:[beacon.minor intValue]];
            }
        }
    }
}

-(void)AfterFindABallCallBack:(int)minor
{
    if ([UserDefaultTool nowFoundBallsCount] == 7) {
        //召唤神龙
//        [self callTheDragon];
    }else{
        UIAlertView* alertView = [[UIAlertView alloc]initWithTitle:@"恭喜你" message:[NSString stringWithFormat:@"找到%d星球啦",minor] delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        [alertView show];
    }
}

并在viewDidLoad中调用startToFindBeacons:

- (void)viewDidLoad {
    …
    // Start monitoring and ranging
    [self startToFindBeacons];
    [self setUpAudioPlay];
}

在DragonBallTableViewCell.m里面加入方法:

- (void)setBeacon:(BRTBeacon *)beacon{
    [self setDistanceByBeacon:(BRTBeacon *)beacon];
}

- (void)setDistanceByBeacon:(BRTBeacon *)beacon{
    
    NSArray* array = [UserDefaultTool nowFoundBallsArray];
    
    if ([[array objectAtIndex:_rowNum-1] isEqual:@(1)]) {
        //这个beacon已经找到过
        [self setDistanceLabelWithText:@"囊中之物" andColor:[UIColor greenColor]];
    }else{
        //没找到过这个beacon
        if(beacon == nil)
        {
            [self setDistanceLabelWithText:@"千里之外" andColor:[UIColor grayColor]];
        }else{
            if ([beacon.distance floatValue] < 2) {
                [self setDistanceLabelWithText:@"一步之遥" andColor:kNearTextColor];
                [self thereIsANearBeacon];
            }else if([beacon.distance floatValue] < 6){
                [self setDistanceLabelWithText:@"十步之遥" andColor:kNearTextColor];
            }else{
                [self setDistanceLabelWithText:@"百步之遥" andColor:kNearTextColor];
            }
        }
    }
}

-(void)thereIsANearBeacon
{
    if (!self.isShocked) {
        //通知代理
        if ([self.delegate respondsToSelector:@selector(CellDidCheckCloseToABall)]) {
            [self.delegate CellDidCheckCloseToABall];
        }
        self.isShocked = 1;
    }
}

完成在真机上运行,就可以看到龙珠距离我们的位置并且可以找到它啦:)当拿着一个Beacon靠近手机的时候,就会有下面的显示了。看,游戏的雏形已经出来了。

图8

说明:这块最关键的核心就是通过下面方法,来监测Beacon,根据找到Beacon的距离信息,来判断是否找到龙珠。这个方法默认是每秒回调一次。

[BRTBeaconSDK startRangingWithUuids: onCompletion:]

找到了龙珠之后,我们要在神龙碎片那里可以看到神龙的真面目。在PickerViewController.m中,添加方法:

- (void)setUpCurrentDragonFragment{
    self.dragonFragImages = @[_dragonFragImage1,
                              _dragonFragImage2,
                              _dragonFragImage3,
                              _dragonFragImage4,
                              _dragonFragImage5,
                              _dragonFragImage6,
                              _dragonFragImage7
                              ];
    NSArray* array = [UserDefaultTool nowFoundBallsArray];
    for (int i = 0 ; i < array.count; i++) {
        NSNumber* isFound = [array objectAtIndex:i];
        UIImageView* dragonView = self.dragonFragImages[i];
        if ([isFound isEqual:@(1)]) {
            dragonView.hidden = NO;
        }
    }
}

并在viewDidLoad中调用:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setUpCurrentDragonFragment];
}

这样,在神龙碎片中就可以看到找到了多少碎片了。

图9

完成游戏

游戏设定在最后,找到所有龙珠之后,神龙出现。现在让我们完成最后一部分吧。

创建DragonMovieViewController文件,召唤神龙。

图10
#import "DragonMovieViewController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface DragonMovieViewController ()

@property (nonatomic, strong) MPMoviePlayerController *movie;

@end

@implementation DragonMovieViewController

- (void)viewDidLoad {
    
    self.view.backgroundColor = [UIColor whiteColor];
    [self setUpMoviePlay];
}

-(void)setUpMoviePlay
{
    //找到文件路径
    NSString* path = [[NSBundle mainBundle]pathForResource:@"dragon_appear" ofType:@"MP4"];
    NSURL* url = [NSURL fileURLWithPath:path];
    
    //
    MPMoviePlayerController* movC = [[MPMoviePlayerController alloc]initWithContentURL:url];
    movC.controlStyle = MPMovieControlStyleNone;
    movC.scalingMode = MPMovieScalingModeAspectFill;
    movC.view.frame=self.view.bounds;
    [self.view addSubview:movC.view];
    self.movie = movC;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dragonMovieFinished:)
                                                 name:MPMoviePlayerPlaybackDidFinishNotification
                                               object:movC];
    [movC play];
    
    
}

-(void)dragonMovieFinished:(NSNotification *)notify
{
    //视频播放对象
    MPMoviePlayerController* theMovie = [notify object];
    //销毁播放通知
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:MPMoviePlayerPlaybackDidFinishNotification
                                                  object:theMovie];
    [theMovie.view removeFromSuperview];
    
    [self dismissViewControllerAnimated:YES completion:nil];
    
    UIAlertView* alertView = [[UIAlertView alloc]initWithTitle:@"恭喜恭喜恭喜你" message:@"你已经成功召唤神龙!" delegate:nil cancelButtonTitle:@"噢耶" otherButtonTitles:nil, nil];
    
    [alertView show];
}

召唤神龙的地方准备就绪,回到SafariViewController.m文件,引入DragonMovieViewController,并在最后加上方法:

-(void)callTheDragon
{
    DragonMovieViewController* dC = [[DragonMovieViewController alloc]init];
    [self presentViewController:dC animated:YES completion:nil];
}

在AfterFindABallCallBack里面,召唤神龙:

-(void)AfterFindABallCallBack:(int)minor
{
    if ([UserDefaultTool nowFoundBallsCount] == 7) {
        //召唤神龙
        [self callTheDragon];
    }else{
        UIAlertView* alertView = [[UIAlertView alloc]initWithTitle:@"恭喜你" message:[NSString stringWithFormat:@"找到%d星球啦",minor] delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil, nil];
        [alertView show];
    }
}

这样整个游戏就完成了:)
赶快开始游戏吧,看看谁是召唤神龙的第一人!

图11

源代码

整个项目的源代码上传到了我的github里面,欢迎大家下载。

结语

这里的Beacon游戏只是物联网应用的一个初步尝试,还有很多应用有待大家深挖和细挖。如果你有更好的建议,也请给我留言。因为是个人力量,不保证响应速度但保证响应质量:)

下一篇文章准备写关于物联网的一个实际应用小demo。

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

推荐阅读更多精彩内容

  • @end 与之前一样,你需要初始化位置管理器并设置它们的 delegate 。 在 application:did...
    LiWeiJ阅读 2,121评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,424评论 25 707
  • 作者:Jakub Krzych翻译:胡玮原文:beekn 译文仅供个人学习,不用于任何形式商业目的,转载请注明原作...
    whuiscool阅读 4,256评论 3 22
  • 最近,老听见舍友充满惊恐的抱怨“啊!我同学都有男朋友了!我是不是真的没人要啊!我嫁不出去了!”一开始,我也只是当笑...
    叫我婉桃阅读 757评论 2 6
  • 束缚是爱的教条 却囚禁着你自由的梦 你努力的蹿逃 像渴望高飞的笼中鸟 拼命的挣脱关住你生死的铁牢 , 抹杀不是爱的...
    草芥人阅读 244评论 2 1