HTML+JS+websocket 实例,联机“游戏王”对战(五)- 卡片选中系统

目录

HTML+JS+websocket 实例,联机“游戏王”对战 1
HTML+JS+websocket 实例,联机“游戏王”对战 2 - 联机模式
HTML+JS+websocket 实例,联机“游戏王”对战 3 - 界面布局
HTML+JS+websocket 实例,联机“游戏王”对战 4 - 卡组系统
HTML+JS+websocket 实例,联机“游戏王”对战 5 - 卡片选中系统
HTML+JS+websocket 实例,联机“游戏王”对战 6 - 卡片放置,战场更新
HTML+JS+websocket 实例,联机“游戏王”对战 7 - 墓地,副控制面板
HTML+JS+websocket 实例,联机“游戏王”对战 8 - 返回手卡,卡组
HTML+JS+websocket 实例,联机“游戏王”对战 9 - 实现简单 websocket 通信
HTML+JS+websocket 实例,联机“游戏王”对战 10 - 搭建游戏服务端
HTML+JS+websocket 实例,联机“游戏王”对战 11 - 客户端消息的收发
HTML+JS+websocket 实例,联机“游戏王”对战 12 - 消息发送具体场景
HTML+JS+websocket 实例,联机“游戏王”对战 13 - 实机演示

卡片选中系统

大部分卡片的操作功能都是针对单张卡片的,比如召唤,盖卡,回到卡组,返回手牌,送去墓地。执行这些操作前玩家必须先选定需要操作的对象,即某一张卡片。这个时候我们就需要建立一个卡片选中系统,在玩家选定卡片时记录该卡的相关信息,以便我们对选中的卡片执行相应的操作。


selection_structure.png

1.全局变量:

首先我们需要一个用来随时存储被选中卡片相关信息的全局对象 SelectedCard:

var SelectedCard = {  //被选中的卡对象
    type: "null",  //卡的来源类型(手牌,场上)
    cardNo: -1,  //卡片的序号
    cardSrc: "null",
    player: "null"  //玩家号
};

对象中变量的含义:

变量 含义
type 被选中卡片的来源,是属于手牌还是场上
cardNo 被选中卡片所属的卡槽序号,卡槽可以是手牌卡槽或战场卡槽
cardSrc 被选中卡片的图片 url
player 被选中卡片所属的玩家类型,我方(player1)还是对方(player2)

这些变量是根据游戏其他功能的需求总结得出。举个栗子 type 变量,当我们执行召唤功能的时候,我们需要通过 type 判断被选中的卡片属于场上还是手牌,只有手牌的卡片才能执行召唤(墓地或者卡组的卡片需要先拿到手牌再执行召唤),因此我们需要在选中某张卡牌后记录它的 type 信息为其他功能函数所用。

此外,我们还需要一个用于存储我方战场信息的全局对象数组 fieldArrayPly1:

// 储存场上卡片信息(10张卡的图片src,状态)
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"},
    ] 
};

手卡的属性比较简单,操作手牌的卡牌时我们不需要关注卡片的状态。而战场卡槽的卡片情况要复杂一些,首先战场卡片有魔法陷阱区,有怪兽区,魔法陷阱区的卡片又分为打开与盖覆,而怪兽区的卡片则有攻击,防御与背盖防御三种状态。我们这里针对这些状态用一个变量state来表示,并把这些状态分类为:

状态 字符
攻击状态 attk
防御状态 defen
背盖(防御)状态 back
打开状态(即表侧表示) on
盖覆状态(即里侧表示) off

这些状态将用于玩家选中某个卡槽的卡片并执行某些操作时(比如更变形式)用于函数的判断。

另外 fieldArrayPly1 存储的就是战场卡槽的卡片url了。对于手卡卡片,卡片url就记录在html的img标签中,需要获取的时候(比如召唤怪兽的时候我们需要把手卡的url拿出来赋值给战场的卡槽src属性)我们可以直接通过element方法从html元素中获取。战场卡槽同样在对应的img标签中存有该卡槽的卡片url,但是需要考虑一种特殊情况,就是当卡槽中的卡是被盖防御或者盖覆状态时,该卡槽的url储存的并非是某张卡牌的路径,而是一个专门的卡背路径:

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

这样我们才能在战场UI中显示一张卡背的图片以表示背盖防御或是盖覆状态:


game_back_state.png

但是这样相当于丢失了这张背盖卡片的url信息,所以在它放置到战场的前一刻我们就要率先拿出这张卡片的url,并存放在战场数组 fieldArrayPly1 中。这样即使是被盖的卡片我们也能知晓它的信息,并且显示给我方玩家:

game_show_backcard.png

当然对方是看不到些信息的,选中后也就显示个卡背而已。

2.卡片选中函数:

相关的变量介绍完了,现在我们来实现选中函数。

选中函数由玩家鼠标点击手牌或战场的某个卡槽时触发,设置在html中卡槽的onclick属性中,函数名为 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 的参数:

参数 含义
id 选中卡槽 id(img 标签 id)
type 选中卡槽类型(手卡 / 场上)
cardsrc 卡片的图片路径或 url
cardNo 卡槽序号
ply 所属玩家(player1 我方 / player2 对方)

Html代码中可以看到已经填写的所有参数,之后:

如果(所选卡槽不是空卡槽):
    清空所有已选中的卡片信息与样式;
    储存选中卡片的类型(type),卡槽序号(cardNo),所属玩家(ply)信息;
    
    如果(卡片属于手牌):
        通过卡槽id获取当前卡槽对象;
        修改卡槽样式为选中状态(金色边框);
        直接从参数cardsrc中获取并存储卡片url;
        
    如果(卡片属于场上):
        通过卡槽id获取当前卡槽对象;
        修改卡槽样式为选中状态(金色边框);
        如果(卡片属于我方):
            从战场数组fieldArrayPly1中获取并存储卡片url;
        如果(卡片属于对方):
            直接从参数cardsrc中获取并存储卡片url;
/**
 * 选择卡片(游戏中始终只有一张卡牌处于选中状态),并记录当前卡片信息
 * @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();  //选择卡片之前首先清空场上已选中的卡片样式再更新

        SelectedCard.type = type;
        SelectedCard.cardNo = cardNo;
        SelectedCard.player = ply;
        
        //console.log("image" + cardsrc.split('image')[1]);
        var CardSrc = "image" + cardsrc.split('image')[1];  //把带盘符的绝对路径改为相对路径

        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;  //若从我方场上选择则由场上状态数组获取卡片src
            } else if (ply == 'player2') {
                SelectedCard.cardSrc = CardSrc;  //若从对方场上选择则直接从img容器获取src
            }
        }
    }
}

被选中的样式(场上与手牌):


game_cardselected_field.png
game_cardselected_hand.png

在选中函数中,每次执行功能前会调用一次选择清空函数 cleanSelected(),重置手牌及场上所有被选中的卡槽样式与卡片信息对象 SelectedCard,以免出现多选的情况。

cleanSelected() 函数内容很简单,遍历重置所有卡槽样式,之后重置对象 SelectedCard。注意函数里有一个关于副控制面板的操作,副控制面板较特殊,选中函数也是另外写的,这个放到后面的章节来介绍。

/**
 * 清除所有卡片被选中的状态
 * 注意这里手牌和场上用于显示被选中状态的容器不同
 * 手牌的是img容器而场上用的是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++) {  // 清楚场上选中
        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";
}


卡片信息显示

最后还有个最重要的卡片显示函数,当玩家鼠标指到某张卡片时会在UI的左上角显示卡片的详细信息,其实就是放大版的卡牌图片。

game_cardpointed.png

:鼠标指着的是“强欲之壶”,不是那张被选中的“帝王海马”,鼠标截图截不到惹…

先来回顾下卡片信息显示板块的html代码:

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

就是一个简单的 img 标签而已。触发显示的函数设置在各个卡槽的 onmouseover 属性中,函数名 showCardInfo,鼠标悬浮于某个卡槽上时即会触发:

<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参数表:

参数 含义
type 选中卡槽类型(手卡 / 场上)
cardsrc 卡片的图片路径或 url
cardNo 卡槽序号
ply 所属玩家(player1 我方 / player2 对方)

参数和 selectCard 相似,接下来照例 sudo code:

如果(被指示的卡槽不是空卡槽):
    获取卡片显示框对象;
    
    如果(卡片所属是我方):
        手卡:直接从参数获取卡片url并赋值给显示框对象;
        场上的卡:从战场数组fieldArrayPly1中获取卡片url并赋值给显示框对象;
        
    如果(卡片所属是对方):
        手卡:直接用卡背图片url赋值给显示框对象;
        场上的卡:直接从参数获取卡片url并赋值给显示框对象;
/**
 * 显示卡片信息
 * 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];  //把带盘符的绝对路径改为相对路径

        switch(ply) {
            /*我方卡片一律显示 */
            case 'player1':
                if (type == 'hand') {  //手卡均显示
                    element.src = CardSrc;
                } else {  //场上卡片按数组记录的img的src显示(若直接取img容器的src则无法看到我方盖卡)
                    element.src = fieldArrayPly1.FieldCards[cardNo].imgsrc;
                }
                break;
            /*对方卡片视情况显示 */
            case 'player2':
                if (type == 'hand') {  //手卡均不显示
                    element.src = CardBackSrc;
                } else {  //场上卡片直接按容器的img值显示
                    element.src = CardSrc;
                }
                break;
            default: break;
        }
    }
}

玩家无法看到对方手牌,所以对方手牌一律用卡背显示。对方场上的卡片也直接从img标签元素中获取url(img标签的src值作为参数传给了showInfo),因为我们不需要看到对方背盖表示的卡片,如果要看需要让对方翻开。

game_cardpointed_back.png

指示对方手牌或被盖卡片只能看到卡背的图片。

贯穿整个游戏的功能函数应该介绍的差不多了,下一章开始就该针对具体的按钮来实现对应功能啦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容