Unity三消算法

前言

目前的游戲市場(chǎng)可謂日漸蕭條,分分鐘就逼死眾多產(chǎn)品經(jīng)理,三消游戲可謂一把溫柔的彎刀朦肘,從女人這塊獲取到了一大片的市場(chǎng),動(dòng)不動(dòng)就做個(gè)幾百關(guān)双饥,相互之間還有攀比媒抠,果然女人的錢還是更好賺一些的。當(dāng)然咏花,三消游戲確實(shí)有很大的優(yōu)勢(shì)趴生,不浪費(fèi)太多時(shí)間,不那么燒腦昏翰,簡(jiǎn)單有趣苍匆。但如果要做一款集成性很高的三消游戲,對(duì)于開(kāi)發(fā)者而言也并不是那么簡(jiǎn)單棚菊,畢竟要用到很多算法浸踩,相比所謂的FPS、MMORPG有另一層次的深度统求。今天民轴,就給大家簡(jiǎn)單分享一下三消中的核心算法,以及在Unity中的實(shí)現(xiàn)球订。

  • 消除算法圖文詳解
    • 三消算法首要實(shí)現(xiàn)的就是找到所有三個(gè)或三個(gè)以上的可消除對(duì)象后裸,但直接找到這些對(duì)象是不太現(xiàn)實(shí)的,所以我們要將需求拆分冒滩∥⑹唬可不可以先獲取所有圖案相連的對(duì)象,進(jìn)而在獲取三消對(duì)象开睡,這個(gè)算法也是眾多三消游戲的一致實(shí)現(xiàn)因苹。


      獲取圖案相同的所有相連對(duì)象
/// <summary>
    /// 填充相同Item列表
    /// </summary>
    public void FillSameItemsList(Item current)
    {
        //如果已存在,跳過(guò)
        if (sameItemsList.Contains (current))
            return;
        //添加到列表
        sameItemsList.Add (current);
        //上下左右的Item
        Item[] tempItemList = new Item[]{
            GetUpItem(current),GetDownItem(current),
            GetLeftItem(current),GetRightItem(current)};
        for (int i = 0; i < tempItemList.Length; i++) {
            //如果Item不合法篇恒,跳過(guò)
            if (tempItemList [i] == null)
                continue;
            if (current.currentSpr == tempItemList [i].currentSpr) {
                FillSameItemsList (tempItemList[i]);
            }
        }
    }
  • 獲取圖案相同的對(duì)象扶檐,一定要以一個(gè)對(duì)象為基準(zhǔn),這樣才能夠知道以誰(shuí)為中心胁艰,以這個(gè)中心為核心橫向及縱向的檢測(cè)款筑,檢測(cè)到三個(gè)及以上的對(duì)象智蝠,那說(shuō)明是可以消除的對(duì)象。


    以檢測(cè)點(diǎn)為中心橫向縱向檢測(cè)
/// <summary>
    /// 填充待消除列表
    /// </summary>
    /// <param name="current">Current.</param>
    public void FillBoomList(Item current)
    {
        //計(jì)數(shù)器
        int rowCount = 0;
        int columnCount = 0;
        //臨時(shí)列表
        List<Item> rowTempList = new List<Item> ();
        List<Item> columnTempList = new List<Item> ();
        ///橫向縱向檢測(cè)
        foreach (var item in sameItemsList) {

            //如果在同一行
            if (item.itemRow == current.itemRow) {
                //判斷該點(diǎn)與Curren中間有無(wú)間隙
                bool rowCanBoom  = CheckItemsInterval(true,current,item);
                if (rowCanBoom) {
                    //計(jì)數(shù)
                    rowCount++;
                    //添加到行臨時(shí)列表
                    rowTempList.Add (item);
                }
            }
            //如果在同一列
            if (item.itemColumn == current.itemColumn) {
                //判斷該點(diǎn)與Curren中間有無(wú)間隙
                bool columnCanBoom  = CheckItemsInterval(false,current,item);
                if (columnCanBoom) {
                    //計(jì)數(shù)
                    columnCount++;
                    //添加到列臨時(shí)列表
                    columnTempList.Add (item);
                }
            }
        }
        //橫向消除
        bool horizontalBoom = false;
        //如果橫向三個(gè)以上
        if (rowCount > 2) {
            //將臨時(shí)列表中的Item全部放入BoomList
            boomList.AddRange (rowTempList);
            //橫向消除
            horizontalBoom = true;
        }
        //如果縱向三個(gè)以上
        if (columnCount > 2) {
            if (horizontalBoom) {
                //剔除自己
                boomList.Remove (current);
            }
            //將臨時(shí)列表中的Item全部放入BoomList
            boomList.AddRange (columnTempList);
        }
        //如果沒(méi)有消除對(duì)象奈梳,返回
        if (boomList.Count == 0)
            return;
        //創(chuàng)建臨時(shí)的BoomList
        List<Item> tempBoomList = new List<Item> ();
        //轉(zhuǎn)移到臨時(shí)列表
        tempBoomList.AddRange (boomList);
        //開(kāi)啟處理BoomList的協(xié)程
        StartCoroutine (ManipulateBoomList (tempBoomList));
    }
  • 當(dāng)然也有特殊情況杈湾,在游戲開(kāi)始時(shí),如沒(méi)有設(shè)置任何阻止同色的算法攘须,即有可能出現(xiàn)這種狀況漆撞,我們就要也采用一些算法去防止Bug出現(xiàn)。


    跳躍同行同列Bug
