HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn)(五)- 卡片選中系統(tǒng)

目錄

HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 1
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 2 - 聯(lián)機(jī)模式
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 3 - 界面布局
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 4 - 卡組系統(tǒng)
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 5 - 卡片選中系統(tǒng)
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 6 - 卡片放置,戰(zhàn)場(chǎng)更新
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 7 - 墓地,副控制面板
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 8 - 返回手卡,卡組
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 9 - 實(shí)現(xiàn)簡(jiǎn)單 websocket 通信
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 10 - 搭建游戲服務(wù)端
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 11 - 客戶端消息的收發(fā)
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 12 - 消息發(fā)送具體場(chǎng)景
HTML+JS+websocket 實(shí)例,聯(lián)機(jī)“游戲王”對(duì)戰(zhàn) 13 - 實(shí)機(jī)演示

卡片選中系統(tǒng)

大部分卡片的操作功能都是針對(duì)單張卡片的,比如召喚,蓋卡,回到卡組,返回手牌,送去墓地。執(zhí)行這些操作前玩家必須先選定需要操作的對(duì)象,即某一張卡片。這個(gè)時(shí)候我們就需要建立一個(gè)卡片選中系統(tǒng),在玩家選定卡片時(shí)記錄該卡的相關(guān)信息,以便我們對(duì)選中的卡片執(zhí)行相應(yīng)的操作。


selection_structure.png

1.全局變量:

首先我們需要一個(gè)用來(lái)隨時(shí)存儲(chǔ)被選中卡片相關(guān)信息的全局對(duì)象 SelectedCard:

var SelectedCard = {  //被選中的卡對(duì)象
    type: "null",  //卡的來(lái)源類型(手牌,場(chǎng)上)
    cardNo: -1,  //卡片的序號(hào)
    cardSrc: "null",
    player: "null"  //玩家號(hào)
};

對(duì)象中變量的含義:

變量 含義
type 被選中卡片的來(lái)源,是屬于手牌還是場(chǎng)上
cardNo 被選中卡片所屬的卡槽序號(hào),卡槽可以是手牌卡槽或戰(zhàn)場(chǎng)卡槽
cardSrc 被選中卡片的圖片 url
player 被選中卡片所屬的玩家類型,我方(player1)還是對(duì)方(player2)

這些變量是根據(jù)游戲其他功能的需求總結(jié)得出。舉個(gè)栗子 type 變量,當(dāng)我們執(zhí)行召喚功能的時(shí)候,我們需要通過(guò) type 判斷被選中的卡片屬于場(chǎng)上還是手牌,只有手牌的卡片才能執(zhí)行召喚(墓地或者卡組的卡片需要先拿到手牌再執(zhí)行召喚),因此我們需要在選中某張卡牌后記錄它的 type 信息為其他功能函數(shù)所用。

此外,我們還需要一個(gè)用于存儲(chǔ)我方戰(zhàn)場(chǎng)信息的全局對(duì)象數(shù)組 fieldArrayPly1:

// 儲(chǔ)存場(chǎng)上卡片信息(10張卡的圖片src,狀態(tài))
var fieldArrayPly1 = { 
    FieldCards: [
        { "imgsrc": "null", "state": "null"}, 
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
        { "imgsrc": "null", "state": "null"},
    ] 
};

手卡的屬性比較簡(jiǎn)單,操作手牌的卡牌時(shí)我們不需要關(guān)注卡片的狀態(tài)。而戰(zhàn)場(chǎng)卡槽的卡片情況要復(fù)雜一些,首先戰(zhàn)場(chǎng)卡片有魔法陷阱區(qū),有怪獸區(qū),魔法陷阱區(qū)的卡片又分為打開(kāi)與蓋覆,而怪獸區(qū)的卡片則有攻擊,防御與背蓋防御三種狀態(tài)。我們這里針對(duì)這些狀態(tài)用一個(gè)變量state來(lái)表示,并把這些狀態(tài)分類為:

