三消游戏算法图文详解

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

消除算法图文详解

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

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

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

// 填充相同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

跳跃同行同列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类图

核心UML类图

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

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

  • 13
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小温同学的账号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值