/// <summary>
    /// 檢測(cè)兩個(gè)Item之間是否有間隙(圖案不一致)
    /// </summary>
    /// <param name="isHorizontal">是否是橫向.</param>
    /// <param name="begin">檢測(cè)起點(diǎn).</param>
    /// <param name="end">檢測(cè)終點(diǎn).</param>
    private bool CheckItemsInterval(bool isHorizontal,Item begin,Item end)
    {
        //獲取圖案
        Sprite spr = begin.currentSpr;
        //如果是橫向
        if (isHorizontal) {
            //起點(diǎn)終點(diǎn)列號(hào)
            int beginIndex = begin.itemColumn;
            int endIndex = end.itemColumn;
            //如果起點(diǎn)在右于宙,交換起點(diǎn)終點(diǎn)列號(hào)
            if (beginIndex > endIndex) {
                beginIndex = end.itemColumn;
                endIndex = begin.itemColumn;
            }
            //遍歷中間的Item
            for (int i = beginIndex + 1; i < endIndex; i++) {
                //異常處理(中間未生成浮驳,標(biāo)識(shí)為不合法)
                if (allItems [begin.itemRow, i] == null)
                    return false;
                //如果中間有間隙(有圖案不一致的)
                if (allItems [begin.itemRow, i].currentSpr != spr) {
                    return false;
                }
            }
            return true;
        } else {
            //起點(diǎn)終點(diǎn)行號(hào)
            int beginIndex = begin.itemRow;
            int endIndex = end.itemRow;
            //如果起點(diǎn)在上,交換起點(diǎn)終點(diǎn)列號(hào)
            if (beginIndex > endIndex) {
                beginIndex = end.itemRow;
                endIndex = begin.itemRow;
            }
            //遍歷中間的Item
            for (int i = beginIndex + 1; i < endIndex; i++) {
                //如果中間有間隙(有圖案不一致的)
                if (allItems [i, begin.itemColumn].currentSpr != spr) {
                    return false;
                }
            }
            return true;
        }
    }
  • 接下來(lái)就是消除處理了捞魁,采用一些動(dòng)畫之類至会,此處略過(guò),我們來(lái)講解下落算法署驻。下落算法有很多,我們采用的是逐個(gè)入位法健霹。


    逐個(gè)入位法下落
