章國(guó)雁
(安徽工商職業(yè)學(xué)院 信息工程學(xué)院,安徽 合肥 231131)
隨著游戲行業(yè)的發(fā)展,中國(guó)游戲用戶的數(shù)量呈現(xiàn)爆發(fā)式的增長(zhǎng).新型冠狀病毒肺炎疫情雖然影響了一些行業(yè)的發(fā)展,卻促進(jìn)了游戲行業(yè)的進(jìn)一步發(fā)展.隨著游戲行業(yè)的發(fā)展,出現(xiàn)了各類(lèi)型的游戲,如射擊類(lèi)、休閑類(lèi)、動(dòng)作類(lèi)、RPG類(lèi)、解密類(lèi)等.在各類(lèi)型的游戲中,休閑娛樂(lè)類(lèi)的輕度游戲一直受到用戶的歡迎,因?yàn)楝F(xiàn)代社會(huì)工作節(jié)奏快、壓力大,人們希望在工作間隙能夠有個(gè)適度的放松休閑.休閑類(lèi)游戲由于較好利用了用戶的碎片化時(shí)間,其游戲玩法簡(jiǎn)單、快速上手,因此擁有龐大的用戶群體.
近些年,不少學(xué)者對(duì)使用游戲引擎進(jìn)行游戲開(kāi)發(fā)做了較多的研究,李昊宇[1]在基于Unity3D的橫版過(guò)關(guān)游戲一文中介紹了經(jīng)典的過(guò)關(guān)游戲“馬里奧”,并在之前的傳統(tǒng)玩法基礎(chǔ)上加入了新的玩法和創(chuàng)意;孟子權(quán)等[2]在基于Unity3D的環(huán)境保護(hù)類(lèi)游戲研究與實(shí)現(xiàn)一文中提出了游戲與環(huán)境保護(hù)主題相結(jié)合的創(chuàng)意設(shè)計(jì),讓用戶在玩游戲的同時(shí)更多地關(guān)注環(huán)境保護(hù);張勝男等[3]在基于Unity3D的尋寶游戲的設(shè)計(jì)與制作一文中實(shí)現(xiàn)了以各類(lèi)生活知識(shí)為線索、以拓展知識(shí)為目的的尋寶游戲,兼顧了娛樂(lè)和教育的內(nèi)容;陳麗梅等[4]分析了基于Unreal Engine的第三人稱(chēng)動(dòng)作游戲的設(shè)計(jì)與實(shí)現(xiàn);郭建軍等[5]、岳書(shū)丹等[6]、付夢(mèng)遠(yuǎn)等[7]對(duì)使用Unity3D引擎制作塔防類(lèi)游戲進(jìn)行了研究與實(shí)現(xiàn).以上學(xué)者的研究成果給游戲開(kāi)發(fā)從業(yè)人員提供了新的創(chuàng)意和思路.
本文以Unity3D游戲引擎為開(kāi)發(fā)平臺(tái),使用C#程序設(shè)計(jì)語(yǔ)言,介紹一款三消類(lèi)休閑游戲“寶石迷陣”的算法設(shè)計(jì)及實(shí)現(xiàn)步驟.游戲以寶石為主題,通過(guò)移動(dòng)交換位置相鄰的2個(gè)寶石,當(dāng)相鄰的3個(gè)或3個(gè)以上寶石類(lèi)型匹配相同時(shí),系統(tǒng)自動(dòng)消除寶石并給予相應(yīng)獎(jiǎng)勵(lì),當(dāng)獎(jiǎng)勵(lì)達(dá)到滿足條件時(shí)給予通關(guān).
為了增加游戲的趣味性,“寶石迷陣”游戲提供9種不同類(lèi)型的寶石樣式,剛開(kāi)始運(yùn)行時(shí),初始界面隨機(jī)產(chǎn)生7行10列不同類(lèi)型的寶石矩陣,如圖1所示.設(shè)計(jì)思路:(1)使用普通數(shù)組gemstoneBgs[]存儲(chǔ)9種不同類(lèi)型的寶石樣式,當(dāng)寶石被生成時(shí),從數(shù)組中隨機(jī)產(chǎn)生一種寶石樣式;(2)使用動(dòng)態(tài)數(shù)組gemstoneList來(lái)存儲(chǔ)游戲運(yùn)行時(shí)產(chǎn)生的7行10列共70個(gè)寶石,可以通過(guò)一個(gè)雙重循環(huán)語(yǔ)句把寶石順序存儲(chǔ)在動(dòng)態(tài)數(shù)組中,由于動(dòng)態(tài)數(shù)組可以動(dòng)態(tài)增加或減少數(shù)組的大小,可以很好地適應(yīng)游戲運(yùn)行過(guò)程中寶石被消除和生成時(shí)的數(shù)量動(dòng)態(tài)變化,具有較好的擴(kuò)展性.
圖1 隨機(jī)產(chǎn)生不同類(lèi)型寶石矩陣
要實(shí)現(xiàn)寶石的交換,需要同時(shí)滿足以下條件:(1)第一次點(diǎn)擊的寶石和第二次點(diǎn)擊的寶石進(jìn)行交換,第二次點(diǎn)擊的寶石不能和第三次點(diǎn)擊的寶石交換,即以2次點(diǎn)擊為一個(gè)交換循環(huán),交換過(guò)后重新開(kāi)始;(2)相互交換的寶石必須在位置上屬于水平方向上相鄰或者垂直方向上相鄰.針對(duì)第一個(gè)條件,本文通過(guò)設(shè)定一個(gè)臨時(shí)變量currentGemstone來(lái)保存第一次被點(diǎn)擊的寶石.當(dāng)寶石第一次被點(diǎn)擊時(shí),判斷currentGemstone的值,如果為空,則存儲(chǔ)當(dāng)前被點(diǎn)擊的寶石;如果不為空,則進(jìn)入第二個(gè)條件判斷.針對(duì)第二個(gè)條件的解決思路為:當(dāng)?shù)谝淮伪稽c(diǎn)擊的寶石,即currentGemstone里存儲(chǔ)的寶石和第二次被點(diǎn)擊的寶石進(jìn)行交換前,需要計(jì)算出兩個(gè)寶石的行坐標(biāo)差值和列坐標(biāo)差值的和;當(dāng)行差值和列差值二者和為1時(shí),表示兩個(gè)寶石處于行相鄰或者列相鄰,可以進(jìn)行交換操作,否則不滿足第二個(gè)條件,不能進(jìn)行寶石交換.
當(dāng)寶石被初始化生成或者被點(diǎn)擊交換后,程序需要檢測(cè)是否有相同類(lèi)型的寶石,發(fā)現(xiàn)有相同的寶石則進(jìn)行消除.相同類(lèi)型寶石的判斷條件為:同一行里面的寶石超過(guò)3個(gè)相鄰的寶石為相同類(lèi)型;或者同一列里面的寶石超過(guò)3個(gè)相鄰的寶石為相同類(lèi)型.針對(duì)以上條件,程序?qū)崿F(xiàn)的思路為:(1)首先判斷每一行的寶石.通過(guò)使用雙重循環(huán)語(yǔ)句,外循環(huán)為遍歷每一行,內(nèi)循環(huán)為遍歷當(dāng)前行的每一列,取出當(dāng)前列的寶石和當(dāng)前列后一位(即當(dāng)前列的值+1)的寶石、當(dāng)前列的寶石和當(dāng)前列后兩位(即當(dāng)前列的值+2)的寶石倆倆進(jìn)行比較,如果寶石的類(lèi)型相同,則判斷參與比較的相鄰3個(gè)寶石為當(dāng)前行相同類(lèi)型的寶石,暫時(shí)保存相同類(lèi)型的寶石到matchesGemstone動(dòng)態(tài)數(shù)組中.(2)接著判斷每一列的寶石.再次通過(guò)使用雙重循環(huán)語(yǔ)句,外循環(huán)為遍歷每一列,內(nèi)循環(huán)為遍歷當(dāng)前列的每一行,取出當(dāng)前行的寶石和當(dāng)前行上面一位(即當(dāng)前行的值+1)的寶石、當(dāng)前行的寶石和當(dāng)前行上面兩位(即當(dāng)前行的值+2)的寶石倆倆進(jìn)行比較,如果寶石的類(lèi)型相同,則判斷參與比較的相鄰3個(gè)寶石為當(dāng)前列相同類(lèi)型的寶石,暫時(shí)保存相同類(lèi)型的寶石到matchesGemstone動(dòng)態(tài)數(shù)組中.
通過(guò)程序檢測(cè)到相同類(lèi)型的寶石后,不能馬上消除寶石,因?yàn)榭赡軙?huì)存在3個(gè)以上相鄰的寶石或者行列交叉都相同的寶石情況,程序需要先暫時(shí)保存所有相同的寶石到matchesGemstone動(dòng)態(tài)數(shù)組,待所有行和所有列全部檢測(cè)結(jié)束后,統(tǒng)一進(jìn)行消除.當(dāng)相同的寶石被消除后,游戲需要及時(shí)進(jìn)行寶石的增補(bǔ),便于玩家進(jìn)行下一次的操作.寶石的消除和增補(bǔ)功能實(shí)現(xiàn)思路為:(1)循環(huán)遍歷保存著相同類(lèi)型寶石的matchesGemstone動(dòng)態(tài)數(shù)組,消除遍歷到的當(dāng)前寶石;(2)讀取當(dāng)前被消除的寶石所在列,循環(huán)遍歷該列的每一個(gè)寶石,從當(dāng)前寶石的上一行開(kāi)始,向下一行移動(dòng)一個(gè)位置,從下往上依次對(duì)該列的所有寶石做位移操作后,在該列的最上方空出一個(gè)位置等待寶石的增補(bǔ);(3)實(shí)例化一個(gè)新的寶石物體,從寶石樣式數(shù)組gemstoneBgs[]中隨機(jī)取出一個(gè)寶石,放在空出的位置上.通過(guò)以上步驟實(shí)現(xiàn)了寶石消除和增補(bǔ)的算法.
基于上述的程序算法和實(shí)現(xiàn)思路,在Unity3D引擎中實(shí)現(xiàn)寶石迷陣游戲案例的制作,其關(guān)鍵節(jié)點(diǎn)的實(shí)現(xiàn)步驟如下:
在Unity3D引擎的Project窗口中,導(dǎo)入本案例的資源包,包含有9張不同效果的寶石圖片和1張游戲背景圖片,背景音樂(lè)和寶石交換、匹配、銷(xiāo)毀、生成時(shí)的各種音效.將9張寶石圖片類(lèi)型從普通Texture修改為精靈Sprite(2D and UI)類(lèi)型.為了增強(qiáng)游戲界面中寶石的顯示效果,適當(dāng)放大每個(gè)寶石的大小,依次修改每個(gè)寶石圖片X軸和Y軸的縮放項(xiàng)數(shù)值為1.2,保存每個(gè)寶石圖片為預(yù)制體,如圖2所示.
圖2 9種類(lèi)型的寶石預(yù)制體
游戲初始界面需要生成一個(gè)7行10列的寶石矩陣,如圖1所示.通過(guò)一個(gè)雙重循環(huán)語(yǔ)句保存生成的寶石,外循環(huán)語(yǔ)句保存每一行的寶石,內(nèi)循環(huán)語(yǔ)問(wèn)保存當(dāng)前行中遍歷的每一列寶石,其部分核心代碼如下:
public Gemstone gemstone;//寶石變量
public int rowNum = 7;//行數(shù)
public int columnNum = 10;//列數(shù)
public ArrayList gemstoneList;//動(dòng)態(tài)數(shù)組,用來(lái)保存生成的寶石
void Start () {
gemstoneList = new ArrayList ();//數(shù)組的初始化
//雙重循環(huán)語(yǔ)句順序存儲(chǔ)每一個(gè)生成的寶石
for (int rowIndex = 0;rowIndex < rowNum;rowIndex++) {
//臨時(shí)動(dòng)態(tài)數(shù)組,用來(lái)存儲(chǔ)當(dāng)前行的每一列寶石
ArrayList temp = new ArrayList();
for(int columnIndex=0;columnIndex < columnNum;columnIndex++){
Gemstone gemstone = AddGemstone(rowIndex,columnIndex);
temp.Add (gemstone);
}
gemstoneList.Add (temp);
}
}
每個(gè)寶石在生成時(shí)需要隨機(jī)產(chǎn)生一個(gè)類(lèi)型.通過(guò)實(shí)例化函數(shù)Instantiate()生成寶石,通過(guò)隨機(jī)讀取寶石類(lèi)型數(shù)組gemstoneBgs[]的值產(chǎn)生不同的寶石.其部分核心代碼如下:
//生成寶石函數(shù)
public Gemstone AddGemstone(int rowIndex,int columnIndex){
Gemstone gemstone = Instantiate (gemstone) as Gemstone;//實(shí)例化生成寶石
gemstone.transform.parent = this.transform;//指定位置
gemstone.RandomCreateGemstoneBg();//生成隨機(jī)的寶石類(lèi)型
return gemstone;
}
//生成隨機(jī)的寶石類(lèi)型函數(shù)
public void RandomCreateGemstoneBg(){
gemstoneType = Random.Range (0,gemstoneBgs.Length);//隨機(jī)產(chǎn)生一個(gè)0至數(shù)組長(zhǎng)度之間的整數(shù)值
gemstoneBg = Instantiate(gemstoneBgs[gemstoneType]) as GameObject;//產(chǎn)生一種寶石的樣式
gemstoneBg.transform.parent = this.transform;//指定位置
}
當(dāng)寶石第一次被點(diǎn)擊時(shí),需要存儲(chǔ)到currentGemstone變量中,等待和下一次被點(diǎn)擊的寶石進(jìn)行交換,同時(shí)在交換之前需要判斷兩者是否在同一行相鄰或者同一列相鄰,其部分核心代碼如下:
//點(diǎn)擊寶石函數(shù)
public void Select(Gemstone gemstone){
if (currentGemstone == null) {//第一次點(diǎn)擊
currentGemstone = gemstone;//保存當(dāng)前點(diǎn)擊的寶石
currentGemstone.isSelected = true;//標(biāo)記該寶石為已選中
return;
}else{//第二次點(diǎn)擊
if( Mathf.Abs(currentGemstone.rowIndex - gemstone.rowIndex)+Mathf.Abs(currentGemstone.columnIndex - gemstone.columnIndex) == 1 ){
//如果寶石相鄰,則進(jìn)行交換
StartCoroutine (ExangeAndMatches(currentGemstone,gemstone));
}else{
audio.PlayOneShot(errorClip);//播放交換失敗的音效
}
currentGemstone.isSelected = false;//重置標(biāo)志位
currentGemstone = null;//清空
}
}
根據(jù)上述2.3節(jié)的判斷條件,程序先后檢測(cè)行方向和列方向的所有寶石類(lèi)型,即寶石的gemstoneType字段值,該字段為整型,如果參與比較的寶石gemstoneType相同,則為相同類(lèi)型的寶石.以檢測(cè)每一行的寶石為例,其部分代碼如下:
bool CheckHorizontalMatches(){//實(shí)現(xiàn)檢測(cè)水平方向的寶石類(lèi)型
bool isMatches = false;
for (int rowIndex = 0;rowIndex < rowNum;rowIndex++) {//遍歷行
for(int columnIndex =0;columnIndex < columnNum - 2;columnIndex++){//遍歷列
//判斷相鄰寶石的gemstoneType值
if((GetGemstone (rowIndex,columnIndex).gemstoneType == GetGemstone (rowIndex,columnIndex+1).gemstoneType ) && (GetGemstone (rowIndex,columnIndex).gemstoneType == GetGemstone (rowIndex,columnIndex+2).gemstoneType )){
AddMatches (GetGemstone (rowIndex,columnIndex));//存儲(chǔ)相同寶石
AddMatches (GetGemstone (rowIndex,columnIndex+1));//存儲(chǔ)相同寶石
AddMatches (GetGemstone (rowIndex,columnIndex+2));//存儲(chǔ)相同寶石
isMatches = true;
}
}
}
return isMatches;
}
程序檢測(cè)完成,如果發(fā)現(xiàn)相同類(lèi)型的寶石,則需要進(jìn)行消除操作,使用循環(huán)語(yǔ)句遍歷matchesGemstone動(dòng)態(tài)數(shù)組,刪除該數(shù)組保存的每一個(gè)元素(寶石).寶石刪除后,需要移動(dòng)寶石的位置進(jìn)行填充,并生成新的寶石.以消除為例其部分核心代碼如下:
//遍歷matchesGemstone動(dòng)態(tài)數(shù)組消除寶石
void RemoveMatches(){
for(int i=0;i< matchesGemstone.Count;i++){
Gemstone gemstone = matchesGemstone[i] as Gemstone;
RemoveGemstone(gemstone);//調(diào)用消除寶石函數(shù)
}
matchesGemstone = new ArrayList ();
}
寶石迷陣游戲案例注重三消類(lèi)游戲的算法設(shè)計(jì)與實(shí)現(xiàn)過(guò)程,使用Unity3D游戲引擎作為開(kāi)發(fā)平臺(tái),后期可以發(fā)布到普通PC電腦、Mac電腦、安卓、IOS、平板、游戲主機(jī)等多種平臺(tái),使本游戲案例具備較好的跨平臺(tái)特性.本案例游戲算法的設(shè)計(jì)思路對(duì)于游戲開(kāi)發(fā)的教學(xué)和行業(yè)應(yīng)用有積極的促進(jìn)作用.