目錄
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 - 搭建游戲服務端
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 - 實機演示
功能按鍵的實現(xiàn)(一)
1. 卡片放置:
卡片的攻擊繁莹,防御檩互,背蓋防御召喚以及發(fā)動,覆蓋咨演,這些操作都可歸類為玩家向戰(zhàn)場上放置卡片闸昨,卡片放置函數(shù)可通過傳遞不同參數(shù)來分別執(zhí)行這些功能。
實現(xiàn)卡片放置主要有幾個步驟:
(1)選中手牌某一張卡片薄风,并記錄卡片信息:
這個操作由我們前面章節(jié)介紹的卡片選中系統(tǒng)來完成饵较,當我們選中某張卡片后會用一個全局對象記錄卡片的來源類型(手牌/場上),卡片序號遭赂,卡牌圖片 url 等信息供其他函數(shù)使用循诉。
(2)尋找場上的空卡槽:
確定怪獸/魔法陷阱區(qū)域的某個空卡槽,如果沒有空卡槽剩余則無法執(zhí)行召喚/發(fā)動/覆蓋嵌牺。
(3)將被選中的手牌刪除:
被選中的手牌被打到場上去了打洼,故需從手牌被刪除龄糊。
(4)更新戰(zhàn)場信息并更新戰(zhàn)場:
將被選中的卡片按要求加載至戰(zhàn)場上并記錄其狀態(tài)。
卡片放置函數(shù) placeCard:
參數(shù) | 含義 |
---|---|
placetype | 放置類型募疮,包括 攻擊 / 防御 / 背蓋防御 / 發(fā)動 / 覆蓋 |
cardtype | 卡片類型炫惩,包括 怪獸 / 魔法陷阱 |
尋找 怪獸/魔法陷阱 區(qū)域的空卡槽;
如果(卡槽未滿)阿浓;
如果(被選中的卡片來源于手牌):
記錄手牌id他嚷;
通過手牌id獲取手牌卡槽對象;
清除手牌卡槽的卡片芭毙;
更新戰(zhàn)場數(shù)組fieldArrayPly1的內容筋蓖;
更新戰(zhàn)場上的卡片;
清空所有卡槽的選中狀態(tài)退敦;
/**
* 我方從手牌向場上放置卡片粘咖,并發(fā)出放置指令 (攻擊,防御侈百,背蓋防御瓮下,發(fā)動,蓋卡)
* @param {string} placetype - place type (attack/defence/back/on/off)
* @param {string} cardtype - card type (monster/magic)
*/
function placeCard(placetype, cardtype) {
var cardslot = findEmptySlot(cardtype) //尋找空的卡槽
var cardsrc;
if(cardslot == -1) {
alert("卡槽已滿");
} else {
if (SelectedCard.type == 'hand') { //放置卡片必須來源于手牌
/*獲取被選中手卡信息 */
var handslot = (SelectedCard.cardNo).toString();
var handID = "p1-hand" + handslot;
element = document.getElementById(handID);
cardsrc = SelectedCard.cardSrc;
element.src = ""; //手牌該卡消失
/*更新戰(zhàn)場信息 */
fieldArrayPly1.FieldCards[cardslot].imgsrc = cardsrc;
fieldArrayPly1.FieldCards[cardslot].state = placetype;
/*發(fā)出指令钝域,執(zhí)行更新戰(zhàn)場卡片的函數(shù) */
var fieldID = "p1-field" + cardslot.toString();
updateField(fieldID, placetype, cardsrc);
/**
* 放置后告知對手執(zhí)行戰(zhàn)場更新函數(shù);
* 放置完成后記得告訴對手哪張手卡消失了;
* 注意:我方戰(zhàn)場變化對對方來說是P2;
*/
var updateID = "p2-field" + cardslot.toString();
messageField(placetype, updateID, cardsrc);
messageHand('reduce', handslot);
/*清空所有選中狀態(tài) */
cleanSelected();
}
}
}
在html中:
<button class="button" type="button" name="attkSummon" onclick="placeCard('attk', 'monster')">攻擊召喚</button>
<button class="button" type="button" name="defenSummon" onclick="placeCard('defen', 'monster')">守備召喚</button>
<button class="button" type="button" name="backSummon" onclick="placeCard('back', 'monster')">背蓋召喚</button>
<button class="button" type="button" name="launchCard" onclick="placeCard('on', 'magic')">發(fā)動(手卡)</button>
<button class="button" type="button" name="coverCard" onclick="placeCard('off', 'magic')">覆蓋(手卡)</button>
這些 button 都會調用 placeCard 函數(shù)讽坏,且根據(jù)不同的功能傳入不同的參數(shù)。
尋找空卡槽函數(shù) findEmptySlot:
/**
* 返回當前我方場上/手牌的空卡槽序號(怪獸卡槽與魔法陷阱卡槽也要區(qū)分開)
* @param {string} slottype - type of wanted empty slot (monster/magic/hand)
*/
function findEmptySlot(slottype) {
var emptySlot = -1;
if (slottype == 'monster') { //放置怪獸卡搜索0-4卡槽
for (var i=0; i<5; i++) {
if (fieldArrayPly1.FieldCards[i].state == "null") {
emptySlot = i;
break;
}
}
} else if (slottype == 'magic') { //放置魔法陷阱卡搜索5-9卡槽
for (var i=5; i<10; i++) {
if (fieldArrayPly1.FieldCards[i].state == "null") {
emptySlot = i;
break;
}
}
} else if (slottype == 'hand') {
for (var i=0; i<8; i++) {
var handID = 'p1-hand' + i.toString();
element = document.getElementById(handID);
if (element.src == emptysrc) { //如果該卡槽為空
emptySlot = i;
break;
}
}
}
return emptySlot;
}
根據(jù)被選中卡片的類型去尋找相應區(qū)域的空卡槽例证,并返回卡槽序號路呜。
2. 更變卡片表示形式:
卡片表示形式的更變包括怪獸卡與魔法陷阱卡。
怪獸卡的形式變更順序:
攻擊 -> 防御 -> 背蓋 -> 攻擊 -> ...
魔法陷阱的形式更變順序:
覆蓋 -> 表側 -> 覆蓋 -> ...
更變形式函數(shù) changeState:
參數(shù) | 含義 |
---|---|
cardtype | 卡片類型织咧,包括 怪獸/魔法陷阱 |
如果(被選中的卡屬于場上 且 屬于我方玩家player1):
獲取卡槽id胀葱;
獲取卡槽中的卡片url以及當前狀態(tài)(即表示形式state);
如果卡片類型是:
怪獸:
按 攻擊/防御/背蓋 的順序變更1次狀態(tài)并記錄烦感;
魔法陷阱:
按 表側/背蓋 的順序變更1次狀態(tài)并記錄巡社;
更新戰(zhàn)場數(shù)組fieldArrayPly1中該卡槽的狀態(tài)(state);
更新戰(zhàn)場上的卡片手趣;
/**
* 更變卡片的表示形式
* 更變順序為:攻擊 -> 防御 -> 背蓋 -> 攻擊, 蓋覆卡 -> 表側卡 -> 蓋覆卡
* @param {string} cardtype - card type (monster/magic)
*/
function changeState(cardtype) {
if (SelectedCard.type == 'field' && SelectedCard.player == 'player1') { //必須是我方場上的卡方可更變表示形式
var fieldID = "p1-field" + (SelectedCard.cardNo).toString();
var cardsrc = fieldArrayPly1.FieldCards[SelectedCard.cardNo].imgsrc;
var cardstate = fieldArrayPly1.FieldCards[SelectedCard.cardNo].state;
switch (cardtype) {
case 'monster':
if (cardstate == 'attk') {
cardstate = "defen";
} else if (cardstate == 'defen') {
cardstate = "back";
} else if (cardstate == 'back') {
cardstate = "attk";
}
break;
case 'magic':
if (cardstate == 'off') {
cardstate = "on";
} else {
cardstate = "off";
}
break;
default:
break;
}
fieldArrayPly1.FieldCards[SelectedCard.cardNo].state = cardstate; //更新場上卡片狀態(tài)信息
cardstate = "change-" + cardstate; //為通過更變形式而導致的戰(zhàn)場更新操作添加一個標簽方便更新函數(shù)識別(因為更變形式不觸發(fā)音效)
updateField(fieldID, cardstate, cardsrc); //更新指定卡槽
/**
* 告知對手某一卡槽的表示形式發(fā)生變化肥荔,執(zhí)行戰(zhàn)場更新函數(shù)
*/
var updateID = "p2-field" + (SelectedCard.cardNo).toString();
messageField(cardstate, updateID, cardsrc);
}
}
更變后的形式(state)會被戰(zhàn)場數(shù)組記錄并同時傳遞給戰(zhàn)場更新函數(shù) updateField 以更新所選卡槽的卡片樣式绿渣。
3. 戰(zhàn)場更新函數(shù):
戰(zhàn)場更新函數(shù) updateField 用于更改某一個卡槽的卡片樣式,讓玩家可以實際的看到操作帶來的變化燕耿,前面介紹的兩個函數(shù)均有在作用域的末尾調用此函數(shù)中符。
參數(shù) | 含義 |
---|---|
fieldID | 需要更新的卡槽 id |
cardstate | 需要更新的卡片狀態(tài) |
cardsrc | 需要更新的卡牌圖片 url |
updateField 的原理很簡單,我們在css中準備了幾種卡片狀態(tài)對應的卡槽樣式誉帅,根據(jù)參數(shù)的不同修改卡槽樣式即可(其實有些樣式是一樣的完全可以共用…)淀散。
.main-field .battle-field .card-field .item .card-attk {
width: 65px;
height: 94px;
margin: 1px 40px;
}
.main-field .battle-field .card-field .item .card-defen {
width: 65px;
height: 94px;
transform: rotate(90deg);
margin: 1px 40px;
}
.main-field .battle-field .card-field .item .card-back {
width: 65px;
height: 94px;
transform: rotate(90deg);
margin: 1px 40px;
}
.main-field .battle-field .card-field .item .card-on { /* 魔法陷阱翻開狀態(tài)*/
width: 65px;
height: 94px;
margin: 1px 40px;
}
.main-field .battle-field .card-field .item .card-off { /* 魔法陷阱覆蓋狀態(tài)*/
width: 65px;
height: 94px;
margin: 1px 40px;
}
/**
* 戰(zhàn)場狀態(tài)更新右莱,單獨更新某一個卡槽
* @param {string} fieldID - field img container id
* @param {string} cardstate - state of card (attk/defen/back/on/off)
* @param {string} cardsrc - card source url
*/
function updateField(fieldID, cardstate, cardsrc) {
var stateclass;
element = document.getElementById(fieldID);
/**
* 如果是蓋卡或背蓋召喚直接顯示卡片背面
* 檢查showCardInfo函數(shù)可知對于我方來說,即使卡片是背面圖片仍可以顯示卡片信息
* 由于音效種類問題修改分類了多種情況
*/
switch (cardstate) {
case 'off':
case 'back':
element.src = CardBackSrc;
stateclass = "card-" + cardstate;
/*觸發(fā)背蓋或蓋卡音效 */
var snd = new Audio("sound/activate.wav");
snd.play();
break;
case 'on': //正常發(fā)動卡片
element.src = cardsrc;
stateclass = "card-" + cardstate;
/*觸發(fā)發(fā)動卡片音效 */
var snd = new Audio("sound/activate.wav");
snd.play();
break;
case 'change-off': //通過更變形式覆蓋卡片
element.src = CardBackSrc;
stateclass = "card-" + cardstate.replace("change-", "");
break;
case 'change-back': //通過更變形式背蓋召喚卡片
element.src = CardBackSrc
stateclass = "card-" + cardstate.replace("change-", "");
break;
case 'change-on': //通過更變形式實現(xiàn)的打開蓋卡
/*觸發(fā)打開蓋卡音效 */
element.src = cardsrc;
stateclass = "card-" + cardstate.replace("change-", "");
var snd = new Audio("sound/open.wav");
snd.play();
break;
case 'null':
stateclass = "card";
element.src = cardsrc;
break;
default:
element.src = cardsrc;
if (cardstate.search("change-") == -1) { //正常召喚
stateclass = "card-" + cardstate;
/*觸發(fā)發(fā)召喚怪獸音效 */
var snd = new Audio("sound/summon.wav");
snd.play();
} else { //更變形式
stateclass = "card-" + cardstate.replace("change-", "");
}
break;
}
element.setAttribute("class", stateclass); //更新對應img容器的class
}
注:這里由于添加音效的問題多出了3種狀態(tài)档插,change-off, change-back, change-on慢蜓,因為通過更變形式打開或背蓋卡片與直接從手牌發(fā)動或背蓋卡片的音效是不同的,故多加了幾個狀態(tài)區(qū)分一下郭膛。前文 changeState 函數(shù)中也有一段代碼是為了這里區(qū)分而在調用戰(zhàn)場更新前修改了狀態(tài)名稱:
fieldArrayPly1.FieldCards[SelectedCard.cardNo].state = cardstate; //更新場上卡片狀態(tài)信息
cardstate = "change-" + cardstate; //為通過更變形式而導致的戰(zhàn)場更新操作添加一個標簽方便更新函數(shù)識別(因為更變形式不觸發(fā)音效)
updateField(fieldID, cardstate, cardsrc); //更新指定卡槽
最后來測試一下:
各種召喚:
發(fā)動/放置 魔法陷阱:
更變形式:
下一章繼續(xù)介紹其他的功能按鍵晨抡。