/// <summary>
    /// Items下落
    /// </summary>
    /// <returns>The drop.</returns>
    IEnumerator ItemsDrop()
    {
        isOperation = true;
        //逐列檢測(cè)
        for (int i = 0; i < tableColumn; i++) {
            //計(jì)數(shù)器
            int count = 0;
            //下落隊(duì)列
            Queue<Item> dropQueue = new Queue<Item> ();
            //逐行檢測(cè)
            for (int j = 0; j < tableRow; j++) {
                if (allItems [j, i] != null) {
                    //計(jì)數(shù)
                    count++;
                    //放入隊(duì)列
                    dropQueue.Enqueue(allItems [j, i]);
                }
            }
            //下落
            for (int k = 0; k < count; k++) {
                //獲取要下落的Item
                Item current = dropQueue.Dequeue ();
                //修改全局?jǐn)?shù)組(原位置情況)
                allItems[current.itemRow,current.itemColumn] = null;
                //修改Item的行數(shù)
                current.itemRow = k;
                //修改全局?jǐn)?shù)組(填充新位置)
                allItems[current.itemRow,current.itemColumn] = current;
                //下落
                current.GetComponent<ItemOperation>().
                    CurrentItemDrop(allPos[current.itemRow,current.itemColumn]);
            }
        }

        yield return new WaitForSeconds (0.2f);

        StartCoroutine (CreateNewItem());
        yield return new WaitForSeconds (0.2f);
        AllBoom ();
    }
  • 最后生成新的對(duì)象
/// <summary>
    /// 生成新的Item
    /// </summary>
    /// <returns>The new item.</returns>
    public IEnumerator CreateNewItem()
    {
        isOperation = true;
        for (int i = 0; i < tableColumn; i++) {
            int count = 0;
            Queue<GameObject> newItemQueue = new Queue<GameObject> ();
            for (int j = 0; j < tableRow; j++) {
                if (allItems [j, i] == null) {
                    //生成一個(gè)Item
                    GameObject current = (GameObject)Instantiate(Resources.
                        Load<GameObject> (Util.ResourcesPrefab + Util.Item));
//                      ObjectPool.instance.GetGameObject (Util.Item, transform);
                    current.transform.parent = transform;
                    current.transform.position = allPos [tableRow - 1, i];
                    newItemQueue.Enqueue (current);
                    count++;
                }
            }
            for (int k = 0; k < count; k++) {
                //獲取Item組件
                Item currentItem = newItemQueue.Dequeue ().GetComponent<Item>();
                //隨機(jī)數(shù)
                int random = Random.Range (0, randomSprites.Length);
                //修改腳本中的圖片
                currentItem.currentSpr = randomSprites [random];
                //修改真實(shí)圖片
                currentItem.currentImg.sprite = randomSprites [random];
                //獲取要移動(dòng)的行數(shù)
                int r = tableRow - count + k;
                //移動(dòng)
                currentItem.GetComponent<ItemOperation> ().ItemMove (r,i,allPos[r,i]);
            }
        }
        yield break;
    }
  • 當(dāng)然如果兩個(gè)圖片交換后旺上,無(wú)法消除要還原回原來(lái)位置
/// <summary>
    /// Item交換
    /// </summary>
    /// <returns>The exchange.</returns>
    /// <param name="dir">Dir.</param>
    IEnumerator ItemExchange(Vector2 dir)
    {
        //獲取目標(biāo)行列
        int targetRow = item.itemRow + System.Convert.ToInt32(dir.y);
        int targetColumn = item.itemColumn + System.Convert.ToInt32(dir.x);
        //檢測(cè)合法
        bool isLagal = GameController.instance.CheckRCLegal (targetRow, targetColumn);
        if (!isLagal) {
            GameController.instance.isOperation = false;
            //不合法跳出
            yield break;
        }
        //獲取目標(biāo)
        Item target = GameController.instance.allItems [targetRow, targetColumn];
        //從全局列表中獲取當(dāng)前item,查看是否已經(jīng)被消除糖埋,被消除后不能再交換
        Item myItem = GameController.instance.allItems [item.itemRow, item.itemColumn];
        if (!target || !myItem) {
            GameController.instance.isOperation = false;
            //Item已經(jīng)被消除
            yield break;
        }
        //相互移動(dòng)
        target.GetComponent<ItemOperation> ().ItemMove (item.itemRow, item.itemColumn, transform.position);
        ItemMove (targetRow, targetColumn, target.transform.position);
        //還原標(biāo)志位
        bool reduction = false;
        //消除處理
        item.CheckAroundBoom();
        if (GameController.instance.boomList.Count == 0) {
            reduction = true;
        }
        target.CheckAroundBoom ();
        if (GameController.instance.boomList.Count != 0) {
            reduction = false;
        }
        //還原
        if (reduction) {
            //延遲
            yield return new WaitForSeconds (0.2f);
            //臨時(shí)行列
            int tempRow, tempColumn;
            tempRow = myItem.itemRow;
            tempColumn = myItem.itemColumn;
            //移動(dòng)
            myItem.GetComponent<ItemOperation> ().ItemMove (target.itemRow,
                target.itemColumn, target.transform.position);
            target.GetComponent<ItemOperation> ().ItemMove (tempRow,
                tempColumn, myItem.transform.position);
            //延遲
            yield return new WaitForSeconds (0.2f);
            //操作完畢
            GameController.instance.isOperation = false;
        } 
    }
  • 項(xiàng)目實(shí)踐


    項(xiàng)目實(shí)踐

    核心UML類圖

結(jié)束語(yǔ)

當(dāng)然這個(gè)項(xiàng)目是最基礎(chǔ)版宣吱,只有簡(jiǎn)單的消除操作,如果加上道具特效瞳别,算法會(huì)更多征候,以后在慢慢琢磨品鑒。最后奉上源碼祟敛,這個(gè)項(xiàng)目下落及生成新對(duì)象的延遲時(shí)間還沒(méi)有細(xì)調(diào)疤坝,調(diào)好后玩起來(lái)比較流暢。(內(nèi)附價(jià)值50美元的資源包喔??????)鏈接: https://pan.baidu.com/s/1gfqpDmz 密碼: n9r9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末馆铁,一起剝皮案震驚了整個(gè)濱河市跑揉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌埠巨,老刑警劉巖历谍,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異辣垒,居然都是意外死亡望侈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門勋桶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脱衙,“玉大人侥猬,你說(shuō)我怎么就攤上這事∑袂穑” “怎么了陵究?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奥帘。 經(jīng)常有香客問(wèn)我铜邮,道長(zhǎng),這世上最難降的妖魔是什么寨蹋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任松蒜,我火速辦了婚禮,結(jié)果婚禮上已旧,老公的妹妹穿的比我還像新娘秸苗。我一直安慰自己,他們只是感情好运褪,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布惊楼。 她就那樣靜靜地躺著,像睡著了一般秸讹。 火紅的嫁衣襯著肌膚如雪檀咙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天璃诀,我揣著相機(jī)與錄音弧可,去河邊找鬼。 笑死劣欢,一個(gè)胖子當(dāng)著我的面吹牛棕诵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凿将,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼校套,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了牧抵?” 一聲冷哼從身側(cè)響起搔确,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驶鹉,失蹤者是張志新(化名)和其女友劉穎迄汛,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體趾浅,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弛作,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年涕蜂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片映琳。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡机隙,死狀恐怖蜘拉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情有鹿,我是刑警寧澤旭旭,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站葱跋,受9級(jí)特大地震影響持寄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娱俺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一稍味、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荠卷,春花似錦模庐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至慎冤,卻和暖如春疼燥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粪薛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工悴了, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留搏恤,地道東北人违寿。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像熟空,于是被迫代替她去往敵國(guó)和親藤巢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)息罗。 張土汪:刷leetcod...
    土汪閱讀 12,748評(píng)論 0 33
  • 回溯算法 回溯法:也稱為試探法掂咒,它并不考慮問(wèn)題規(guī)模的大小,而是從問(wèn)題的最明顯的最小規(guī)模開(kāi)始逐步求解出可能的答案迈喉,并...
    fredal閱讀 13,669評(píng)論 0 89
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法绍刮,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法挨摸,繼承相關(guān)的語(yǔ)法孩革,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,664評(píng)論 18 399
  • 想了一整年的用輸出倒逼自己的輸入得运,讓自己的知識(shí)更加成體系化膝蜈,網(wǎng)絡(luò)化锅移。懶惰于我,終于在一年多的今天利用午休小憩來(lái)寫上...
    最早的時(shí)光_最好的時(shí)光閱讀 245評(píng)論 0 0
  • 廣泛持久的學(xué)習(xí)助我們認(rèn)識(shí)這個(gè)世界饱搏,發(fā)現(xiàn)更多可能性非剃;認(rèn)真精專的學(xué)習(xí)讓我們變得不可替代,能以更好的姿態(tài)迎接未來(lái)推沸。 ...
    蘭小魚(yú)的海底世界閱讀 353評(píng)論 0 0