三消游戏算法图文详解

来源:互联网 发布:淘宝商店转让 编辑:程序博客网 时间:2024/05/29 14:35

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

消除算法图文详解

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

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

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

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

原创粉丝点击