三消游戏算法图文详解

    xiaoxiao2022-06-25  205

    转载自:https://blog.csdn.net/sinat_39291423/article/details/78089828

    之前小编查询发的资料小编本人也不太理解,所以这里又找了一个讲的个很详细的文章,整理过后发出来大家一起分享!

    消除算法图文详解

    三消算法首要实现的就是找到所有三个或三个以上的可消除对象,但直接找到这些对象是不太现实的,所以我们要将需求拆分。可不可以先获取所有图案相连的对象,进而在获取三消对象,这个算法也是众多三消游戏的一致实现。

    获取图案相同的所有相连对象

    // 填充相同Item列表 public void FillSameItemsList(Item current) {     //如果已存在,跳过     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不合法,跳过         if (tempItemList [i] == null)         continue;         if (current.currentSpr == tempItemList [i].currentSpr)          {             FillSameItemsList (tempItemList[i]);         }     } } 获取图案相同的对象,一定要以一个对象为基准,这样才能够知道以谁为中心,以这个中心为核心横向及纵向的检测,检测到三个及以上的对象,那说明是可以消除的对象。

    以检测点为中心横向纵向检测

    // 填充待消除列表 public void FillBoomList(Item current) {     //计数器     int rowCount = 0;     int columnCount = 0;     //临时列表     List rowTempList = new List ();     List columnTempList = new List ();     //横向纵向检测     foreach (var item in sameItemsList)      {         //如果在同一行         if (item.itemRow == current.itemRow)          {             //判断该点与Curren中间有无间隙             bool rowCanBoom = CheckItemsInterval(true,current,item);             if (rowCanBoom)              {                 //计数                 rowCount++;                 //添加到行临时列表                 rowTempList.Add (item);             }         }         //如果在同一列         if (item.itemColumn == current.itemColumn)          {             //判断该点与Curren中间有无间隙             bool columnCanBoom = CheckItemsInterval(false,current,item);              if (columnCanBoom)              {                 //计数                 columnCount++;                 //添加到列临时列表                 columnTempList.Add (item);             }         }     }     //横向消除     bool horizontalBoom = false;     //如果横向三个以上     if (rowCount > 2)      {         //将临时列表中的Item全部放入BoomList         boomList.AddRange (rowTempList);         //横向消除         horizontalBoom = true;     }      //如果纵向三个以上     if (columnCount > 2)      {         if (horizontalBoom)          {             //剔除自己             boomList.Remove (current);         }         //将临时列表中的Item全部放入BoomList         boomList.AddRange (columnTempList);     }     //如果没有消除对象,返回     if (boomList.Count == 0)     {         return;     }     //创建临时的BoomList     List tempBoomList = new List ();     //转移到临时列表     tempBoomList.AddRange (boomList);     //开启处理BoomList的协程     StartCoroutine (ManipulateBoomList (tempBoomList)); } 当然也有特殊情况,在游戏开始时,如没有设置任何阻止同色的算法,即有可能出现这种状况,我们就要也采用一些算法去防止Bug出现。

    跳跃同行同列Bug

    /// <summary> /// 检测两个Item之间是否有间隙(图案不一致) /// </summary> /// <param name="isHorizontal">是否是横向</param> /// <param name="begin">检测起点</param> /// <param name="end">监测终点</param> /// <returns></returns> private bool CheckItemsInterval(bool isHorizontal,Item begin,Item end) {     //获取图案     Sprite spr = begin.currentSpr; //如果是横向     if (isHorizontal)      {         //起点终点列号         int beginIndex = begin.itemColumn;         int endIndex = end.itemColumn;         //如果起点在右,交换起点终点列号         if (beginIndex > endIndex)          {             beginIndex = end.itemColumn;             endIndex = begin.itemColumn;         }         //遍历中间的Item         for (int i = beginIndex + 1; i < endIndex; i++)          {             //异常处理(中间未生成,标识为不合法)             if (allItems [begin.itemRow, i] == null)             {                    return false;             }             //如果中间有间隙(有图案不一致的)             if (allItems [begin.itemRow, i].currentSpr != spr)              {                 return false;             }         }         return true;     }      else      {         //起点终点行号         int beginIndex = begin.itemRow;         int endIndex = end.itemRow;         //如果起点在上,交换起点终点列号         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;     } } 接下来就是消除处理了,采用一些动画之类,此处略过,我们来讲解下落算法。下落算法有很多,我们采用的是逐个入位法。

    逐个入位法下落

        /// <summary>     /// Items下落     /// </summary>     /// <returns>The drop</returns>     IEnumerator ItemsDrop()     {         isOperation = true;         //逐列检测         for (int i = 0; i < tableColumn; i++)         {             //计数器             int count = 0;             //下落队列             Queue dropQueue = new Queue();             //逐行检测             for (int j = 0; j < tableRow; j++)             {                 if (allItems[j, i] != null)                 {                     //计数                     count++;                     //放入队列                     dropQueue.Enqueue(allItems[j, i]);                 }             }             //下落             for (int k = 0; k < count; k++)             {                 //获取要下落的Item                 Item current = dropQueue.Dequeue();                 //修改全局数组(原位置情况)                 allItems[current.itemRow, current.itemColumn] = null;                 //修改Item的行数                 current.itemRow = k;                 //修改全局数组(填充新位置)                 allItems[current.itemRow, current.itemColumn] = current;                 //下落                 current.GetComponent().                 CurrentItemDrop(allPos[current.itemRow, current.itemColumn]);             }         }         yield return new WaitForSeconds(0.2f);         StartCoroutine(CreateNewItem());         yield return new WaitForSeconds(0.2f);         AllBoom();     }     // 最后生成新的对象

        /// <summary>     /// 生成新的Item     /// </summary>     /// <returns>The new item</returns>     public IEnumerator CreateNewItem()     {         isOperation = true;         for (int i = 0; i < tableColumn; i++)         {             int count = 0;             Queue newItemQueue = new Queue();             for (int j = 0; j < tableRow; j++)             {                 if (allItems[j, i] == null)                 {                     //生成一个Item                     GameObject current = (GameObject)Instantiate(Resources.                     Load(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();                 //随机数                 int random = Random.Range(0, randomSprites.Length);                 //修改脚本中的图片                 currentItem.currentSpr = randomSprites[random];                 //修改真实图片                 currentItem.currentImg.sprite = randomSprites[random];                 //获取要移动的行数                 int r = tableRow - count + k;                 //移动                 currentItem.GetComponent().ItemMove(r, i, allPos[r, i]);             }         }         yield break;     } 当然如果两个图片交换后,无法消除要还原回原来位置

    这里写代码片    /// <summary>     /// Item交换     /// </summary>     /// <param name="dir">The exchange</param>     /// <returns>Dir</returns>     IEnumerator ItemExchange(Vector2 dir)     {         //获取目标行列         int targetRow = item.itemRow + System.Convert.ToInt32(dir.y);         int targetColumn = item.itemColumn + System.Convert.ToInt32(dir.x);         //检测合法         bool isLagal = GameController.instance.CheckRCLegal(targetRow, targetColumn);         if (!isLagal)         {             GameController.instance.isOperation = false;             //不合法跳出             yield break;         }         //获取目标         Item target = GameController.instance.allItems[targetRow, targetColumn];         //从全局列表中获取当前item,查看是否已经被消除,被消除后不能再交换         Item myItem = GameController.instance.allItems[item.itemRow, item.itemColumn];         if (!target || !myItem)         {             GameController.instance.isOperation = false;             //Item已经被消除             yield break;         }         //相互移动         target.GetComponent().ItemMove(item.itemRow, item.itemColumn, transform.position);         ItemMove(targetRow, targetColumn, target.transform.position);         //还原标志位         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);             //临时行列             int tempRow, tempColumn;             tempRow = myItem.itemRow;             tempColumn = myItem.itemColumn;             //移动             myItem.GetComponent().ItemMove(target.itemRow,             target.itemColumn, target.transform.position);             target.GetComponent().ItemMove(tempRow,             tempColumn, myItem.transform.position);             //延迟             yield return new WaitForSeconds(0.2f);             //操作完毕             GameController.instance.isOperation = false;         }     } 项目实践

    项目实践

    核心UML类图

    结束语  当然这个项目是最基础版,只有简单的消除操作,如果加上道具特效,算法会更多,以后在慢慢琢磨品鉴。最后奉上源码,这个项目下落及生成新对象的延迟时间还没有细调,调好后玩起来比较流畅。

    http://pan.baidu.com/s/1hrBfXdU 密码:6uqw


    最新回复(0)