手把手教你如何實現(xiàn)iOS消消樂小游戲Demo

文章也同時在個人博客 http://kimihe.com/更新

引言

做消消樂Demo屬于一個意外,本想借助學(xué)習(xí)iOS游戲開發(fā)把CoreAnimation學(xué)好煌抒,并完成第一個游戲Demo:俄羅斯方塊。卻在這過程中發(fā)現(xiàn)了一些實現(xiàn)消消樂的小技巧袍榆,于是興起完成了這個小Demo您旁,供大家參考。

當(dāng)然荆永,這個Demo不是平白無故產(chǎn)生的,筆者也是參考了一些資料国章,其中就包括斯坦福大學(xué)的iOS公開課具钥,這里放上百度云的鏈接(含字幕):Dynamic Animation。視頻是用swift講的液兽,筆者從視頻中獲取了幫助和靈感骂删,大家英語好的話也可以嘗試學(xué)習(xí)一下。

本文將會講解如何實現(xiàn)這個消消樂小游戲四啰,相信你一定會有所收獲宁玫。

項目地址

Cubee Game
歡迎一切fork,issue柑晒,pull request來幫助該項目做得更好欧瘪。

效果演示

如下圖,和大多數(shù)消消樂一樣匙赞,Demo根據(jù)顏色恋追,進行垂直,水平以及兩個斜向的三消罚屋。用戶可以上下左右自由交換兩個方塊的位置。

消消樂Demo效果

基本思路

先講解一下基本思路嗅绸。主要分如下幾個部分:

  • 首先脾猛,大家可以看到這個消消樂需要一些動畫,以及一些諸如碰撞和重力下落等物理特性的支持鱼鸠。
  • 其次猛拴,我們需要能夠正確計算出三消羹铅,并以美觀的動畫樣式將其消除。
  • 接著愉昆,我們需要響應(yīng)用戶的移動方塊的操作职员,實現(xiàn)方塊位置的調(diào)換。
  • 最后跛溉,我們添加一些美化效果焊切。

物理特性及其對應(yīng)的動畫

很顯然,物理特性實現(xiàn)的好壞芳室,直接關(guān)系到消消樂游戲的體驗专肪。在Demo中筆者使用了UIDynamicAnimatorUIDynamicBehavior這兩個基于UIKit的類來進行管理。

通過UIDynamicAnimator來實現(xiàn)各種物理特性發(fā)生時的動畫堪侯,如下落加速動畫和碰撞反彈動畫嚎尤。而其中涉及的物理特性則使用UIDynamicBehavior

KMAnimatorManager

動畫管理器:KMAnimatorManager繼承自UIDynamicAnimator伍宦,用來管理各種物理特性對應(yīng)的動畫效果芽死。它會關(guān)聯(lián)到一個UIView,這個UIView是我們動畫展現(xiàn)的場所次洼,之后所有的物理特性和動畫顯示都在這個view上進行关贵。如下:

_animator = [[KMAnimatorManager alloc] initWithReferenceView:self];
_animator.delegate = self;

Demo中,我們所有的游戲場景都在KMGameView的實例_gameView中滓玖,上述代碼的self就是_gameView坪哄。而封裝好的_gameView就可以直接添加到任意ViewController了。如下:

_gameView = [[KMGameView alloc] initWithFrame:self.view.frame];
UIImage *background = [UIImage imageNamed:@"background"];
_gameView.contentMode = UIViewContentModeScaleAspectFill;
_gameView.layer.contents = (__bridge id _Nullable)(background.CGImage);
_gameView.delegate = self;
[self.view addSubview:_gameView];

KMCubeBehavior

通過自定義UIDynamicBehavior的子類KMCubeBehavior势篡,筆者向其中封裝了諸如重力翩肌,碰撞檢測,彈性系數(shù)禁悠,是否圍繞質(zhì)心旋轉(zhuǎn)等特性念祭。這可能需要你有一些相關(guān)物理學(xué)方面的基礎(chǔ)。但幸好Apple已經(jīng)做好了封裝碍侦,我們大可以放心地使用它提供的接口粱坤。如下:

