童年的記憶——iOS 拼圖游戲

前言

最近寫了一個 iOS小游戲,純屬一時興起玲昧。動機:那天看到妹妹在朋友圈發(fā)了一組圖片窟蓝,正好是九宮格的形狀罪裹,突然間就覺得這些圖片不就像是一個拼圖游戲嗎?如果可以直接移動玩拼圖,那也挺酷哇状共。擼起袖子就是干套耕!做出來的效果就是這樣的:

demo.gif

基本思路

首先我選取了一張大的原始圖片,這張圖片用來裁成一定數(shù)量的小方塊(不用數(shù)學語言嚴謹描述了峡继,影響閱讀性)冯袍,最好是選取的圖片可以讓每個小方塊圖片都有一定的辨識度。原圖片右下角的一個小方塊丟棄作為可移動的空白空間碾牌。每一個小方塊都給她編上一個獨一無二的號碼康愤。這個編號可以用來校驗拼圖是否完成。

方塊布局是使用UICollectionView來搭建的小染,難點在于拼圖的移動翘瓮,實際上我是把圖塊的移動處理成了圖塊位置的交換,只要點擊你想要移動的圖塊裤翩,這個圖塊就會瞬移到空白位置资盅,這樣來說在游戲體驗上移動更靈敏,效率更高踊赠!

開玩時呵扛,將圖塊順序打亂。

核心算法

判斷當前點擊的圖塊是否可移動

-(void)calculateIndexOfMoveable {

    //記錄空白塊的索引筐带,緊靠空白塊的方塊才可以移動今穿,實際上就是與空白塊交換位置。初始化時的空白塊統(tǒng)一在右下角伦籍。
    //計算當前可移動的方塊
    // 白色塊所在行row = indexOfWhite / totalCols
    // 白色塊所在列col = indexOfWhite % totalCols
    left = indexOfWhite - 1
    right = indexOfWhite + 1;
    up = indexOfWhite - totalCols;
    down = indexOfWhite + totalCols;

    //    但是要排除一些四周情況下的索引
    if ([self indexOfCol: left] > [self indexOfCol: indexOfWhite]) {
        //left 排除
        left = -1;
    }
    if ([self indexOfCol: right] < [self indexOfCol: indexOfWhite]) {
        //right 排除
        right = -1;
    }
    if (up < 0) {
        //up 排除
        up = -1;
    }
    if (down > totalCols*totalRows-1) {
        //down 排除
        down = -1;
    }
}
-(NSInteger)indexOfRow:(NSInteger)index {
    return index / totalCols;
}

-(NSInteger)indexOfCol:(NSInteger)index {
     return index % totalCols;
}

上面的 calculateIndexOfMoveable方法可以優(yōu)化成如下四個方法:

-(NSInteger)calculateIndexOfMoveable_left {
    left = indexOfWhite - 1;
    return [self indexOfCol: left] > [self indexOfCol: indexOfWhite] ? -1 : left;
}

-(NSInteger)calculateIndexOfMoveable_right {
    right = indexOfWhite + 1;
    return [self indexOfCol: right] < [self indexOfCol: indexOfWhite] ? -1 : right;
}

-(NSInteger)calculateIndexOfMoveable_up {
    
    return (indexOfWhite - totalCols) < 0 ? -1 : indexOfWhite - totalCols;
}

-(NSInteger)calculateIndexOfMoveable_down {
    
    return (indexOfWhite + totalCols) > (totalCols*totalRows-1) ? -1 : indexOfWhite + totalCols;
}

我這里定義了兩個數(shù)組蓝晒,一個是圖片小方塊的數(shù)組,一個是圖片塊對應(yīng)的編號數(shù)組帖鸦。這兩個數(shù)組必須保持同步更新芝薇。也可以把圖片小方塊與其對應(yīng)的編號作為一個模型類的屬性。也可以建立一個字典作儿,將編號與圖片映射洛二。
初始化圖片塊數(shù)組:

-(NSMutableArray *)dataSource {
    if (!_dataSource) {
        _dataSource = [NSMutableArray array];
        
        CGFloat x,y,w,h;
         w = (self.oringinalImg.image.size.width/totalCols)/[UIScreen mainScreen].scale;
         h = (self.oringinalImg.image.size.height/totalRows)/[UIScreen mainScreen].scale;
        
        for (int i=0; i<totalRows; i++) {
            for (int j=0; j<totalCols; j++) {
                x = j*w;
                y = i*h;
               
                CGRect rect = CGRectMake(x,y,w,h);
                if ((i==totalRows-1) && (j== totalCols-1)) {
                    [_dataSource addObject: [[UIImage alloc] init] ];
                } else {
                    
                    [_dataSource addObject: [self ct_imageFromImage:self.oringinalImg.image inRect: rect]];
                }
            }
       }
     }
     return _dataSource;
}

初始化圖片塊對應(yīng)的編號數(shù)組:

-(NSMutableArray *)startIndexs {
    if (!_startIndexs) {
        _startIndexs = [NSMutableArray array];
        for (int i = 0; i < totalCols*totalRows; i++) {
            _startIndexs[i] = @(i);
        };
    }
    return _startIndexs;
}

裁剪圖片的具體方法:

/**
 *  從圖片中按指定的位置大小截取圖片的一部分
 *
 *  @param image UIImage image 原始的圖片
 *  @param rect  CGRect rect 要截取的區(qū)域
 *
 *  @return UIImage
 */
