- 消除算法图文详解
- 三消算法首要实现的就是找到所有三个或三个以上的可消除对象,但直接找到这些对象是不太现实的,所以我们要将需求拆分。可不可以先获取所有图案相连的对象,进而在获取三消对象,这个算法也是众多三消游戏的一致实现。
- 获取图案相同的所有相连对象
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类图
结束语
当然这个项目是最基础版,只有简单的消除操作,如果加上道具特效,算法会更多,以后在慢慢琢磨品鉴。最后奉上源码,这个项目下落及生成新对象的延迟时间还没有细调,调好后玩起来比较流畅。