- (instancetype)init
{
    self = [super init];
    
    [self addChildBehavior:self.gravity];
    [self addChildBehavior:self.collider];
    [self addChildBehavior:self.animationOptions];
    
    return self;
}

- (void)addItem:(id<UIDynamicItem>)item
{
    [self.gravity addItem:item];
    [self.collider addItem:item];
    [self.animationOptions addItem:item];
}

我們向KMCubeBehavior類中加入了所需的各種物理特性,使得之后基于此生成的每一個小方塊都有這些效果瓷产。如下:

_cubeBehavior = [[KMCubeBehavior alloc] init];
[_animator addBehavior:_cubeBehavior];

三消計算及消除動畫

消除的時機

在Demo中站玄,我們以隨機下落不同顏色方塊的形式來累積磚塊,供用戶調(diào)換位置來消除濒旦。因此株旷,需要在兩個情況下進行三消判斷。一個是在方塊下落動畫結(jié)束后,一個是在用戶執(zhí)行完調(diào)換操作晾剖。

對于前者我們可以利用<UIDynamicAnimatorDelegate>中的接口- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator锉矢。每當(dāng)物理動畫執(zhí)行完畢,我們都可以進入該方法齿尽,在其中執(zhí)行我們的三消計算沽损。

消除的計算

整個的計算,我們會多次調(diào)用- (NSArray *)checkCrossAt:(KMDropView *)centerView方法循头。該方法類似于一個掃描绵估,傳入一個方塊視圖,然后執(zhí)行四個方向的掃描贷岸,發(fā)現(xiàn)可以三消的方塊后壹士,將它們進行標(biāo)記,最后以數(shù)組統(tǒng)一返回偿警。供外部程序進行消除躏救。

這其中涉及到如何識別方塊是否屬于同一個類型。雖然Demo是通過顏色區(qū)分螟蒸,但在更多實際場景中盒使,我們可以加載各種圖片,比如各種顏色的糖果點心等七嫌。因此對于視圖中所有小方塊少办,筆者讓它們繼承于自定義的KMDropView類,其中封裝了方塊所需的各種屬性诵原,詳細(xì)內(nèi)容我們放到下一小節(jié)講英妓。

這里,你需要知道绍赛,我們通過任一方塊的type屬性來進行識別蔓纠,type是一個字符串,其內(nèi)容會在方塊創(chuàng)建時進行賦值吗蚌,不同類型的方塊有不同的type腿倚。用戶看到的僅僅是視圖樣式,背后真正的匹配可以和視圖樣式完全獨立蚯妇。例如敷燎,我們把匹配三消的方塊加入消除數(shù)組中:

NSString *centerColor = centerView.type;
NSString *leftColor = (leftView)?leftView.type : @"#$%^&*";
NSString *rightColor = (rightView)?rightView.type : @"#$%^&*";
    ...
    ...   
NSMutableArray *totalArr = [NSMutableArray new];
if ([centerColor isEqualToString:leftColor] && [centerColor isEqualToString:rightColor]) {
    NSArray *arr = [NSArray arrayWithObjects:leftView, centerView, rightView, nil];
    [totalArr addObjectsFromArray:arr];
}

因此,整個的三消計算思路是遍歷所有的方塊箩言,利用- (NSArray *)checkCrossAt:(KMDropView *)centerView檢測可消除的方塊硬贯,不斷地進行消除。該算法思路比較簡單陨收,可以后續(xù)進一步優(yōu)化饭豹。

消除動畫

