匹配和掉落:
匹配算法:
public MatchItem MatchItems(GameObject item, Vector2 vector2)
{
MatchItem match;
//横向遍历到的所有相同元素
List<GameObject> matchRowItems = new List<GameObject>();
//纵向遍历到的所有相同元素
List<GameObject> matchLineItems = new List<GameObject>();
//最终可消除元素
List<GameObject> finishedMatchItems = new List<GameObject>();
//四方块的消除:
List<GameObject> otherRowItems = new List<GameObject>();
List<GameObject> otherLineItems = new List<GameObject>();
List<GameObject> finish= new List<GameObject>();
//先将选中的元素加入行匹配List
matchRowItems.Add(item);
matchLineItems.Add(item);
//横向遍历
//i=0代表往左遍历,i=1代表往右遍历
//Column为横向最大值
for (int i = 0; i <= 1; i++)
{
for (int xDistance = 1; xDistance <ConstClass.Column; xDistance++)
{
float x;
//←左遍历的时候:
if (i == 0)
{
x = vector2.y - xDistance;
}
//→右遍历的时候
else
{
// Debug.Log("右遍历");
x = vector2.y + xDistance;
}
//判断横向是否到达边界
if (x < 0 || x > ConstClass.Column)
{
break;
}
//判断是否为同一种元素 一个应该是itemX ???
if (objStore[vector2].GetComponent<ZYCell>().localTexture == objStore[new Vector2(vector2.x, x)].GetComponent<ZYCell>().localTexture)
{
//加入行list
matchRowItems.Add(objStore[new Vector2(vector2.x, x)]);
//Debug.Log(objStore[new Vector2(x, vector2.y)]);
}
else
{
break;
}
}
}
//竖向遍历
for (int i = 0; i <= 1; i++)
{
for (int yDistance = 1; yDistance <ConstClass.Row; yDistance++)
{
float y;
//上遍历的时候:
if (i == 0)
{
y = vector2.x - yDistance;
}
//下遍历的时候
else
{
// Debug.Log("下遍历");
y = vector2.x + yDistance;
}
//判断横向是否到达边界
if (y < 0 || y >ConstClass.Row)
{
break;
}
//判断是否为同一种元素 一个应该是itemX ???
if (objStore[vector2].GetComponent<ZYCell>().localTexture == objStore[new Vector2(y, vector2.y)].GetComponent<ZYCell>().localTexture)
{
//加入行list
matchLineItems.Add(objStore[new Vector2(y, vector2.y)]);
//Debug.Log(objStore[new Vector2(x, vector2.y)]);
}
else
{
break;
}
}
}
if (matchRowItems.Count == 2)
{
//四个方块正方形的检测
otherRowItems.AddRange(matchRowItems);
Vector2 vector2_1 = otherRowItems[0].GetComponent<ZYCell>().localPosition;
Vector2 vector2_2 = otherRowItems[1].GetComponent<ZYCell>().localPosition;
if (vector2_1.x > 0 && vector2_2.x < ConstClass.Row)
{
if (objStore[new Vector2(vector2_1.x + 1, vector2_1.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
if (objStore[new Vector2(vector2_2.x + 1, vector2_2.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
otherLineItems.Add(objStore[new Vector2(vector2_1.x + 1, vector2_1.y)]);
otherLineItems.Add(objStore[new Vector2(vector2_1.x + 1, vector2_2.y)]);
}
}
else if (objStore[new Vector2(vector2_1.x - 1, vector2_1.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
if (objStore[new Vector2(vector2_2.x - 1, vector2_2.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
otherLineItems.Add(objStore[new Vector2(vector2_1.x - 1, vector2_1.y)]);
otherLineItems.Add(objStore[new Vector2(vector2_1.x - 1, vector2_2.y)]);
}
}
}
else if (vector2_1.x == 0)
{
if (objStore[new Vector2(vector2_1.x + 1, vector2_1.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
if (objStore[new Vector2(vector2_2.x + 1, vector2_2.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
otherLineItems.Add(objStore[new Vector2(vector2_1.x + 1, vector2_1.y)]);
otherLineItems.Add(objStore[new Vector2(vector2_1.x + 1, vector2_2.y)]);
}
}
}
else if (vector2_2.x == ConstClass.Row)
{
if (objStore[new Vector2(vector2_1.x - 1, vector2_1.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
if (objStore[new Vector2(vector2_2.x - 1, vector2_2.y)].GetComponent<ZYCell>().localTexture.Equals(otherRowItems[0].GetComponent<ZYCell>().localTexture))
{
otherLineItems.Add(objStore[new Vector2(vector2_1.x - 1, vector2_1.y)]);
otherLineItems.Add(objStore[new Vector2(vector2_1.x - 1, vector2_2.y)]);
}
}
}
if (otherLineItems.Count >= 2 && otherRowItems.Count >= 2)
{
Debug.Log(otherLineItems[0]);
tempGo = otherLineItems[0];
Invoke("DoBoom",0.3f);
finishedMatchItems.AddRange(otherLineItems);
finishedMatchItems.AddRange(otherRowItems);
}
else
{
otherRowItems.Clear();
otherLineItems.Clear();
}
}
//测试横向删除
else if (matchRowItems.Count >= 3)
{
finishedMatchItems.AddRange(matchRowItems);
}
else
{
matchRowItems.Clear();
}
if (matchLineItems.Count >= 3)
{
finishedMatchItems.AddRange(matchLineItems);
}
else
{
matchLineItems.Clear();
}
if (finishedMatchItems.Count >= 3)
{
finish.AddRange(finishedMatchItems);
match = new MatchItem(true, finish);
}
else
{
finishedMatchItems.Clear();
match = new MatchItem(false, null);
}
matchRowItems.Clear();
matchLineItems.Clear();
finishedMatchItems.Clear();
return match;
}
思路很简单,就是遍历,遍历某个方块十字方向的方块,然后存储在相关的对应的数组里面,最后综合起来,计算长度,判断数量,完成了相关的匹配,匹配以后一定要清空相关的数组。
这里有大佬提供了另一种思路,就是将每一种可以消除的形状,存储在一个配置表里面,采用枚举的方法去看整个图里面有没有和形状相符的连接体,这个思路和代码会更简单,虽然看上去似乎你觉得这个方法有点平淡无奇,觉得枚举是个很小白的操作,但是这种方法后期的扩展性会变得非常好,假如后期要加入一些比较奇怪的形状进行扩展,使用复杂的算法的时候后期就需要去修改你的算法,很容易出错,但是这种配置的思路,就只需要简简单单加一个配置形状就行,扩展性非常好,值得研究。
掉落算法:
public void Drop()
{
for (int i = 0; i <= ConstClass.Column; i++)
{
for (int j = ConstClass.Row; j >= 0; j--)
{
ZYCell DisShow = null;
ZYCell Show = null;
if (objStore[new Vector2(j, i)].GetComponent<ZYCell>().localTexture == null && j != 0)
{
DisShow = objStore[new Vector2(j, i)].GetComponent<ZYCell>();
for (int k = j; k >= 0; k--)
{
if (objStore[new Vector2(k, i)].GetComponent<ZYCell>().localTexture != null)
{
Show = objStore[new Vector2(k, i)].GetComponent<ZYCell>();
//更新个体内部数据
DoExchange(DisShow, Show);
MoveList.Add(Show);
MoveList.Add(DisShow);
}
}
}
}
}
ConstClass.combo++;
foreach (var item in MoveList)
{
item.MoveBack();
}
MoveList.Clear();
Invoke("ChangeTexture", 0.3f);
StartCoroutine("Judge");
}
IEnumerator Judge()
{
yield return new WaitForSeconds(0.5f);
MatchItem match_1;
List<GameObject> finishList = new List<GameObject>();
foreach (var item in objStore.Values)
{
item.GetComponent<RawImage>().raycastTarget = false;
//判断是否可以消除
match_1 =MatchItems(item, item.GetComponent<ZYCell>().localPosition);
if (match_1.finish != null)
{
//Debug.Log(item.name);
list.Add(item);
}
}
if (list.Count != 0)
{
foreach (var item in list)
{
if (item.GetComponent<ZYCell>().boomFlag == true)
{
for (int i = 0; i <= ConstClass.Row; i++)
{
for (int j = 0; j <= ConstClass.Column; j++)
{
if (objStore[new Vector2(i, j)].GetComponent<ZYCell>().localPosition.x == item.GetComponent<ZYCell>().localPosition.x || objStore[new Vector2(i, j)].GetComponent<ZYCell>().localPosition.y == item.GetComponent<ZYCell>().localPosition.y)
{
objStore[new Vector2(i, j)].transform.DOScale(new Vector3(0f, 0f, 0f), 0.1f);
objStore[new Vector2(i, j)].GetComponent<RawImage>().color = Color.black;
objStore[new Vector2(i, j)].GetComponent<ZYCell>().localTexture = null;
objStore[new Vector2(i, j)].transform.SetAsFirstSibling();
}
}
}
item.GetComponent<ZYCell>().boomFlag = false;
item.transform.DOScale(new Vector3(1, 1, 1), 0.3f);
}
else
{
item.GetComponent<RawImage>().color = Color.black;
item.GetComponent<ZYCell>().localTexture = null;
item.transform.SetAsFirstSibling();
item.transform.DOScale(new Vector3(0f, 0f, 0f), 0.3f);
ConstClass.count++;
}
}
list.Clear();
Drop();
}
else
{
Invoke("ClearCombo", 0.5f);
}
}
public void ChangeTexture()
{
for (int p = 0; p <= ConstClass.Row; p++)
{
for (int q = 0; q <= ConstClass.Column; q++)
{
if (objStore[new Vector2(p, q)].GetComponent<ZYCell>().localTexture == null)
{
int cellTexture2 = Random.Range(1, 6);
Texture newTexture = Resources.Load<Texture>("Texture/" + cellTexture2.ToString());
objStore[new Vector2(p, q)].GetComponent<RawImage>().texture = newTexture;
objStore[new Vector2(p, q)].GetComponent<ZYCell>().localTexture = objStore[new Vector2(p, q)].GetComponent<RawImage>().texture;
objStore[new Vector2(p, q)].GetComponent<RawImage>().color = Color.white;
objStore[new Vector2(p, q)].transform.DOScale(new Vector3(1f,1f,1f),0.3f);
objStore[new Vector2(p, q)].transform.SetAsLastSibling();
}
}
}
}
掉落的整体思路其实很多,可以看到之前我们消除的时候其实不是真正的把消除物体Destroy掉,而是将上面的材质和颜色进行替换,内部的材质数据变成null,所以这里的思路就是当遇到内部材质数据是null的物体的时候,就往上寻找第一个材质不为空的物体,然后进行内部数据的更新和位置的交换,以此类推。如果上面没有物体了,那这个空物体就会自动生成一个材质,进行更换。其中Judge方法是干嘛的呢?当方块掉落之后,整个场景会重新出现可以消除的物体,这个时候就需要进行一个类似递归的操作,对整个场景进行遍历,有可以消除的,再次消除,然后掉落,直到最后没有可以消除的时候,跳出整个循环。这里一定要注意要有相应的跳出循环判断,不然整个unity会瞬间卡死,只能任务管理器来关闭了。
同时需要注意一个问题,在有数据交换的时候尽量不要在循环里面使用Dotween,会产生很多莫名其妙的bug,个人分析可能是因为dotween动画是一个过程,而在这个过程中,数据交换其实很快就会完成,然后继续向下个循环或者其他地方进行,不会等待动画完成,哪怕你使用了OnComplete方法也不行,所以最好将使用dotween方法的地方想办法移出循环。