目录:

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

卡片选中系统

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

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中显示一张卡背的图片以表示背盖防御或是盖覆状态:

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


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

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}}}
}

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


在选中函数中,每次执行功能前会调用一次选择清空函数 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的左上角显示卡片的详细信息,其实就是放大版的卡牌图片。

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

先来回顾下卡片信息显示板块的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),因为我们不需要看到对方背盖表示的卡片,如果要看需要让对方翻开。


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

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

HTML+JS+websocket 实现联机“游戏王”对战(五)- 卡片选中系统相关推荐

  1. HTML+JS+websocket 实现联机“游戏王”对战(十三)- 实机演示视频

    最后一章啦,这章主要放个demo演示,然后讨论一些待改进的细节问题. 目录: 游戏王联机卡牌对战 1 - 前言 游戏王联机卡牌对战 2 - 联机模式 游戏王联机卡牌对战 3 - 界面布局 游戏王联机卡 ...

  2. HTML+JS+websocket 实现联机“游戏王”对战(一)

    目录: 游戏王联机卡牌对战 1 - 前言 游戏王联机卡牌对战 2 - 联机模式 游戏王联机卡牌对战 3 - 界面布局 游戏王联机卡牌对战 4 - 卡组系统 游戏王联机卡牌对战 5 - 卡片选中系统 游 ...

  3. HTML+JS+websocket 实现联机“游戏王”对战(十)- 搭建游戏服务端

    目录: 游戏王联机卡牌对战 1 - 前言 游戏王联机卡牌对战 2 - 联机模式 游戏王联机卡牌对战 3 - 界面布局 游戏王联机卡牌对战 4 - 卡组系统 游戏王联机卡牌对战 5 - 卡片选中系统 游 ...

  4. HTML+JS+websocket 实现联机“游戏王”对战(九)- 实现简单websocket通信

    目录: 游戏王联机卡牌对战 1 - 前言 游戏王联机卡牌对战 2 - 联机模式 游戏王联机卡牌对战 3 - 界面布局 游戏王联机卡牌对战 4 - 卡组系统 游戏王联机卡牌对战 5 - 卡片选中系统 游 ...

  5. node.js Websocket消息推送---GoEasy

    Goeasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送!个人感觉goeasy推送更稳定,推送速度快,代码简单易懂上手快 浏览器兼容性:GoEasy推送支持websocket 和 ...

  6. 基于Node.js + WebSocket 的简易聊天室

    代码地址如下: http://www.demodashi.com/demo/13282.html Node.js聊天室运行说明 Node.js的本质就是运行在服务端的JavaScript.Node.j ...

  7. socket服务器断开消息,详解JS WebSocket断开原因和心跳机制

    1.断开原因 WebSocket断开的原因有很多,最好在WebSocket断开时,将错误打印出来. ws.onclose = function (e) { console.log('websocket ...

  8. 从零学习游戏服务器开发(一) 从一款多人联机实时对战游戏开始

    写在前面的话 经常有学生或者初学者问我如何去阅读和学习一个开源软件的代码,也有不少朋友在工作岗位时面对前同事留下的项目,由于文档不完善.代码注释少.工程数量大,而无从下手.本文将来通过一个多人联机实时 ...

  9. 使用SpringBoot及Construct2的WebSocket制作联机游戏(二)

    前情概要: 使用SpringBoot及Construct2的WebSocket制作联机游戏(一) 一.介绍: 1.SpringBoot服务端添加登录及注册接口,并进行Postman测试 2.Sprin ...

最新文章

  1. 使程序在后台执行,并将日志输出至文件
  2. mysql运维工资_MySQL运维踩坑
  3. 打不死的小强机器人来了,向心加速度堪比猎豹,能抵抗自身数百倍重量碾压...
  4. mysql sleep 5908_mysql连接卡死,很多线程sleep状态,导致CPU中mysqld占用率极高(问题原因还待考证)...
  5. Flink 1.11 与 Hive 批流一体数仓实践
  6. Linux(centos)下安装JDK
  7. javaweb超市仓库管理系统
  8. 【原创】《管理的实践》阅读有感
  9. Differential Geometry之第二章曲线的局部理论
  10. hdu_5145_NPY and girls(莫队算法+组合)
  11. 免费不限速不限存储的网盘推荐
  12. fluxion-wifi破解/钓鱼
  13. 嵌入式linux学习路径--新手入门篇
  14. 读jquery 权威指南[2]-事件
  15. Request对象的一般用法
  16. 混沌测试工具chaosblade介绍及常用命令汇总
  17. 企鹅号绑定微信公众号 问题 微信授权失败!输入的微信号和微信公众平台设置的不一致
  18. 2022安徽安全员B考试单选题库预测分享
  19. 文件在回收站被清空要怎么恢复回来?
  20. 从前端菜鸟到大V的成长经验分享

热门文章

  1. 麦当劳中国公司更名“金拱门”;上海首批无人店已关闭;谷歌地图推出“减肥”功能丨价值早报
  2. 基于XDOC云服务的标准公文模板【纪要】
  3. 23年2月CCF会议截稿8条-SACMAT2023/UAI2023/Euro-Par2023/ASAP2023/ICCCN2023/MobHoc2023/ICCBR2023/PETS2023
  4. JAVA:实现二进制转十六进制算法(附完整源码)
  5. 如果要你选择某读书APP的图标,你会选择哪一个?
  6. 虚幻4学习笔记(2)BSP画刷
  7. 共享经济带动共享网红?共享网红的未来如何?
  8. 提供源码:java获取节假日、工作日,存入数据库,查找指定日期前一天,后一天。
  9. Android开发 之 曲线运动动画(贝塞尔曲线)
  10. [WIN32]IsWindowVisible