有了需要消除的方塊,我們就可以執(zhí)行消除動畫,將它們從視圖中移除墨状。在- (void)kickAwayDrops:(NSArray *)drops方法中進行響應(yīng)的實現(xiàn)。我們將這些視圖移動至視圖的視野外側(cè)菲饼,然后從父視圖上移除肾砂。最后我們的動畫管理器KMAnimatorManager的實例_cubeBehavior會移除這些方塊。方塊就會以美觀的動畫形式消除宏悦。如下:

[UIView animateWithDuration:0.5 animations:^{
    
    for (UIView *drop in drops) {
        
        //設(shè)定移除后的位置
        int x = self.bounds.size.width+DROP_SIZE.width;
        int y = - DROP_SIZE.height;
        drop.center = CGPointMake(x, y);
    }
    
} completion:^(BOOL finished) {
    [drops makeObjectsPerformSelector:@selector(removeFromSuperview)];
}];
    
    
for (UIView *drop in drops) {
    [_cubeBehavior removeItem:drop];
}

用戶調(diào)換操作

KMPanGestureRecognizer

消消樂需要響應(yīng)用戶對于方塊調(diào)換的操作镐确,筆者在這里首先想到了使用Gesture。為了能夠更好地響應(yīng)用戶操作饼煞,并簡化View的代碼源葫,我自己封裝了一個手勢KMPanGestureRecognizer,并將其添加到游戲主視圖_gameView中砖瞧。

查看其頭文件息堂,可以看到一些外部需要的屬性和接口。其中比較重要的就是對于手勢的判斷块促。用戶移動方塊屬于一種Pan操作荣堰,而不是簡單的Swipe。這表明竭翠,用戶除了常規(guī)的輕掃屏幕振坚,也可以先按住一個方塊,然后再慢慢悠悠地往一個方向滑動斋扰。因此渡八,系統(tǒng)原生的UISwipeGestureRecognizer可能就不能很好滿足需求了。特自定義一個传货。

在自定義的手勢中屎鳍,對于手指滑動的方向,我們需要設(shè)定閾值损离,某些范圍內(nèi)的滑動我們需要將其標(biāo)記為無效滑動哥艇,即該操作不匹配我們的手勢。通過枚舉KMPanGestureRecognizerDirection僻澎,筆者定義了一系列方向類型貌踏,并通過direction這個@property供外部讀取。

此外窟勃,筆者來提供了一些接口祖乳,供特定情況下的使用,如可以在手指按住方塊時進行回調(diào)接口秉氧,通知外部代碼讓改方塊高亮眷昆,以達到更好的顯示效果。

KMDropView

有了調(diào)換手勢,我們就可以在手勢提供的幫助下亚斋,正確得知移動兩個方塊的時機作媚。上文提到過,我們的方塊的視圖和背后的type是分離的帅刊。type確定了纸泡,方塊的類型就確定了,用戶看到的顯示效果可以額外設(shè)置赖瞒,與type獨立女揭。所以調(diào)換兩個方塊,最根本的是調(diào)換它們的type栏饮,而顯示的視圖效果是可以通過動畫來“偽裝”的吧兔。

因此,在自定義的KMDropView中袍嬉。筆者提供了一系列@property來正確設(shè)置方塊的屬性境蔼。對于方塊使用的思路,筆者經(jīng)過思考冬竟,認(rèn)為如下是比較合理的:對于方塊屬性的設(shè)置并不直接體現(xiàn)在方塊的樣式上欧穴,方塊通過state字段的設(shè)置才最終完成樣式的繪制。而這個state也是通過枚舉泵殴,舉出了方塊所有可能的狀態(tài)涮帘。因此一個方塊最終顯示的效果,其實是取決于它當(dāng)前所處的狀態(tài)的笑诅。例如普通狀態(tài)或者高亮狀態(tài)调缨。

調(diào)換動畫

