前言
目前的游戲市場(chǎng)可謂日漸蕭條,分分鐘就逼死眾多產(chǎn)品經(jīng)理,三消游戲可謂一把溫柔的彎刀朦肘,從女人這塊獲取到了一大片的市場(chǎng),動(dòng)不動(dòng)就做個(gè)幾百關(guān)双饥,相互之間還有攀比媒抠,果然女人的錢還是更好賺一些的。當(dāng)然咏花,三消游戲確實(shí)有很大的優(yōu)勢(shì)趴生,不浪費(fèi)太多時(shí)間,不那么燒腦昏翰,簡(jiǎn)單有趣苍匆。但如果要做一款集成性很高的三消游戲,對(duì)于開(kāi)發(fā)者而言也并不是那么簡(jiǎn)單棚菊,畢竟要用到很多算法浸踩,相比所謂的FPS、MMORPG有另一層次的深度统求。今天民轴,就給大家簡(jiǎn)單分享一下三消中的核心算法,以及在Unity中的實(shí)現(xiàn)球订。
- 消除算法圖文詳解
-
三消算法首要實(shí)現(xiàn)的就是找到所有三個(gè)或三個(gè)以上的可消除對(duì)象后裸,但直接找到這些對(duì)象是不太現(xiàn)實(shí)的,所以我們要將需求拆分冒滩∥⑹唬可不可以先獲取所有圖案相連的對(duì)象,進(jìn)而在獲取三消對(duì)象开睡,這個(gè)算法也是眾多三消游戲的一致實(shí)現(xiàn)因苹。
獲取圖案相同的所有相連對(duì)象
-
/// <summary>
/// 填充相同Item列表
/// </summary>
public void FillSameItemsList(Item current)
{
//如果已存在,跳過(guò)
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不合法篇恒,跳過(guò)
if (tempItemList [i] == null)
continue;
if (current.currentSpr == tempItemList [i].currentSpr) {
FillSameItemsList (tempItemList[i]);
}
}
}
-
獲取圖案相同的對(duì)象扶檐,一定要以一個(gè)對(duì)象為基準(zhǔn),這樣才能夠知道以誰(shuí)為中心胁艰,以這個(gè)中心為核心橫向及縱向的檢測(cè)款筑,檢測(cè)到三個(gè)及以上的對(duì)象智蝠,那說(shuō)明是可以消除的對(duì)象。
以檢測(cè)點(diǎn)為中心橫向縱向檢測(cè)
/// <summary>
/// 填充待消除列表
/// </summary>
/// <param name="current">Current.</param>
public void FillBoomList(Item current)
{
//計(jì)數(shù)器
int rowCount = 0;
int columnCount = 0;
//臨時(shí)列表
List<Item> rowTempList = new List<Item> ();
List<Item> columnTempList = new List<Item> ();
///橫向縱向檢測(cè)
foreach (var item in sameItemsList) {
//如果在同一行
if (item.itemRow == current.itemRow) {
//判斷該點(diǎn)與Curren中間有無(wú)間隙
bool rowCanBoom = CheckItemsInterval(true,current,item);
if (rowCanBoom) {
//計(jì)數(shù)
rowCount++;
//添加到行臨時(shí)列表
rowTempList.Add (item);
}
}
//如果在同一列
if (item.itemColumn == current.itemColumn) {
//判斷該點(diǎn)與Curren中間有無(wú)間隙
bool columnCanBoom = CheckItemsInterval(false,current,item);
if (columnCanBoom) {
//計(jì)數(shù)
columnCount++;
//添加到列臨時(shí)列表
columnTempList.Add (item);
}
}
}
//橫向消除
bool horizontalBoom = false;
//如果橫向三個(gè)以上
if (rowCount > 2) {
//將臨時(shí)列表中的Item全部放入BoomList
boomList.AddRange (rowTempList);
//橫向消除
horizontalBoom = true;
}
//如果縱向三個(gè)以上
if (columnCount > 2) {
if (horizontalBoom) {
//剔除自己
boomList.Remove (current);
}
//將臨時(shí)列表中的Item全部放入BoomList
boomList.AddRange (columnTempList);
}
//如果沒(méi)有消除對(duì)象奈梳,返回
if (boomList.Count == 0)
return;
//創(chuàng)建臨時(shí)的BoomList
List<Item> tempBoomList = new List<Item> ();
//轉(zhuǎn)移到臨時(shí)列表
tempBoomList.AddRange (boomList);
//開(kāi)啟處理BoomList的協(xié)程
StartCoroutine (ManipulateBoomList (tempBoomList));
}
-
當(dāng)然也有特殊情況杈湾,在游戲開(kāi)始時(shí),如沒(méi)有設(shè)置任何阻止同色的算法攘须,即有可能出現(xiàn)這種狀況漆撞,我們就要也采用一些算法去防止Bug出現(xiàn)。
跳躍同行同列Bug
/// <summary>
/// 檢測(cè)兩個(gè)Item之間是否有間隙(圖案不一致)
/// </summary>
/// <param name="isHorizontal">是否是橫向.</param>
/// <param name="begin">檢測(cè)起點(diǎn).</param>
/// <param name="end">檢測(cè)終點(diǎn).</param>
private bool CheckItemsInterval(bool isHorizontal,Item begin,Item end)
{
//獲取圖案
Sprite spr = begin.currentSpr;
//如果是橫向
if (isHorizontal) {
//起點(diǎn)終點(diǎn)列號(hào)
int beginIndex = begin.itemColumn;
int endIndex = end.itemColumn;
//如果起點(diǎn)在右于宙,交換起點(diǎn)終點(diǎn)列號(hào)
if (beginIndex > endIndex) {
beginIndex = end.itemColumn;
endIndex = begin.itemColumn;
}
//遍歷中間的Item
for (int i = beginIndex + 1; i < endIndex; i++) {
//異常處理(中間未生成浮驳,標(biāo)識(shí)為不合法)
if (allItems [begin.itemRow, i] == null)
return false;
//如果中間有間隙(有圖案不一致的)
if (allItems [begin.itemRow, i].currentSpr != spr) {
return false;
}
}
return true;
} else {
//起點(diǎn)終點(diǎn)行號(hào)
int beginIndex = begin.itemRow;
int endIndex = end.itemRow;
//如果起點(diǎn)在上,交換起點(diǎn)終點(diǎn)列號(hào)
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;
}
}
-
接下來(lái)就是消除處理了捞魁,采用一些動(dòng)畫之類至会,此處略過(guò),我們來(lái)講解下落算法署驻。下落算法有很多,我們采用的是逐個(gè)入位法健霹。
逐個(gè)入位法下落
/// <summary>
/// Items下落
/// </summary>
/// <returns>The drop.</returns>
IEnumerator ItemsDrop()
{
isOperation = true;
//逐列檢測(cè)
for (int i = 0; i < tableColumn; i++) {
//計(jì)數(shù)器
int count = 0;
//下落隊(duì)列
Queue<Item> dropQueue = new Queue<Item> ();
//逐行檢測(cè)
for (int j = 0; j < tableRow; j++) {
if (allItems [j, i] != null) {
//計(jì)數(shù)
count++;
//放入隊(duì)列
dropQueue.Enqueue(allItems [j, i]);
}
}
//下落
for (int k = 0; k < count; k++) {
//獲取要下落的Item
Item current = dropQueue.Dequeue ();
//修改全局?jǐn)?shù)組(原位置情況)
allItems[current.itemRow,current.itemColumn] = null;
//修改Item的行數(shù)
current.itemRow = k;
//修改全局?jǐn)?shù)組(填充新位置)
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 ();
}
- 最后生成新的對(duì)象
/// <summary>
/// 生成新的Item
/// </summary>
/// <returns>The new item.</returns>
public IEnumerator CreateNewItem()
{
isOperation = true;
for (int i = 0; i < tableColumn; i++) {
int count = 0;
Queue<GameObject> newItemQueue = new Queue<GameObject> ();
for (int j = 0; j < tableRow; j++) {
if (allItems [j, i] == null) {
//生成一個(gè)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 - 1, i];
newItemQueue.Enqueue (current);
count++;
}
}
for (int k = 0; k < count; k++) {
//獲取Item組件
Item currentItem = newItemQueue.Dequeue ().GetComponent<Item>();
//隨機(jī)數(shù)
int random = Random.Range (0, randomSprites.Length);
//修改腳本中的圖片
currentItem.currentSpr = randomSprites [random];
//修改真實(shí)圖片
currentItem.currentImg.sprite = randomSprites [random];
//獲取要移動(dòng)的行數(shù)
int r = tableRow - count + k;
//移動(dòng)
currentItem.GetComponent<ItemOperation> ().ItemMove (r,i,allPos[r,i]);
}
}
yield break;
}
- 當(dāng)然如果兩個(gè)圖片交換后旺上,無(wú)法消除要還原回原來(lái)位置
/// <summary>
/// Item交換
/// </summary>
/// <returns>The exchange.</returns>
/// <param name="dir">Dir.</param>
IEnumerator ItemExchange(Vector2 dir)
{
//獲取目標(biāo)行列
int targetRow = item.itemRow + System.Convert.ToInt32(dir.y);
int targetColumn = item.itemColumn + System.Convert.ToInt32(dir.x);
//檢測(cè)合法
bool isLagal = GameController.instance.CheckRCLegal (targetRow, targetColumn);
if (!isLagal) {
GameController.instance.isOperation = false;
//不合法跳出
yield break;
}
//獲取目標(biāo)
Item target = GameController.instance.allItems [targetRow, targetColumn];
//從全局列表中獲取當(dāng)前item,查看是否已經(jīng)被消除糖埋,被消除后不能再交換
Item myItem = GameController.instance.allItems [item.itemRow, item.itemColumn];
if (!target || !myItem) {
GameController.instance.isOperation = false;
//Item已經(jīng)被消除
yield break;
}
//相互移動(dòng)
target.GetComponent<ItemOperation> ().ItemMove (item.itemRow, item.itemColumn, transform.position);
ItemMove (targetRow, targetColumn, target.transform.position);
//還原標(biāo)志位
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);
//臨時(shí)行列
int tempRow, tempColumn;
tempRow = myItem.itemRow;
tempColumn = myItem.itemColumn;
//移動(dòng)
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;
}
}
-
項(xiàng)目實(shí)踐
項(xiàng)目實(shí)踐
核心UML類圖
結(jié)束語(yǔ)
當(dāng)然這個(gè)項(xiàng)目是最基礎(chǔ)版宣吱,只有簡(jiǎn)單的消除操作,如果加上道具特效瞳别,算法會(huì)更多征候,以后在慢慢琢磨品鑒。最后奉上源碼祟敛,這個(gè)項(xiàng)目下落及生成新對(duì)象的延遲時(shí)間還沒(méi)有細(xì)調(diào)疤坝,調(diào)好后玩起來(lái)比較流暢。(內(nèi)附價(jià)值50美元的資源包喔??????)鏈接: https://pan.baidu.com/s/1gfqpDmz 密碼: n9r9