狀態(tài) 字符
攻擊狀態(tài) attk
防御狀態(tài) defen
背蓋(防御)狀態(tài) back
打開(kāi)狀態(tài)(即表側(cè)表示) on
蓋覆狀態(tài)(即里側(cè)表示) off

這些狀態(tài)將用于玩家選中某個(gè)卡槽的卡片并執(zhí)行某些操作時(shí)(比如更變形式)用于函數(shù)的判斷。

另外 fieldArrayPly1 存儲(chǔ)的就是戰(zhàn)場(chǎng)卡槽的卡片url了。對(duì)于手卡卡片,卡片url就記錄在html的img標(biāo)簽中,需要獲取的時(shí)候(比如召喚怪獸的時(shí)候我們需要把手卡的url拿出來(lái)賦值給戰(zhàn)場(chǎng)的卡槽src屬性)我們可以直接通過(guò)element方法從html元素中獲取。戰(zhàn)場(chǎng)卡槽同樣在對(duì)應(yīng)的img標(biāo)簽中存有該卡槽的卡片url,但是需要考慮一種特殊情況,就是當(dāng)卡槽中的卡是被蓋防御或者蓋覆狀態(tài)時(shí),該卡槽的url儲(chǔ)存的并非是某張卡牌的路徑,而是一個(gè)專門(mén)的卡背路徑:

var CardBackSrc = "image/cards/cardback.jpg";  //卡片背面圖片的src

這樣我們才能在戰(zhàn)場(chǎng)UI中顯示一張卡背的圖片以表示背蓋防御或是蓋覆狀態(tài):


game_back_state.png

但是這樣相當(dāng)于丟失了這張背蓋卡片的url信息,所以在它放置到戰(zhàn)場(chǎng)的前一刻我們就要率先拿出這張卡片的url,并存放在戰(zhàn)場(chǎng)數(shù)組 fieldArrayPly1 中。這樣即使是被蓋的卡片我們也能知曉它的信息,并且顯示給我方玩家:

game_show_backcard.png

當(dāng)然對(duì)方是看不到些信息的,選中后也就顯示個(gè)卡背而已。

2.卡片選中函數(shù):

相關(guān)的變量介紹完了,現(xiàn)在我們來(lái)實(shí)現(xiàn)選中函數(shù)。

選中函數(shù)由玩家鼠標(biāo)點(diǎn)擊手牌或戰(zhàn)場(chǎng)的某個(gè)卡槽時(shí)觸發(fā),設(shè)置在html中卡槽的onclick屬性中,函數(shù)名為 selectCard。

<div id="p1field2" class="item">
    <img id="p1-field2" class="card" onmouseover="showCardInfo('field', this.src, 2, 'player1')" onclick="selectCard('p1field2', 'field', this.src, 2, 'player1')" src="">
</div>

selectCard 的參數(shù):

參數(shù) 含義
id 選中卡槽 id(img 標(biāo)簽 id)
type 選中卡槽類型(手卡 / 場(chǎng)上)
cardsrc 卡片的圖片路徑或 url
cardNo 卡槽序號(hào)
ply 所屬玩家(player1 我方 / player2 對(duì)方)

Html代碼中可以看到已經(jīng)填寫(xiě)的所有參數(shù),之后:

如果(所選卡槽不是空卡槽):
    清空所有已選中的卡片信息與樣式;
    儲(chǔ)存選中卡片的類型(type),卡槽序號(hào)(cardNo),所屬玩家(ply)信息;
    
    如果(卡片屬于手牌):
        通過(guò)卡槽id獲取當(dāng)前卡槽對(duì)象;
        修改卡槽樣式為選中狀態(tài)(金色邊框);
        直接從參數(shù)cardsrc中獲取并存儲(chǔ)卡片url;
        
    如果(卡片屬于場(chǎng)上):
        通過(guò)卡槽id獲取當(dāng)前卡槽對(duì)象;
        修改卡槽樣式為選中狀態(tài)(金色邊框);
        如果(卡片屬于我方):
            從戰(zhàn)場(chǎng)數(shù)組fieldArrayPly1中獲取并存儲(chǔ)卡片url;
        如果(卡片屬于對(duì)方):
            直接從參數(shù)cardsrc中獲取并存儲(chǔ)卡片url;