有了調(diào)換的時機和調(diào)換所需改變的東西,我們就可以實現(xiàn)最終的調(diào)換動畫了吆你。這里筆者使用了一些“偽裝”弦叶。筆者并沒有真的移動兩個方塊的位置,而是在底層的模型中簡單地調(diào)換type妇多,而上層的用戶視圖中伤哺,臨時生成兩個方塊,覆蓋在兩個原方塊上方者祖。然后將這兩個臨時方塊進行位移操作立莉,在動畫完成后消除,從而產(chǎn)生方塊調(diào)換的假象七问。為此蜓耻,我特地在KMDropView中加入了一個深拷貝(KMDropView *)duplicateFrom:(KMDropView *)originView;類方法,使得臨時生成的方塊能夠和原來的看起來一模一樣械巡。

美化操作

有了上述三步最關(guān)鍵的操作刹淌,剩下的就是一些美化和代碼整理饶氏。例如高亮選中的方塊,把游戲主視圖封裝起來有勾,獨立于ViewConroller等等疹启。

總結(jié)

這個消消樂小Demo的編寫,還是涉及到了不少新內(nèi)容蔼卡。并且含有很多可以值得優(yōu)化算法的地方皮仁。越往后學(xué)習(xí)真的越感覺到基礎(chǔ)的重要性,甚至出現(xiàn)了跨學(xué)科的需求菲宴。希望大家對于編程,能夠靜下心打好基礎(chǔ)趋急,避免急于求成。

希望我的這篇文章能夠給大家?guī)韼椭卜浅g迎大家提出寶貴意見苗傅,幫助改進這個Demo滑潘。感謝您的閱讀,歡迎分享~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末查近,一起剝皮案震驚了整個濱河市眉踱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霜威,老刑警劉巖谈喳,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戈泼,居然都是意外死亡婿禽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門大猛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扭倾,“玉大人,你說我怎么就攤上這事挽绩√乓迹” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵唉堪,是天一觀的道長模聋。 經(jīng)常有香客問我,道長巨坊,這世上最難降的妖魔是什么撬槽? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮趾撵,結(jié)果婚禮上侄柔,老公的妹妹穿的比我還像新娘共啃。我一直安慰自己,他們只是感情好暂题,可當(dāng)我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布移剪。 她就那樣靜靜地躺著,像睡著了一般薪者。 火紅的嫁衣襯著肌膚如雪纵苛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天言津,我揣著相機與錄音攻人,去河邊找鬼。 笑死悬槽,一個胖子當(dāng)著我的面吹牛怀吻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播初婆,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼蓬坡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了磅叛?” 一聲冷哼從身側(cè)響起屑咳,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弊琴,沒想到半個月后兆龙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡敲董,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年详瑞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臣缀。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡坝橡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出精置,到底是詐尸還是另有隱情计寇,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布脂倦,位于F島的核電站番宁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赖阻。R本人自食惡果不足惜蝶押,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望火欧。 院中可真熱鬧棋电,春花似錦茎截、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至于未,卻和暖如春撕攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烘浦。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工抖坪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闷叉。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓柳击,卻偏偏與公主長得像,于是被迫代替她去往敵國和親片习。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,587評論 2 350

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,813評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫蹬叭、插件藕咏、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,066評論 4 62
  • 第一次注意到這部電影是因為它的導(dǎo)演楊德昌先生,他憑借該片獲得戛納國際電影節(jié)最佳導(dǎo)演獎秽五。這部電影拍攝于2000年孽查,...
    裂錦公子閱讀 537評論 0 1
  • 無回應(yīng)之地即是絕境盲再。如果缺乏情感回應(yīng),人就等于出于絕境瓣铣,甚至是死亡之地答朋。回應(yīng)棠笑,就是光梦碗。 人是社會性動物,是群居類的...
    423429d90f38閱讀 263評論 0 0
  • 模塊是構(gòu)建應(yīng)用程序的基礎(chǔ)蓖救,也使得函數(shù)和變量私有化洪规,不直接對外暴露出來,接下來我們就要介紹Node的模塊化系統(tǒng)和它最...
    一個胖子的我閱讀 555評論 0 1