- (UIImage *)ct_imageFromImage:(UIImage *)image inRect:(CGRect)rect {
    
    //把像素rect 轉(zhuǎn)化為點rect(如無轉(zhuǎn)化則按原圖像素取部分圖片)
    CGFloat scale = [UIScreen mainScreen].scale;
    CGFloat x= rect.origin.x*scale,y=rect.origin.y*scale,w=rect.size.width*scale,h=rect.size.height*scale;
    CGRect dianRect = CGRectMake(x, y, w, h);
    
    //截取部分圖片并生成新圖片
    CGImageRef sourceImageRef = [image CGImage];
    CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, dianRect);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
    return newImage;
}

兩個數(shù)組同步隨機亂序的方法,完成后兩個數(shù)組在相同的索引位置其對應(yīng)關(guān)系仍保持不變攻锰。

- (void)randomArray {
    //兩個數(shù)組同步打亂順序,早知道這么麻煩我就用模型將索引值綁定image了晾嘶。/(ㄒoㄒ)/~~
    NSMutableArray *newDatasourceArr = [NSMutableArray array];
    NSMutableArray *newStartIndexArr = [NSMutableArray array];

    int m = (int)self.dataSource.count;

    for (int i=0; i<m; i++) {
        int t = arc4random() % (self.dataSource.count);
        newDatasourceArr[i] = self.dataSource[t];
        newStartIndexArr[i] = self.startIndexs[t];
        self.dataSource[t] = [self.dataSource lastObject];
        self.startIndexs[t] = [self.startIndexs lastObject];
        [self.dataSource removeLastObject];
        [self.startIndexs removeLastObject];
    }
    self.dataSource = newDatasourceArr;
    self.startIndexs = newStartIndexArr;
}


12.17修改更新:關(guān)于打亂圖序,我這種隨機打亂順序的做法欠妥娶吞,試玩幾次后發(fā)現(xiàn)有些情況我總是還原不了垒迂,回憶上學時玩過的一款游戲沒有出現(xiàn)過這樣的情況。這時候我開始懷疑并不是所有的序列都可以進行還原妒蛇。而我卻忽略了娇斑,這非常不應(yīng)該策添。

打亂后還需要驗證當前狀態(tài)是否有解。根據(jù)相關(guān)定理毫缆,如果打亂后的排列與原始排列的逆序數(shù)奇偶性相同唯竹,則是可還原的(證明比較簡單 參考鏈接——不可還原的拼圖)。如果拼圖的版塊是隨機打亂的苦丁,那么只有50%概率是可以被還原的浸颓。我這里統(tǒng)一將空格設(shè)置在末尾最后一個,可以忽略掉旺拉,不影響逆序數(shù)产上。

方案二:讓程序隨機移動數(shù)次,這樣肯定是能夠還原的蛾狗。這個“數(shù)次”也值得商榷晋涣,要盡可能亂,又不能太多次了沉桌。但是我這個游戲設(shè)定的打亂后空格統(tǒng)一在最后一格谢鹊,還需要調(diào)整空格位置,同樣用到剛才的逆序數(shù)相關(guān)定理留凭,將空格與當前最后一個格子交換佃扼,現(xiàn)在排列奇偶性改變,還需要隨機將非空格的兩個格子進行交換一次蔼夜。這樣就可以了兼耀。

方案三:對于m*n的拼圖,從拼圖板塊中任取三塊做輪換求冷,通過[(m*n)/3]^2次輪換瘤运,即可實現(xiàn)相當“亂”的打亂效果。所謂三輪換匠题,實質(zhì)就是兩次交換:如123拯坟,1與2交換后,這時候狀態(tài)213梧躺,再3與2交換似谁,這時候狀態(tài)312傲绣。體現(xiàn)在拼圖上很好實驗掠哥,把包含空格的2*2格子進行各種移動變換,就對應(yīng)了3輪換秃诵。


還有個功能就是可以自定義幾行幾列续搀,難點是需要動態(tài)更新相關(guān)數(shù)據(jù),值得注意的是本例中cell是復用的菠净,大小禁舷、內(nèi)容需要根據(jù)需要即時調(diào)整彪杉。

最后奉獻上demo https://github.com/imsz5460/-puzzlegame 歡迎大家找bug,并提出優(yōu)化意見牵咙,謝謝派近!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洁桌,隨后出現(xiàn)的幾起案子渴丸,更是在濱河造成了極大的恐慌,老刑警劉巖另凌,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谱轨,死亡現(xiàn)場離奇詭異,居然都是意外死亡吠谢,警方通過查閱死者的電腦和手機土童,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來工坊,“玉大人献汗,你說我怎么就攤上這事≌ぷ椋” “怎么了雀瓢?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玉掸。 經(jīng)常有香客問我刃麸,道長,這世上最難降的妖魔是什么司浪? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任泊业,我火速辦了婚禮,結(jié)果婚禮上啊易,老公的妹妹穿的比我還像新娘吁伺。我一直安慰自己,他們只是感情好租谈,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布篮奄。 她就那樣靜靜地躺著,像睡著了一般割去。 火紅的嫁衣襯著肌膚如雪窟却。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天呻逆,我揣著相機與錄音夸赫,去河邊找鬼。 笑死咖城,一個胖子當著我的面吹牛茬腿,可吹牛的內(nèi)容都是我干的呼奢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼切平,長吁一口氣:“原來是場噩夢啊……” “哼握础!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起悴品,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弓候,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后他匪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菇存,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年邦蜜,在試婚紗的時候發(fā)現(xiàn)自己被綠了依鸥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡悼沈,死狀恐怖贱迟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情絮供,我是刑警寧澤衣吠,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站壤靶,受9級特大地震影響缚俏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贮乳,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一忧换、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧向拆,春花似錦亚茬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颈将,卻和暖如春梢夯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吆鹤。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工厨疙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洲守,地道東北人疑务。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓沾凄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親知允。 傳聞我的和親對象是個殘疾皇子撒蟀,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

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