/**
 * 選擇卡片(游戲中始終只有一張卡牌處于選中狀態(tài)),并記錄當(dāng)前卡片信息
 * @param {string} id - container id
 * @param {string} type - card source type (hand/field)
 * @param {string} cardsrc - card url
 * @param {int} cardNo - card No
 * @param {string} ply - player tag
 */
function selectCard(id, type, cardsrc, cardNo, ply) {
    if (cardsrc != emptysrc) {
        cleanSelected();  //選擇卡片之前首先清空?qǐng)錾弦堰x中的卡片樣式再更新

        SelectedCard.type = type;
        SelectedCard.cardNo = cardNo;
        SelectedCard.player = ply;
        
        //console.log("image" + cardsrc.split('image')[1]);
        var CardSrc = "image" + cardsrc.split('image')[1];  //把帶盤(pán)符的絕對(duì)路徑改為相對(duì)路徑

        if (type == 'hand') {
            element = document.getElementById(id);
            element.setAttribute("class", "card-selected");
            SelectedCard.cardSrc = CardSrc;  //若從手牌選擇直接從img容器獲取src
        } else {
            element = document.getElementById(id);  
            element.setAttribute("class", "item-selected");
            if (ply == 'player1') {
                SelectedCard.cardSrc = fieldArrayPly1.FieldCards[cardNo].imgsrc;  //若從我方場(chǎng)上選擇則由場(chǎng)上狀態(tài)數(shù)組獲取卡片src
            } else if (ply == 'player2') {
                SelectedCard.cardSrc = CardSrc;  //若從對(duì)方場(chǎng)上選擇則直接從img容器獲取src
            }
        }
    }
}

被選中的樣式(場(chǎng)上與手牌):


game_cardselected_field.png
game_cardselected_hand.png

在選中函數(shù)中,每次執(zhí)行功能前會(huì)調(diào)用一次選擇清空函數(shù) cleanSelected(),重置手牌及場(chǎng)上所有被選中的卡槽樣式與卡片信息對(duì)象 SelectedCard,以免出現(xiàn)多選的情況。

cleanSelected() 函數(shù)內(nèi)容很簡(jiǎn)單,遍歷重置所有卡槽樣式,之后重置對(duì)象 SelectedCard。注意函數(shù)里有一個(gè)關(guān)于副控制面板的操作,副控制面板較特殊,選中函數(shù)也是另外寫(xiě)的,這個(gè)放到后面的章節(jié)來(lái)介紹。

/**
 * 清除所有卡片被選中的狀態(tài)
 * 注意這里手牌和場(chǎng)上用于顯示被選中狀態(tài)的容器不同
 * 手牌的是img容器而場(chǎng)上用的是item容器
 */
function cleanSelected() {
    for (var i=0; i<8; i++) {  //清除手牌選中
        var handIDPly1 = 'p1-hand' + i.toString();
        element = document.getElementById(handIDPly1);
        element.setAttribute("class", "card");
    }
    for (var i=0; i<10; i++) {  // 清楚場(chǎng)上選中
        var fieldIDPly1 = 'p1field' + i.toString();
        var fieldIDPly2 = 'p2field' + i.toString();
        element = document.getElementById(fieldIDPly1);
        element2 = document.getElementById(fieldIDPly2);
        element.setAttribute("class", "item");
        element2.setAttribute("class", "item");
    }
    for (var i=0; i<sf_Card.size; i++) {
        var sf_ID = 'sf-card' + i.toString();
        element = document.getElementById(sf_ID);
        element.setAttribute("class", "card");
    }

    /*重置被選中的卡片信息 */
    SelectedCard.type = "null";
    SelectedCard.cardNo = -1;
    SelectedCard.cardSrc = "null";
    SelectedCard.player = "null";
}


卡片信息顯示

最后還有個(gè)最重要的卡片顯示函數(shù),當(dāng)玩家鼠標(biāo)指到某張卡片時(shí)會(huì)在UI的左上角顯示卡片的詳細(xì)信息,其實(shí)就是放大版的卡牌圖片。

