目錄
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 1
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 2 - 聯(lián)機模式
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 3 - 界面布局
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 4 - 卡組系統(tǒng)
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 5 - 卡片選中系統(tǒng)
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 6 - 卡片放置,戰(zhàn)場更新
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 7 - 墓地,副控制面板
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 8 - 返回手卡,卡組
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 9 - 實現(xiàn)簡單 websocket 通信
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 10 - 搭建游戲服務(wù)端
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 11 - 客戶端消息的收發(fā)
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 12 - 消息發(fā)送具體場景
HTML+JS+websocket 實例,聯(lián)機“游戲王”對戰(zhàn) 13 - 實機演示
這章開始我們結(jié)合 js 代碼看看游戲具體功能的實現(xiàn)。大致的規(guī)劃是先介紹貫穿整個游戲?qū)值幕A(chǔ)功能,之后再針對不同的功能按鍵和功能板塊來逐一介紹。函數(shù)中涉及聯(lián)機的功能會放在實現(xiàn)聯(lián)機的章節(jié)具體介紹。
卡組系統(tǒng)
首先來介紹下整個游戲存放素材的主體,卡組的實現(xiàn)。
1.卡組結(jié)構(gòu):
游戲的卡組本質(zhì)上是一個簡單的字符串數(shù)組,在游戲初始化時就加載存儲了每一張卡牌圖片的路徑(或者URL)。卡組在結(jié)構(gòu)上是一個堆棧,遵循后進先出(LIFO)的原則,即每次送入牌組的卡片默認放在牌組的最上方,若不進行洗牌,那么最后被放入的卡片將在最近的一次抽卡中率先被抽到,就像槍械彈匣一樣。
堆棧有兩種基本操作,POP(出棧)與 PUSH(入棧)。抽卡時我們執(zhí)行 POP 操作,將卡組最上方的卡片從卡組中剔除,發(fā)到玩家手牌。當碰到某些效果需將手牌或場上的卡片放回卡組最上方時我們則執(zhí)行 PUSH 操作,將指定的卡片壓入卡組的最上方。如需要放回卡組后再切洗,則在執(zhí)行完 PUSH 操作后再執(zhí)行洗牌函數(shù)即可。
當然,有時候我們會需要從卡組中選取指定卡牌并拿出的效果,這時候被指定的卡片需要從卡組中剔除,如果只是單純的 POP 操作我們就得一張一張的將卡片取出直到想要的卡片為止,非常麻煩。JS中提供了一個 splice() 方法,它允許我們直接刪除或替換數(shù)組中的一個或多個元素,只需提供元素的索引值以及需刪除元素的個數(shù)。在這個方法里我們可提供指定卡牌在卡組中的序號,并且只刪除一個元素,即被選中的那張卡牌會被剔除卡組。剔除后該位置不會留空,而是被后面的其他卡向上填補,這意味著卡組中部分卡牌的索引也會發(fā)生變化,這個功能我們也會在以后詳細說到。
2.卡組的加載:
在游戲?qū)珠_始前雙方玩家需要加載各自的卡組。
首先要用到幾個全局變量:
P1DeckName 用于指示我們此次對局要加載哪一副卡組的卡組名稱。一個卡組名稱實質(zhì)上代表一個卡組文件夾,我們可以有多個卡組文件夾,自行填寫本次對局要用的那個:
var P1DeckName = "Deck_KaiMa"; //我方牌組名
P1DeckNum 用于指示卡組中的卡牌數(shù)量(暫時還未實現(xiàn)自動讀取文件夾中的所有文件,實現(xiàn)了可以不用這個變量):
var P1DeckNum = 50; //我方牌組卡片數(shù)量
P1Deck 用于存儲卡組所有圖片url的字符串數(shù)組:
var P1Deck = []; //我方牌組(儲存我方所有卡片src)
之后通過循環(huán)的方法將卡組文件夾中的每一個圖片路徑給push到字符串數(shù)組中:
//儲存P1卡組所有卡片路徑
for (var i=0; i<P1DeckNum; i++) {
var cardsrc = "image/cards/" + P1DeckName + "/" + i + ".jpg"
P1Deck.push(cardsrc);
}
當然在這之前我們已經(jīng)將卡組文件夾中所有卡牌的文件名改成了數(shù)字:
加載完成后還要再執(zhí)行一下洗牌操作,不然每次順序都是固定的:
/*生成隨機數(shù) */
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
/*克隆數(shù)組 */
function cloneArr(arr) {
// 從第一個字符就開始 copy
// slice(start,end) 方法可從已有的數(shù)組中返回選定的元素。
return arr.slice(0);
}
/*數(shù)組洗牌 */
function shuffle(arr) {
let newArr = [];
newArr = arr;
for (let i = 0; i < newArr.length; i++) {
let j = getRandom(0, i);
let temp = newArr[i];
newArr[i] = newArr[j];
newArr[j] = temp;
}
return newArr;
}
P1Deck = shuffle(cloneArr(P1Deck)); //牌組洗牌
3.卡組抽卡的實現(xiàn):
從卡組抽卡是將卡組數(shù)組中的卡牌url傳到手牌卡槽當中,實現(xiàn)這個js函數(shù)之前我們先回顧下html文件中的手牌區(qū)域內(nèi)容:
<div class="item">
<img id="p1-hand0" class="card" onmouseover="showCardInfo('hand', this.src, 0, 'player1')" onclick="selectCard(this.id, 'hand', this.src, 0, 'player1')" src="">
</div>
我方手卡區(qū)中有8個這樣的img標簽,分別對應(yīng)8個手卡卡槽,每個標簽有獨立的id,且src屬性默認是留空的,其他屬性暫且不管。
每次執(zhí)行抽卡函數(shù)時,我們需要確定一個空的手牌卡槽并且獲取它的id,之后給它的src屬性賦上抽出卡片的圖片url,賦值后網(wǎng)頁里才會顯示卡牌的圖片。
為確保即將賦值的卡槽是空卡槽,我們需要對每個卡槽的src值進行逐一判斷。由于src屬性留空后它的值并不是“”或者null,而是默認為所屬html文件存放的路徑,所以我這里又設(shè)置了一個全局變量 emptysrc,用于存放此默認路徑,并在html文件加載完成后從任意一個空的img元素中獲取該路徑用于其他函數(shù)對于空卡槽的判定。
//獲取空的img src路徑,方便其他函數(shù)判斷卡槽是否為空
//window.onload 使函數(shù)在html完全加載后執(zhí)行
var emptysrc;
window.onload = function() {
var handID = 'p1-field0'; //選取任意卡槽id
element = document.getElementById(handID);
emptysrc = element.src;
P1Deck = shuffle(cloneArr(P1Deck)); //洗牌
}
注:此處局部變量 handID 命名有誤,這里我選的是一個戰(zhàn)場卡槽的 id 而非手卡卡槽,但是不影響函數(shù)的運行效果,空src的默認值是相同的。
接下來實現(xiàn)抽卡函數(shù) drawCard () :
遍歷我方所有8個手卡卡槽:
獲取當前卡槽id;
通過卡槽id獲取當前卡槽對象;
如果(卡槽對象是空卡槽):
對卡組數(shù)組P1Deck執(zhí)行POP操作,將卡組最上方的圖片url推出并賦值給卡槽對象的src屬性;
立刻停止遍歷;
/*抽取牌組最上方一張卡至手卡 */
function drawCard() {
for (var i=0; i<8; i++) {
var handID = 'p1-hand' + i.toString();
element = document.getElementById(handID);
if (element.src == emptysrc) { //如果該卡槽為空
element.src = P1Deck.pop();
/*觸發(fā)抽卡音效 */
var snd = new Audio("sound/draw.wav");
snd.play();
/**
* 告知對手哪張手卡卡槽添加了一張卡
*/
messageHand('add', i);
break;
}
}
}
注:代碼中關(guān)于聯(lián)機的函數(shù)將放到實現(xiàn)聯(lián)機的章節(jié)一起匯總介紹。
最后一步,觸發(fā) drawCard() 的條件是玩家點擊了游戲界面卡組區(qū)的那個代表卡組的卡背圖片,我們找到html中對應(yīng)的那個元素:
<div class="item deck">
<img id="deck_r" class="card" src="image/cards/cardback.jpg" alt="cardback" onclick="drawCard()">
</div>
這也是一個img標簽,src設(shè)置了卡片背面圖片的url,onclick屬性設(shè)置我們寫好的抽卡函數(shù)drawCard(),這樣在點擊這個卡背圖片時就會向手牌區(qū)的空卡槽添加一張卡片了。
其他功能后面的章節(jié)來繼續(xù)介紹。