尋寶游戲 - 利用iBeacon特性設(shè)計(jì)的iOS線下尋寶游戲 - 物聯(lián)網(wǎng)小游戲

作者簡(jiǎn)介

科科香,程序員
方向:IoT浓体,方案集成,喜好各種新鮮東東
轉(zhuǎn)載請(qǐng)注明出處

iBeacon簡(jiǎn)介

iBeacon(下面簡(jiǎn)稱Beacon)是Apple在2013年9月發(fā)布的基于iOS7(及以上)的新功能坝辫。其工作方式是,配備有低功耗藍(lán)牙(BLE)通信功能的使用BLE技術(shù)向周圍發(fā)送自己特有的ID弟翘,接收到該ID的應(yīng)用軟件會(huì)根據(jù)該ID采取一些行動(dòng)。

圖0

WWDC 14 之后骄酗,對(duì) iBeacon 加大了基數(shù)支持和對(duì)其用于室內(nèi)地圖的應(yīng)用有個(gè)更明確的規(guī)劃稀余。蘋果公司公布了 iBeacon for DevelopersMaps for Developers等專題頁面。

想要了解iBeacon的更多信息趋翻,見維基百科睛琳。

游戲簡(jiǎn)介

將iBeacon設(shè)備作為需要尋找的寶物,隱藏于各個(gè)地方踏烙,玩家需要安裝上開發(fā)的app师骗,帶著手機(jī)找尋寶物,集齊寶物讨惩,獲取獎(jiǎng)勵(lì)辟癌,游戲演示視頻

Here We Go!

介紹完了背景步脓,進(jìn)入正題愿待,如何制作這款游戲浩螺?

首先靴患,你需要購入Beacon設(shè)備(這個(gè)游戲是尋找龍珠主題,所以買了7個(gè)要出,什么牌子不說了鸳君,有廣告嫌疑)。淘寶上可以搜到很多Beacon賣家患蹂。

Beacon準(zhǔn)備好之后或颊,開始開發(fā),這款游戲只做了iOS版本传于。

創(chuàng)建項(xiàng)目

在Xcode中創(chuàng)建項(xiàng)目囱挑,選擇Simple View項(xiàng)目。輸入名稱dragonball-safari和組織名稱沼溜。


圖1

導(dǎo)入CoreLocation.framework平挑。


?圖2

創(chuàng)建好項(xiàng)目并添加完庫之后,需要?jiǎng)h除ViewController.h和ViewController.m兩個(gè)文件系草。并且添加幾個(gè)新的項(xiàng)目文件:

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

整個(gè)項(xiàng)目結(jié)構(gòu)如下:


?圖3

在模擬器上運(yùn)行項(xiàng)目通熄,效果如下。因?yàn)楝F(xiàn)在沒有添加任何的視圖元素找都,所以是一個(gè)黑屏唇辨。

界面

本文主要介紹如何應(yīng)用iBeacon技術(shù),界面工作在此不做介紹能耻。下圖是最后需要展示的界面赏枚。


圖4

圖5

上述的所有源代碼在此下載亡驰。

尋找龍珠

下載好上述的項(xiàng)目工程,并打開項(xiàng)目運(yùn)行饿幅,你會(huì)得到上面展示的界面隐解。下面,我們開始添加iBeacon的部分诫睬,讓整個(gè)游戲開始正常運(yùn)行煞茫。

開始之前,有幾個(gè)地方先說明一下摄凡。

硬件準(zhǔn)備

我用的是BrightBeacon的產(chǎn)品(好吧续徽,還是說了廠家,不過選擇其他廠家產(chǎn)品也沒有影響亲澡,Beacon的接口和屬性都是通用的钦扭,這是因?yàn)锳pple做了規(guī)范),一共買了7個(gè),需要把7個(gè)Beacon的Minor值設(shè)置成從1到7床绪,分別代表七顆龍珠客情。否則無法進(jìn)行游戲。

SDK

因?yàn)橛玫腂rightBeacon的產(chǎn)品癞己,所以直接用的他們提供的SDK膀斋。

下載并將他的SDK和include文件引入項(xiàng)目工程中,如下圖痹雅。


圖6

準(zhǔn)備工作做完之后仰担,下面開始進(jìn)行編碼。

打開AppDelegate.m文件绩社,引入BRTBeaconSDK.h文件和UserDefaultTool.h文件摔蓝。自定義一個(gè)用于SDK的App Key。這個(gè)key需要在BrightBeacon的官方網(wǎng)站上申請(qǐng)愉耙。

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

之后注冊(cè)app贮尉,并進(jìn)行初始化工作。UserDefaultTool這個(gè)文件是預(yù)先寫好的朴沿,用于控制龍珠的查找和初始化工作猜谚。

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

    return YES;
}

完成這一步之后,運(yùn)行一下悯仙。在模擬器中龄毡,App會(huì)告知用戶需要訪問用戶的地理位置信息,如果沒有打開藍(lán)牙的話锡垄,也會(huì)提示用戶打開藍(lán)牙沦零。是不是很有意思:)


圖7

注冊(cè)好應(yīng)用之后,下面開始監(jiān)測(cè)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];
}

