HTML+JS+websocket 实现联机“游戏王”对战(七)- 墓地,副控制面板


目录:

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

功能按键的实现(二)

文接上回,继续介绍功能按键。


1. 墓地与送去墓地:

先来实现下墓地系统。和卡组系统几乎一样,一个字符串数组,存储卡片url:

var P1Tomb = [];  //我方墓地(卡片src)

当有新卡片送入墓地时,把它的图片路径 PUSH 进去,若需拿出卡片则用 splic() 将其从墓地剔出。

执行送去墓地的操作前,我们仍然需要先选中一张卡片,全局对象 SelectedCard 会像缓存一样帮我们记录选中卡片的相关信息。

为了简化函数,送去墓地函数 sendtoTomb 只允许操作我方卡片,不能直接将对方卡牌送去墓地。同时只能直接操作场上与手牌的卡片,牌组中的卡片需要加入手牌后再送入墓地。

获取选中卡片url;
获取选中卡槽序号;

如果(选中卡片属于我方玩家player1):
	如果(卡片来源于手牌):
		根据卡槽序号获取手牌卡槽id;
		将选中手牌卡槽清空;
		将选中卡片url PUSH 到墓地P1Tomb中;

	如果(卡片来源于场上):
		根据卡槽序号获取场上卡槽id;
		将该卡的记录从战场数组fieldArrayPly1中清除;
		将选中卡片从卡槽清空;
		将选中卡片url PUSH 到墓地P1Tomb中;

清空所有被选中的状态;

/**
 * 将我方的卡片(场上/手牌)送去墓地
 */
function sendtoTomb() {
    var cardsrc = SelectedCard.cardSrc;
    var cardNo = SelectedCard.cardNo;
    var fieldID; 
    
    if (SelectedCard.player == 'player1') {
        /*若卡片来源于手牌 */
        if (SelectedCard.type == 'hand') {
            var handID = "p1-hand" + cardNo.toString();
            element = document.getElementById(handID);
            element.src = "";  //手牌该卡消失
            P1Tomb.push(cardsrc);  //将选中卡片的src存入墓地数组

            /**
             * 告知对方更新我方手牌数及墓地
             */
            messageHand("reduce", cardNo);
            messageTomb("add", "player2", "null", cardsrc);  //向墓地添加卡片不需要cardNo

        /*若卡片来源于场上 */
        } else if (SelectedCard.type == 'field') {
            fieldID = "p1-field" + cardNo.toString();
            fieldArrayPly1.FieldCards[cardNo].imgsrc = "null";  //场上该卡的记录清空
            fieldArrayPly1.FieldCards[cardNo].state = "null";
            P1Tomb.push(cardsrc);  //将选中卡片的src存入墓地数组
            updateField(fieldID, "null", "");  //更新战场

            /**
             * 告知对方更新我方战场及墓地
             */
            var updateID = "p2-field" + cardNo.toString();
            messageField("null", updateID, "");
            messageTomb("add", "player2", "null", cardsrc);
        } else {
            alert("请先将卡片拿到手牌再放入墓地");  //防止直接从卡组或墓地中选卡放回墓地
        }

        /*清空所有选中状态 */
        cleanSelected();

        sf_buttons('p1tomb');  //刷新副面板显示
    }
}

清除手牌的卡时我们直接获取 html 对象并置空它的src属性,清除战场卡槽时我们就直接用已有的函数 updateField 吧。

最后一行代码刷新副面板其实就是在副面板里显示刚被送入墓地的卡片(见演示gif),具体放到后面来说。

演示效果:

send_tomb


2. 副控制面板:

介绍“回到手牌”与“回到卡组”这两个功能前有必要先介绍一下副控制面板了。

当我们操作“回到手牌”时,除了从场地上返回手牌,也有可能是从卡组或者墓地中取回卡片。场上的卡片是玩家直接可见并且可选的,而卡组或墓地一般存放在后台,不会一直展示给玩家。副控制面板就是在玩家需要时,将卡组或者墓地中的卡片全部加载列举出来,供玩家选择操作。

game_ui_subfield2
这个显示区域是动态元素,每次点击按钮后都需要重新加载内容,所以在html里这一块的容器是留空的(id是select-area):

<div class="card-selection">
   <div id="select-area" class="selection-area"></div>
   <div class="button-area">
     <button class="button" type="button" onclick="sf_buttons('deck')">从牌组中选择(刷新列表)</button>
     <button class="button" type="button" onclick="sf_buttons('p1tomb')">从我方墓地选择(刷新列表)</button>
     <button class="button" type="button" onclick="sf_buttons('p2tomb')">从对方墓地选择(刷新列表)</button>
     <button class="button" type="button" onclick="shuffleDeck()">洗牌</button>
   </div>
 </div>

为了避免一些 bug 和参数方面的混淆,我给副控制面板的卡片显示,卡片选中等功能重新写了一套函数(希望以后能设计更好的架构合并相似功能而不是重构)。

在玩家操作时,我们准备了一个全局对象来记录玩家的选择,因为其他函数需要用到这些信息:

var sf_Card = {
    type: "null",  //卡的来源类型(我方卡组,我方墓地,对方墓地)
    player: "null",  //记录副面板显示卡片的玩家类型
    size: 0  //记录副面板显示卡片的数量
};

动态面板上的主要功能按键分为三种,“从(我方)牌组中选择”,“从我方墓地中选择”,“从对方墓地中选择”。这里为了避免一些问题没有设置 “从对方牌组中选择” 这个功能,需要获取对方卡牌可由对方取出后放置场上再由我方加入手卡,实战中这个功能也并不常用。

三个功能按键都对应同一个函数 sf_buttons,参数只有一个,不同按键传入不同参数值来执行对应功能。主要是根据玩家的选择在显示区域动态创建数个img标签并逐一加载卡组/墓地中的卡片图片。每个img标签的 onmouseover 属性设置显示卡片信息的函数 sf_showInfo,onclick 属性设置选中函数 sf_selecteCard

获取副面板显示区域对象;
清空副面板的全部内容;

创建一个空的临时卡组数组cardset;

如果玩家需要显示的卡片来源于:
	我方卡组:
		拷贝当前我方卡组至cardset;
		sf_Card记录其他相关信息;
	我方墓地:
		拷贝当前我方墓地至cardset;
		sf_Card记录其他相关信息;
	对方墓地:
		拷贝当前对方墓地至cardset;
		sf_Card记录其他相关信息;

遍历cardset中所有卡片url:
	在显示区域创建子元素cardImg,类型是img;
	按循环顺序设置子元素id,这个id序号其实也正好对应卡片在卡组/墓地中的位置;
	设置class,onmouseover,onclick属性;
	设置cardImg的src属性为cardset中的图片rul,加载图片;
	将子元素cardImg添加至父容器selectArea;

/**
 * 选择副面板显示的内容(我方卡组/我方墓地/对方卡组)
 * @param {string} type - button type (P1 deck/P1 tomb/ P2 deck)
 */
function sf_buttons(type) {
    var selectArea = document.getElementById("select-area");
    selectArea.innerHTML = "";  //清空副面板显示框

    var cardset = [];

    switch (type) {
        case "deck":
            cardset = P1Deck;
            sf_Card.player = "player1";
            sf_Card.type = "deck";
            break;
        case "p1tomb":
            cardset = P1Tomb;
            sf_Card.player = "player1";
            sf_Card.type = "p1tomb";
            break;
        case "p2tomb":
            cardset = P2Tomb;
            sf_Card.player = "player2";
            sf_Card.type = "p2tomb";
            break;
        default: break;
    }

    /*副面板显示需显示的内容(我方卡组/墓地/对方墓地的全部卡片) */
    sf_Card.size = cardset.length;
    for (i=0; i<sf_Card.size; i++) {
        var cardImg = document.createElement("img");
        cardImg.id = "sf-card" + i.toString();
        cardImg.setAttribute("class", "card");
        cardImg.setAttribute("onmouseover", "sf_showInfo(this.src)");
        cardImg.setAttribute("onclick", "sf_selectCard(this.id, this.src)");
        cardImg.src = cardset[i];
        selectArea.appendChild(cardImg);
    }
}

再贴下onmouseover设置的显示函数 sf_showInfo

/**
 * 显示副面板中被鼠标指到的卡片
 * @param {string} cardsrc - card img source 
 */
function sf_showInfo(cardsrc) {
    if (cardsrc != emptysrc) {
        element = document.getElementById('card-info');
        element.src = cardsrc;
    }
}

然后是onclick设置的选中函数 sf_selectCard

/**
 * 在副面板中选中卡片
 * @param {int} id - selection field img container id 
 * @param {string} cardsrc - selected card img source
 */
function sf_selectCard(id, cardsrc) {
    if (cardsrc != emptysrc) {
        var CardSrc = "image" + cardsrc.split('image')[1];
        cleanSelected();  //选择卡片之前首先清空场上已选中的卡片样式再更新
        element = document.getElementById(id);
        element.setAttribute("class", "card-selected");

        SelectedCard.player = sf_Card.player;
        SelectedCard.type = sf_Card.type;  //我方卡组/我方墓地/对方墓地
        SelectedCard.cardNo = parseInt(id.replace("sf-card", ""));  //用replace去掉id中的英文字符来获取数字字符
        SelectedCard.cardSrc = CardSrc;  //直接从img容器获取src
    }
}

这个函数主要是更改下副控制面板中的卡片样式,卡片相关信息还是要存到大的全局对象 SelectedCard 中的。

最后演示下效果:

select_from_deck
“从我方墓地选择” 与 “从对方墓地选择” 的效果与此相似。另外前文 sendtoTomb 函数中最后调用了副面板显示其实也是重新使副面板加载墓地内容,这样玩家可以看到刚刚向墓地丢了什么卡:

sf_buttons('p1tomb');  //刷新副面板显示

下一章应该能把功能按键全讲完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值