Unity三消算法


  • 消除算法圖文詳解
    • 三消算法首要實現的就是找到所有三個或三個以上的可消除對象,但直接找到這些對象是不太現實的,所以我們要將需求拆分。可不可以先獲取所有圖案相連的對象,進而在獲取三消對象,這個算法也是眾多三消游戲的一致實現。

      獲取圖案相同的所有相連對象
 1 /// <summary>
 2     /// 填充相同Item列表
 3     /// </summary>
 4     public void FillSameItemsList(Item current)
 5     {
 6         //如果已存在,跳過
 7         if (sameItemsList.Contains (current))
 8             return;
 9         //添加到列表
10         sameItemsList.Add (current);
11         //上下左右的Item
12         Item[] tempItemList = new Item[]{
13             GetUpItem(current),GetDownItem(current),
14             GetLeftItem(current),GetRightItem(current)};
15         for (int i = 0; i < tempItemList.Length; i++) {
16             //如果Item不合法,跳過
17             if (tempItemList [i] == null)
18                 continue;
19             if (current.currentSpr == tempItemList [i].currentSpr) {
20                 FillSameItemsList (tempItemList[i]);
21             }
22         }
23     }

 

  • 獲取圖案相同的對象,一定要以一個對象為基准,這樣才能夠知道以誰為中心,以這個中心為核心橫向及縱向的檢測,檢測到三個及以上的對象,那說明是可以消除的對象。


    以檢測點為中心橫向縱向檢測
     1 /// <summary>
     2 /// 填充待消除列表
     3 /// </summary>
     4 /// <param name="current">Current.</param>
     5 public void FillBoomList(Item current)
     6 {
     7     //計數器
     8     int rowCount = 0;
     9     int columnCount = 0;
    10     //臨時列表
    11     List<Item> rowTempList = new List<Item> ();
    12     List<Item> columnTempList = new List<Item> ();
    13     ///橫向縱向檢測
    14     foreach (var item in sameItemsList) {
    15 
    16         //如果在同一行
    17         if (item.itemRow == current.itemRow) {
    18             //判斷該點與Curren中間有無間隙
    19             bool rowCanBoom  = CheckItemsInterval(true,current,item);
    20             if (rowCanBoom) {
    21                 //計數
    22                 rowCount++;
    23                 //添加到行臨時列表
    24                 rowTempList.Add (item);
    25             }
    26         }
    27         //如果在同一列
    28         if (item.itemColumn == current.itemColumn) {
    29             //判斷該點與Curren中間有無間隙
    30             bool columnCanBoom  = CheckItemsInterval(false,current,item);
    31             if (columnCanBoom) {
    32                 //計數
    33                 columnCount++;
    34                 //添加到列臨時列表
    35                 columnTempList.Add (item);
    36             }
    37         }
    38     }
    39     //橫向消除
    40     bool horizontalBoom = false;
    41     //如果橫向三個以上
    42     if (rowCount > 2) {
    43         //將臨時列表中的Item全部放入BoomList
    44         boomList.AddRange (rowTempList);
    45         //橫向消除
    46         horizontalBoom = true;
    47     }
    48     //如果縱向三個以上
    49     if (columnCount > 2) {
    50         if (horizontalBoom) {
    51             //剔除自己
    52             boomList.Remove (current);
    53         }
    54         //將臨時列表中的Item全部放入BoomList
    55         boomList.AddRange (columnTempList);
    56     }
    57     //如果沒有消除對象,返回
    58     if (boomList.Count == 0)
    59         return;
    60     //創建臨時的BoomList
    61     List<Item> tempBoomList = new List<Item> ();
    62     //轉移到臨時列表
    63     tempBoomList.AddRange (boomList);
    64     //開啟處理BoomList的協程
    65     StartCoroutine (ManipulateBoomList (tempBoomList));
    66 }

     

    • 當然也有特殊情況,在游戲開始時,如沒有設置任何阻止同色的算法,即有可能出現這種狀況,我們就要也采用一些算法去防止Bug出現。

      跳躍同行同列Bug
       1 /// <summary>
       2 /// 檢測兩個Item之間是否有間隙(圖案不一致)
       3 /// </summary>
       4 /// <param name="isHorizontal">是否是橫向.</param>
       5 /// <param name="begin">檢測起點.</param>
       6 /// <param name="end">檢測終點.</param>
       7 private bool CheckItemsInterval(bool isHorizontal,Item begin,Item end)
       8 {
       9  //獲取圖案
      10  Sprite spr = begin.currentSpr;
      11  //如果是橫向
      12  if (isHorizontal) {
      13      //起點終點列號
      14      int beginIndex = begin.itemColumn;
      15      int endIndex = end.itemColumn;
      16      //如果起點在右,交換起點終點列號
      17      if (beginIndex > endIndex) {
      18          beginIndex = end.itemColumn;
      19          endIndex = begin.itemColumn;
      20      }
      21      //遍歷中間的Item
      22      for (int i = beginIndex + 1; i < endIndex; i++) {
      23          //異常處理(中間未生成,標識為不合法)
      24          if (allItems [begin.itemRow, i] == null)
      25              return false;
      26          //如果中間有間隙(有圖案不一致的)
      27          if (allItems [begin.itemRow, i].currentSpr != spr) {
      28              return false;
      29          }
      30      }
      31      return true;
      32  } else {
      33      //起點終點行號
      34      int beginIndex = begin.itemRow;
      35      int endIndex = end.itemRow;
      36      //如果起點在上,交換起點終點列號
      37      if (beginIndex > endIndex) {
      38          beginIndex = end.itemRow;
      39          endIndex = begin.itemRow;
      40      }
      41      //遍歷中間的Item
      42      for (int i = beginIndex + 1; i < endIndex; i++) {
      43          //如果中間有間隙(有圖案不一致的)
      44          if (allItems [i, begin.itemColumn].currentSpr != spr) {
      45              return false;
      46          }
      47      }
      48      return true;
      49  }
      50 }

       

    • 接下來就是消除處理了,采用一些動畫之類,此處略過,我們來講解下落算法。下落算法有很多,我們采用的是逐個入位法。


      逐個入位法下落
       1 /// <summary>
       2 /// Items下落
       3 /// </summary>
       4 /// <returns>The drop.</returns>
       5 IEnumerator ItemsDrop()
       6 {
       7   isOperation = true;
       8   //逐列檢測
       9   for (int i = 0; i < tableColumn; i++) {
      10       //計數器
      11       int count = 0;
      12       //下落隊列
      13       Queue<Item> dropQueue = new Queue<Item> ();
      14       //逐行檢測
      15       for (int j = 0; j < tableRow; j++) {
      16           if (allItems [j, i] != null) {
      17               //計數
      18               count++;
      19               //放入隊列
      20               dropQueue.Enqueue(allItems [j, i]);
      21           }
      22       }
      23       //下落
      24       for (int k = 0; k < count; k++) {
      25           //獲取要下落的Item
      26           Item current = dropQueue.Dequeue ();
      27           //修改全局數組(原位置情況)
      28           allItems[current.itemRow,current.itemColumn] = null;
      29           //修改Item的行數
      30           current.itemRow = k;
      31           //修改全局數組(填充新位置)
      32           allItems[current.itemRow,current.itemColumn] = current;
      33           //下落
      34           current.GetComponent<ItemOperation>().
      35               CurrentItemDrop(allPos[current.itemRow,current.itemColumn]);
      36       }
      37   }
      38 
      39   yield return new WaitForSeconds (0.2f);
      40 
      41   StartCoroutine (CreateNewItem());
      42   yield return new WaitForSeconds (0.2f);
      43   AllBoom ();
      44 }

       

    • 最后生成新的對象
       1 /// <summary>
       2 /// 生成新的Item
       3 /// </summary>
       4 /// <returns>The new item.</returns>
       5 public IEnumerator CreateNewItem()
       6 {
       7  isOperation = true;
       8  for (int i = 0; i < tableColumn; i++) {
       9      int count = 0;
      10      Queue<GameObject> newItemQueue = new Queue<GameObject> ();
      11      for (int j = 0; j < tableRow; j++) {
      12          if (allItems [j, i] == null) {
      13              //生成一個Item
      14              GameObject current = (GameObject)Instantiate(Resources.
      15                  Load<GameObject> (Util.ResourcesPrefab + Util.Item));
      16 //                        ObjectPool.instance.GetGameObject (Util.Item, transform);
      17              current.transform.parent = transform;
      18              current.transform.position = allPos [tableRow - 1, i];
      19              newItemQueue.Enqueue (current);
      20              count++;
      21          }
      22      }
      23      for (int k = 0; k < count; k++) {
      24          //獲取Item組件
      25          Item currentItem = newItemQueue.Dequeue ().GetComponent<Item>();
      26          //隨機數
      27          int random = Random.Range (0, randomSprites.Length);
      28          //修改腳本中的圖片
      29          currentItem.currentSpr = randomSprites [random];
      30          //修改真實圖片
      31          currentItem.currentImg.sprite = randomSprites [random];
      32          //獲取要移動的行數
      33          int r = tableRow - count + k;
      34          //移動
      35          currentItem.GetComponent<ItemOperation> ().ItemMove (r,i,allPos[r,i]);
      36      }
      37  }
      38  yield break;
      39 }

       

    • 當然如果兩個圖片交換后,無法消除要還原回原來位置
       1 /// <summary>
       2 /// Item交換
       3 /// </summary>
       4 /// <returns>The exchange.</returns>
       5 /// <param name="dir">Dir.</param>
       6 IEnumerator ItemExchange(Vector2 dir)
       7 {
       8   //獲取目標行列
       9   int targetRow = item.itemRow + System.Convert.ToInt32(dir.y);
      10   int targetColumn = item.itemColumn + System.Convert.ToInt32(dir.x);
      11   //檢測合法
      12   bool isLagal = GameController.instance.CheckRCLegal (targetRow, targetColumn);
      13   if (!isLagal) {
      14       GameController.instance.isOperation = false;
      15       //不合法跳出
      16       yield break;
      17   }
      18   //獲取目標
      19   Item target = GameController.instance.allItems [targetRow, targetColumn];
      20   //從全局列表中獲取當前item,查看是否已經被消除,被消除后不能再交換
      21   Item myItem = GameController.instance.allItems [item.itemRow, item.itemColumn];
      22   if (!target || !myItem) {
      23       GameController.instance.isOperation = false;
      24       //Item已經被消除
      25       yield break;
      26   }
      27   //相互移動
      28   target.GetComponent<ItemOperation> ().ItemMove (item.itemRow, item.itemColumn, transform.position);
      29   ItemMove (targetRow, targetColumn, target.transform.position);
      30   //還原標志位
      31   bool reduction = false;
      32   //消除處理
      33   item.CheckAroundBoom();
      34   if (GameController.instance.boomList.Count == 0) {
      35       reduction = true;
      36   }
      37   target.CheckAroundBoom ();
      38   if (GameController.instance.boomList.Count != 0) {
      39       reduction = false;
      40   }
      41   //還原
      42   if (reduction) {
      43       //延遲
      44       yield return new WaitForSeconds (0.2f);
      45       //臨時行列
      46       int tempRow, tempColumn;
      47       tempRow = myItem.itemRow;
      48       tempColumn = myItem.itemColumn;
      49       //移動
      50       myItem.GetComponent<ItemOperation> ().ItemMove (target.itemRow,
      51           target.itemColumn, target.transform.position);
      52       target.GetComponent<ItemOperation> ().ItemMove (tempRow,
      53           tempColumn, myItem.transform.position);
      54       //延遲
      55       yield return new WaitForSeconds (0.2f);
      56       //操作完畢
      57       GameController.instance.isOperation = false;
      58   } 
      59 }

       

  • 項目實踐

    項目實踐


    核心UML類圖
     

    結束語

    當然這個項目是最基礎版,只有簡單的消除操作,如果加上道具特效,算法會更多,以后在慢慢琢磨品鑒。最后奉上源碼,這個項目下落及生成新對象的延遲時間還沒有細調,調好后玩起來比較流暢。(內附價值50美元的資源包喔😉😉😉)鏈接: https://pan.baidu.com/s/1gfqpDmz 密碼: n9r9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM