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 - 实机演示

客户端发送消息的具体场景

这章最后总结下游戏客户端中具体哪些函数调用了消息发送。

代码中发送消息的地方均有注释。

(1)抽卡:首先我方抽牌时手牌会增加,需要让对方同步我们的手牌数;

/*抽取牌组最上方一张卡至手卡 */
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();

          /*触发抽卡音效 */
          var snd = new Audio("sound/draw.wav");
          snd.play();

          /**
           * 告知对手哪张手卡卡槽添加了一张卡
           */
          messageHand('add', i);
          break;
        }
    }
}

(2)卡片放置:召唤,覆盖卡片等从手牌向战场放置卡片的操作时,我方战场需要更新,且我方手牌减少,同时调用 messageFieldmessageHand

/**
 * 我方从手牌向场上放置卡片,并发出放置指令 (攻击,防御,背盖防御,发动,盖卡)
 * @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 = "";  //手牌该卡消失

            /*更新战场信息 */
            fieldArrayPly1.FieldCards[cardslot].imgsrc = cardsrc;
            fieldArrayPly1.FieldCards[cardslot].state = placetype;

            /*发出指令,执行更新战场卡片的函数 */
            var fieldID = "p1-field" + cardslot.toString();
            updateField(fieldID, placetype, cardsrc);

            /**
             * 放置后告知对手执行战场更新函数;
             * 放置完成后记得告诉对手哪张手卡消失了;
             * 注意:我方战场变化对对方来说是P2;
             */
            var updateID = "p2-field" + cardslot.toString();
            messageField(placetype, updateID, cardsrc);
            messageHand('reduce', handslot);

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

(3)更变形式:更变战场卡片表示形式时,我方战场会有更新,需告知对方同步;

/**
 * 更变卡片的表示形式
 * 更变顺序为:攻击 -> 防御 -> 背盖 -> 攻击, 盖覆卡 -> 表侧卡 -> 盖覆卡
 * @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;  //更新场上卡片状态信息
        cardstate = "change-" + cardstate;  //为通过更变形式而导致的战场更新操作添加一个标签方便更新函数识别(因为更变形式不触发音效)
        updateField(fieldID, cardstate, cardsrc);  //更新指定卡槽

        /**
         * 告知对手某一卡槽的表示形式发生变化,执行战场更新函数
         */
        var updateID = "p2-field" + (SelectedCard.cardNo).toString();
        messageField(cardstate, updateID, cardsrc);
    }
}

(4)回到手牌:
若将战场卡片返回手牌,战场将更新,我方手牌增加;
若将墓地卡片返回手牌,墓地将更新,我方手牌增加;
若将牌组卡片返回手牌,我方手牌增加;
:回到手牌操作可操作双方战场与墓地,注意参数的填写;

/**
 * 将场上选中的卡牌回到手卡槽
 */
