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

posted @ 2017-07-05 09:42  AaronBlogs  阅读(6282)  评论(0编辑  收藏  举报