三消遊戲演算法圖文詳解
阿新 • • 發佈:2019-01-06
之前小編查詢發的資料小編本人也不太理解,所以這裡又找了一個講的個很詳細的文章,整理過後發出來大家一起分享!
消除演算法圖文詳解
三消演算法首要實現的就是找到所有三個或三個以上的可消除物件,但直接找到這些物件是不太現實的,所以我們要將需求拆分。可不可以先獲取所有圖案相連的物件,進而在獲取三消物件,這個演算法也是眾多三消遊戲的一致實現。
獲取圖案相同的所有相連物件
// 填充相同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
/// <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類圖
結束語
當然這個專案是最基礎版,只有簡單的消除操作,如果加上道具特效,演算法會更多,以後在慢慢琢磨品鑑。最後奉上原始碼,這個專案下落及生成新物件的延遲時間還沒有細調,調好後玩起來比較流暢。