function backtoHand() {
    var cardslot = findEmptySlot('hand');  //寻找我方空的手牌卡槽 
    var cardNo = SelectedCard.cardNo;  //被选中卡牌的序号

    if (cardslot == -1) {
        alert("手牌已满");
    } else {
        var handID = "p1-hand" + (cardslot).toString();
        element = document.getElementById(handID);

        /*## 若卡片来源于场上 */
        if (SelectedCard.type == 'field') {  
            var fieldID;
            
            /*我方 卡牌信息从用于存储被选中卡片信息的对象中获取 */
            if (SelectedCard.player == 'player1') {  
                fieldID = "p1-field" + cardNo.toString();
                element.src = SelectedCard.cardSrc;  //手牌获取被选中的卡片
                fieldArrayPly1.FieldCards[cardNo].imgsrc = "null";  //场上该卡的记录清空
                fieldArrayPly1.FieldCards[cardNo].state = "null";

                /**
                 * 通知对方更新战场信息
                 */
                var updateID = "p2-field" + cardNo.toString();
                messageField("null", updateID, "");
                
            /*对方 卡牌信息从用于存储被选中卡片信息的对象中获取(必须是正面表示的卡片) */
            } else if (SelectedCard.cardSrc != CardBackSrc) {
                fieldID = "p2-field" + cardNo.toString();
                element.src = SelectedCard.cardSrc

                /**
                 * 通知对方更新战场信息
                 */
                var updateID = "p1-field" + cardNo.toString();
                messageField("null", updateID, "");
            } 
            
            /*更新战场信息 */
            updateField(fieldID, "null", "");

            /**
              * 通知对方更新手卡数
              */
            messageHand("add", cardslot);

        /*## 若卡片来源于卡组或墓地 */
        } else if (SelectedCard.type != 'hand') {  
            /*我方牌组/墓地的卡片回到我方手牌 */
            if (SelectedCard.player == 'player1') {
                element.src = SelectedCard.cardSrc;  //手牌获取该卡片src

                /*更新 卡组/墓地 列表并刷新副面板显示 */
                if (sf_Card.type == 'deck') {
                    P1Deck.splice(cardNo, 1);  //剔出卡组中的被选中卡片
                    sf_buttons('deck');  //刷新副面板显示
                } else if (sf_Card.type == 'p1tomb') {
                    P1Tomb.splice(cardNo, 1);
                    sf_buttons('p1tomb');

                    /**
                     * 通知对方玩家更新我方墓地(对于对方来说,我方是player2)
                     */
                    messageTomb("reduce", "player2", cardNo, SelectedCard.cardSrc);
                }

                /**
                * 通知对方更新手卡数
                */
                messageHand("add", cardslot);

            /*对方墓地的卡片回到我方手牌 */
            } else if (SelectedCard.player == 'player2') {
                element.src = SelectedCard.cardSrc;  //手牌获取该卡片src
                P2Tomb.splice(cardNo, 1);  //剔出对方墓地中的对应卡片
                sf_buttons('p2tomb');

                /**
                 * 通知对方玩家我方手卡数及对方墓地 (对于对方来说,他自己是player1)
                 */
                messageHand("add", cardslot);
                messageTomb("reduce", "player1", cardNo, SelectedCard.cardSrc);
            }
        }

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

(5)回到牌组:
若将我方手牌返回牌组,我方手牌减少;
若将我方战场卡片返回牌组,我方战场将更新;

/**
 * 将我方被选中的卡片返回卡组
 * 默认回到卡组最上方
 */
function backtoDeck() {
    if (SelectedCard.player == 'player1') {  //只允许将我方的卡片送回牌组
        var cardNo = SelectedCard.cardNo;

        /*手牌卡片回到卡组 */
        if (SelectedCard.type == 'hand') {  
            var handID = "p1-hand" + cardNo.toString();
            element = document.getElementById(handID);
            element.src = ""; //手牌被选中卡片消失
            P1Deck.push(SelectedCard.cardSrc);  //卡片src存回卡组

            /**
             * 告知对手手卡变动
             */
            messageHand("reduce", cardNo);
            alert("卡片已回到卡组最上方!");

        /*场上卡片回到卡组 */
        } else if (SelectedCard.type == 'field') {  
            var fieldID = "p1-field" + cardNo.toString();
            P1Deck.push(SelectedCard.cardSrc);  //卡片src存回卡组
            fieldArrayPly1.FieldCards[cardNo].imgsrc = "null";  //场上该卡的记录清空
            fieldArrayPly1.FieldCards[cardNo].state = "null";

            /*更新战场 */
            updateField(fieldID, "null", "");

            /**
             * 通知对方玩家更新我方战场
             */
            var updateID = "p2-field" + cardNo.toString();
            messageField("null", updateID, "");
            alert("卡片已回到卡组最上方!");

        } else {
            alert("请先将卡片拿到手牌再放回卡组");  //防止直接从卡组或墓地中选卡放回卡组
        }

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

(6)送去墓地:
若将我方手牌送入墓地,我方墓地将更新,我方手牌减少;
若将我方战场卡片送入墓地,我方墓地将更新,我方战场将更新;

/**
 * 将我方的卡片(场上/手牌)送去墓地
 */
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');  //刷新副面板显示
    }
}

整个游戏制作流程到这里就基本结束啦,架起服务端,连上客户端就可进行开黑。记住需要提前在代码里设置卡组信息,玩家信息,ip地址等相关参数。有时间可以把这些基本参数设置转到UI界面里来,完善游戏的完成度。

var P1DeckName = "Deck_KaiMa";  //我方牌组名
var P1DeckNum = 50;  //我方牌组卡片数量
var playerID = "player1";  //独立玩家ID
var ws = new WebSocket("ws://localhost:9999/");

下一章就做一个小demo演示,以及讨论一些待完善的细节,感谢耐心阅读。

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

推荐阅读更多精彩内容