- 消除算法圖文詳解
- 三消算法首要實現的就是找到所有三個或三個以上的可消除對象,但直接找到這些對象是不太現實的,所以我們要將需求拆分。可不可以先獲取所有圖案相連的對象,進而在獲取三消對象,這個算法也是眾多三消游戲的一致實現。
獲取圖案相同的所有相連對象
- 三消算法首要實現的就是找到所有三個或三個以上的可消除對象,但直接找到這些對象是不太現實的,所以我們要將需求拆分。可不可以先獲取所有圖案相連的對象,進而在獲取三消對象,這個算法也是眾多三消游戲的一致實現。
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出現。
跳躍同行同列Bug1 /// <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 }
- 當然也有特殊情況,在游戲開始時,如沒有設置任何阻止同色的算法,即有可能出現這種狀況,我們就要也采用一些算法去防止Bug出現。
- 項目實踐
項目實踐
結束語
當然這個項目是最基礎版,只有簡單的消除操作,如果加上道具特效,算法會更多,以后在慢慢琢磨品鑒。最后奉上源碼,這個項目下落及生成新對象的延遲時間還沒有細調,調好后玩起來比較流暢。(內附價值50美元的資源包喔😉😉😉)鏈接: https://pan.baidu.com/s/1gfqpDmz 密碼: n9r9