• 消除算法图文详解

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

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

    以检测点为中心横向纵向检测
     /// <summary>
    /// 填充待消除列表
    /// </summary>
    /// <param name="current">Current.</param>
    public void FillBoomList(Item current)
    {
    //计数器
    int rowCount = ;
    int columnCount = ;
    //临时列表
    List<Item> rowTempList = new List<Item> ();
    List<Item> columnTempList = new List<Item> ();
    ///横向纵向检测
    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 > ) {
    //将临时列表中的Item全部放入BoomList
    boomList.AddRange (rowTempList);
    //横向消除
    horizontalBoom = true;
    }
    //如果纵向三个以上
    if (columnCount > ) {
    if (horizontalBoom) {
    //剔除自己
    boomList.Remove (current);
    }
    //将临时列表中的Item全部放入BoomList
    boomList.AddRange (columnTempList);
    }
    //如果没有消除对象,返回
    if (boomList.Count == )
    return;
    //创建临时的BoomList
    List<Item> tempBoomList = new List<Item> ();
    //转移到临时列表
    tempBoomList.AddRange (boomList);
    //开启处理BoomList的协程
    StartCoroutine (ManipulateBoomList (tempBoomList));
    }
    • 当然也有特殊情况,在游戏开始时,如没有设置任何阻止同色的算法,即有可能出现这种状况,我们就要也采用一些算法去防止Bug出现。

      跳跃同行同列Bug
       /// <summary>
      /// 检测两个Item之间是否有间隙(图案不一致)
      /// </summary>
      /// <param name="isHorizontal">是否是横向.</param>
      /// <param name="begin">检测起点.</param>
      /// <param name="end">检测终点.</param>
      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 + ; 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 + ; 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 = ; i < tableColumn; i++) {
      //计数器
      int count = ;
      //下落队列
      Queue<Item> dropQueue = new Queue<Item> ();
      //逐行检测
      for (int j = ; j < tableRow; j++) {
      if (allItems [j, i] != null) {
      //计数
      count++;
      //放入队列
      dropQueue.Enqueue(allItems [j, i]);
      }
      }
      //下落
      for (int k = ; 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<ItemOperation>().
      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 = ; i < tableColumn; i++) {
      int count = ;
      Queue<GameObject> newItemQueue = new Queue<GameObject> ();
      for (int j = ; j < tableRow; j++) {
      if (allItems [j, i] == null) {
      //生成一个Item
      GameObject current = (GameObject)Instantiate(Resources.
      Load<GameObject> (Util.ResourcesPrefab + Util.Item));
      // ObjectPool.instance.GetGameObject (Util.Item, transform);
      current.transform.parent = transform;
      current.transform.position = allPos [tableRow - , i];
      newItemQueue.Enqueue (current);
      count++;
      }
      }
      for (int k = ; k < count; k++) {
      //获取Item组件
      Item currentItem = newItemQueue.Dequeue ().GetComponent<Item>();
      //随机数
      int random = Random.Range (, randomSprites.Length);
      //修改脚本中的图片
      currentItem.currentSpr = randomSprites [random];
      //修改真实图片
      currentItem.currentImg.sprite = randomSprites [random];
      //获取要移动的行数
      int r = tableRow - count + k;
      //移动
      currentItem.GetComponent<ItemOperation> ().ItemMove (r,i,allPos[r,i]);
      }
      }
      yield break;
      }
    • 当然如果两个图片交换后,无法消除要还原回原来位置
       /// <summary>
      /// Item交换
      /// </summary>
      /// <returns>The exchange.</returns>
      /// <param name="dir">Dir.</param>
      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<ItemOperation> ().ItemMove (item.itemRow, item.itemColumn, transform.position);
      ItemMove (targetRow, targetColumn, target.transform.position);
      //还原标志位
      bool reduction = false;
      //消除处理
      item.CheckAroundBoom();
      if (GameController.instance.boomList.Count == ) {
      reduction = true;
      }
      target.CheckAroundBoom ();
      if (GameController.instance.boomList.Count != ) {
      reduction = false;
      }
      //还原
      if (reduction) {
      //延迟
      yield return new WaitForSeconds (0.2f);
      //临时行列
      int tempRow, tempColumn;
      tempRow = myItem.itemRow;
      tempColumn = myItem.itemColumn;
      //移动
      myItem.GetComponent<ItemOperation> ().ItemMove (target.itemRow,
      target.itemColumn, target.transform.position);
      target.GetComponent<ItemOperation> ().ItemMove (tempRow,
      tempColumn, myItem.transform.position);
      //延迟
      yield return new WaitForSeconds (0.2f);
      //操作完毕
      GameController.instance.isOperation = false;
      }
      }
  • 项目实践

    项目实践

    核心UML类图
     

    结束语

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

    Unity三消算法的更多相关文章

    1. unity导弹算法 预计目标点

      关于导弹的飞行算法,网上有很多教程.简单算法无非是获取目标点的当前位置,然后导弹朝目标方向移动.高深点的,就是通过计算获取碰撞点然后朝着目标移动.如果你能看懂这个高深算法的话,可以去看原帖:http: ...

    2. [Unity A*算法]A*算法的简单实现

      写在前面:之前看过一点,然后看不懂,也没用过. 最近正好重构项目看到寻路这块,想起来就去查查资料,总算稍微理解一点了,下面记录一下自己的成果(哈哈哈 :> ) 下面分享几篇我觉得挺不错的文章 A ...

    3. C++ 三消游戏基本实现

      最近在研究三消算法,我想试试在完全不借助网络资源的情况下搞定这个东西,所以有些地方可能不是最优的. 代码留此备忘. 1. 3x_desk_event.h 1 #pragma once 2 3 #ifn ...

    4. 由浅入深学习PBR的原理和实现

      目录 一. 前言 1.1 本文动机 1.2 PBR知识体系 1.3 本文内容及特点 二. 初阶:PBR基本认知和应用 2.1 PBR的基本介绍 2.1.1 PBR概念 2.1.2 与物理渲染的差别 2 ...

    5. 第04组 Alpha冲刺(4/6)

      队名:new game 组长博客:戳 作业博客:戳 组员情况 鲍子涵(队长) 燃尽图 过去两天完成了哪些任务 才两天,也就是实现一些功能而已 复习 接下来的计划 实现更多的功能 为这周的比赛准备 还剩 ...

    6. 第04组 Alpha冲刺(3/6)

      队名:new game 组长博客:戳 作业博客:戳 组员情况 鲍子涵(队长) 燃尽图 过去两天完成了哪些任务 才两天,也就是实现一些功能而已 复习 接下来的计划 实现更多的功能 为下周的比赛准备 还剩 ...

    7. Alpha冲刺(3/6)

      队名:new game 组长博客:戳 作业博客:戳 组员情况 鲍子涵(队长) 燃尽图 过去两天完成了哪些任务 才两天,也就是实现一些功能而已 复习 接下来的计划 实现更多的功能 为下周的比赛准备 还剩 ...

    8. Unity 网络斗地主 牌的一些算法

      Unity 网络斗地主  牌的一些算法 在这儿说一下,我的项目是用svn的方式,上传在https://v2.svnspot.com/18666451713.doudizhu这个svn上,大家可以下载T ...

    9. A星寻路算法入门(Unity实现)

      最近简单学习了一下A星寻路算法,来记录一下.还是个萌新,如果写的不好,请谅解.Unity版本:2018.3.2f1 A星寻路算法是什么 游戏开发中往往有这样的需求,让玩家控制的角色自动寻路到目标地点, ...

    随机推荐

    1. wcf会话、实例化、并发

      在asp.net中含有会话,是保存值,供所有的程序使用,同样在wcf中也有会话,供多个客户端使用. 会话的支持通常在契约定义的开始标出,如下 [ServiceContract(Namespace = ...

    2. Android 仿微信的朋友圈发布(1)

      想自己做一个APP,然后等做到类似发布朋友圈的,微博状态的时候,遇到问题了,完全不知道咋下手. 什么访问相册,列出照片,选择照片进行回传,完全不懂,咋办,自己写?自己写是不可能自己写的,打屎都不可能自 ...

    3. AutoLayout——何为intrinsic content size

      上一篇说到了约束就是等式和不等式.仅仅知道其原理还是没法拉出符合需求的线.所以这一篇主要看来什么是intrinsic content size,以及它有什么用. 在Xcode中,差点儿全部控件或视图, ...

    4. WPF中的数据驱动

      在WPF出现之前,Win Form,ASP.NET等技术均采用“事件驱动”理念,这种由“事件-订阅-事件处理器”关系交织在一起构成的程序,景观可以使用MVC.MVP等设计模式,但一不小心就会使界面逻辑 ...

    5. <股市高手和你想的不一样>读书笔记

      书在这里 在股市中挖掘真正有成长潜力的好企业,是成功投资者的关键 股票被低估的时候,才值得买 我们买股票,就是买这家公司的未来 公司的成长性要重点看两个方面,一个方面要看该公司近三年的成长趋势,另外一 ...

    6. Postgresql分表与优化

      --1.创建主表 CREATE TABLE tbl_partition ( date_key date, hour_key smallint, client_key integer, item_key ...

    7. sqoop 常用命令整理(二)

      26.Validate 它用来比较源数据和目标数据的数量 它有三个接口 Validator. 它有三个接口 Validator. Property: validator Description: Dr ...

    8. jce_policy安装【java密码扩展无限制权限策略文件安装】

      下载与JDK或JRE对应版本的jce文件包,当前机器的jdk为1.8,所以下载jce_policy-8.zip. 下载地址:http://www.oracle.com/technetwork/java ...

    9. IntelliJ: Maven projects need to be imported: Import Changes Enable Auto-Import

      upon creating a new maven project I get the popup box saying "Maven projects need to be importe ...

    10. k8s 题目

      这几个月参与了几场面试,设计了多道面试题,觉得可以综合考察应聘人对 kubernetes的掌握情况.在这里分享下,供应聘人自查以及其他面试官参考. 这些面试题的设计初衷并不是考察 kubernetes ...