game_cardpointed.png

:鼠標(biāo)指著的是“強(qiáng)欲之壺”,不是那張被選中的“帝王海馬”,鼠標(biāo)截圖截不到惹…

先來(lái)回顧下卡片信息顯示板塊的html代碼:

<div class="card-field">
    <img id="card-info" class="card" src="">
</div>

就是一個(gè)簡(jiǎn)單的 img 標(biāo)簽而已。觸發(fā)顯示的函數(shù)設(shè)置在各個(gè)卡槽的 onmouseover 屬性中,函數(shù)名 showCardInfo,鼠標(biāo)懸浮于某個(gè)卡槽上時(shí)即會(huì)觸發(fā):

<div class="item">
    <img id="p1-hand1" class="card" onmouseover="showCardInfo('hand', this.src, 1, 'player1')" onclick="selectCard(this.id, 'hand', this.src, 1, 'player1')" src="">
</div>

showCardInfo參數(shù)表:

參數(shù) 含義
type 選中卡槽類型(手卡 / 場(chǎng)上)
cardsrc 卡片的圖片路徑或 url
cardNo 卡槽序號(hào)
ply 所屬玩家(player1 我方 / player2 對(duì)方)

參數(shù)和 selectCard 相似,接下來(lái)照例 sudo code:

如果(被指示的卡槽不是空卡槽):
    獲取卡片顯示框?qū)ο螅?    
    如果(卡片所屬是我方):
        手卡:直接從參數(shù)獲取卡片url并賦值給顯示框?qū)ο螅?        場(chǎng)上的卡:從戰(zhàn)場(chǎng)數(shù)組fieldArrayPly1中獲取卡片url并賦值給顯示框?qū)ο螅?        
    如果(卡片所屬是對(duì)方):
        手卡:直接用卡背圖片url賦值給顯示框?qū)ο螅?        場(chǎng)上的卡:直接從參數(shù)獲取卡片url并賦值給顯示框?qū)ο螅?
/**
 * 顯示卡片信息
 * show card info
 * @param {string} type - card source type (hand/field)
 * @param {string} cardsrc - card url
 * @param {int} cardNo - card No 
 * @param {string} ply - player tag 
 */
function showCardInfo(type, cardsrc, cardNo, ply) {
    if (cardsrc != emptysrc) {
        element = document.getElementById('card-info');
        var CardSrc = "image" + cardsrc.split('image')[1];  //把帶盤(pán)符的絕對(duì)路徑改為相對(duì)路徑

        switch(ply) {
            /*我方卡片一律顯示 */
            case 'player1':
                if (type == 'hand') {  //手卡均顯示
                    element.src = CardSrc;
                } else {  //場(chǎng)上卡片按數(shù)組記錄的img的src顯示(若直接取img容器的src則無(wú)法看到我方蓋卡)
                    element.src = fieldArrayPly1.FieldCards[cardNo].imgsrc;
                }
                break;
            /*對(duì)方卡片視情況顯示 */
            case 'player2':
                if (type == 'hand') {  //手卡均不顯示
                    element.src = CardBackSrc;
                } else {  //場(chǎng)上卡片直接按容器的img值顯示
                    element.src = CardSrc;
                }
                break;
            default: break;
        }
    }
}

玩家無(wú)法看到對(duì)方手牌,所以對(duì)方手牌一律用卡背顯示。對(duì)方場(chǎng)上的卡片也直接從img標(biāo)簽元素中獲取url(img標(biāo)簽的src值作為參數(shù)傳給了showInfo),因?yàn)槲覀儾恍枰吹綄?duì)方背蓋表示的卡片,如果要看需要讓對(duì)方翻開(kāi)。

game_cardpointed_back.png

指示對(duì)方手牌或被蓋卡片只能看到卡背的圖片。

貫穿整個(gè)游戲的功能函數(shù)應(yīng)該介紹的差不多了,下一章開(kāi)始就該針對(duì)具體的按鈕來(lái)實(shí)現(xiàn)對(duì)應(yīng)功能啦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容