// 監(jiān)控選擇龍珠的距離
-(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中調(diào)用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)]) {
        //這個(gè)beacon已經(jīng)找到過
        [self setDistanceLabelWithText:@"囊中之物" andColor:[UIColor greenColor]];
    }else{
        //沒找到過這個(gè)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;
    }
}

完成在真機(jī)上運(yùn)行,就可以看到龍珠距離我們的位置并且可以找到它啦:)當(dāng)拿著一個(gè)Beacon靠近手機(jī)的時(shí)候屯仗,就會(huì)有下面的顯示了搞坝。看魁袜,游戲的雛形已經(jīng)出來了桩撮。

圖8

說明:這塊最關(guān)鍵的核心就是通過下面方法,來監(jiān)測(cè)Beacon峰弹,根據(jù)找到Beacon的距離信息店量,來判斷是否找到龍珠。這個(gè)方法默認(rèn)是每秒回調(diào)一次鞠呈。

[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中調(diào)用:

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

這樣旱爆,在神龍碎片中就可以看到找到了多少碎片了。

圖9

完成游戲

游戲設(shè)定在最后窘茁,找到所有龍珠之后怀伦,神龍出現(xiàn)。現(xiàn)在讓我們完成最后一部分吧庙曙。

創(chuàng)建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
{
    //視頻播放對(duì)象
    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:@"你已經(jīng)成功召喚神龍!" delegate:nil cancelButtonTitle:@"噢耶" otherButtonTitles:nil, nil];
    
    [alertView show];
}

召喚神龍的地方準(zhǔn)備就緒捌朴,回到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];
    }
}

這樣整個(gè)游戲就完成了:)
趕快開始游戲吧,看看誰是召喚神龍的第一人署惯!

圖11

源代碼

整個(gè)項(xiàng)目的源代碼上傳到了我的github里面左驾,歡迎大家下載。

結(jié)語

這里的Beacon游戲只是物聯(lián)網(wǎng)應(yīng)用的一個(gè)初步嘗試极谊,還有很多應(yīng)用有待大家深挖和細(xì)挖诡右。如果你有更好的建議,也請(qǐng)給我留言轻猖。因?yàn)槭莻€(gè)人力量帆吻,不保證響應(yīng)速度但保證響應(yīng)質(zhì)量:)

下一篇文章準(zhǔn)備寫關(guān)于物聯(lián)網(wǎng)的一個(gè)實(shí)際應(yīng)用小demo。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咙边,一起剝皮案震驚了整個(gè)濱河市猜煮,隨后出現(xiàn)的幾起案子次员,更是在濱河造成了極大的恐慌,老刑警劉巖王带,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淑蔚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡愕撰,警方通過查閱死者的電腦和手機(jī)刹衫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搞挣,“玉大人绪妹,你說我怎么就攤上這事∈辆浚” “怎么了邮旷?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蝇摸。 經(jīng)常有香客問我婶肩,道長(zhǎng),這世上最難降的妖魔是什么貌夕? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任律歼,我火速辦了婚禮,結(jié)果婚禮上啡专,老公的妹妹穿的比我還像新娘险毁。我一直安慰自己,他們只是感情好们童,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布畔况。 她就那樣靜靜地躺著,像睡著了一般慧库。 火紅的嫁衣襯著肌膚如雪跷跪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天齐板,我揣著相機(jī)與錄音吵瞻,去河邊找鬼。 笑死甘磨,一個(gè)胖子當(dāng)著我的面吹牛橡羞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播济舆,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼卿泽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了吗冤?” 一聲冷哼從身側(cè)響起又厉,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤九府,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后覆致,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侄旬,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年煌妈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了儡羔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璧诵,死狀恐怖汰蜘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情之宿,我是刑警寧澤族操,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站比被,受9級(jí)特大地震影響色难,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜等缀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一枷莉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尺迂,春花似錦笤妙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至州疾,卻和暖如春辜限,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背严蓖。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氧急,地道東北人颗胡。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吩坝,于是被迫代替她去往敵國(guó)和親毒姨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • @end 與之前一樣钉寝,你需要初始化位置管理器并設(shè)置它們的 delegate 弧呐。 在 application:did...
    LiWeiJ閱讀 2,135評(píng)論 0 0
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,799評(píng)論 25 707
  • 作者:Jakub Krzych翻譯:胡瑋原文:beekn 譯文僅供個(gè)人學(xué)習(xí)闸迷,不用于任何形式商業(yè)目的,轉(zhuǎn)載請(qǐng)注明原作...
    whuiscool閱讀 4,261評(píng)論 3 22
  • 最近今阳,老聽見舍友充滿驚恐的抱怨“啊茅信!我同學(xué)都有男朋友了!我是不是真的沒人要啊!我嫁不出去了!”一開始盾舌,我也只是當(dāng)笑...
    叫我婉桃閱讀 759評(píng)論 2 6
  • 束縛是愛的教條 卻囚禁著你自由的夢(mèng) 你努力的躥逃 像渴望高飛的籠中鳥 拼命的掙脫關(guān)住你生死的鐵牢 , 抹殺不是愛的...
    草芥人閱讀 245評(píng)論 2 1