前言
又过了三个月,咳咳咳……
这次我决定录一个视频,如果不想看文字的朋友,可以翻到最下面看视频,内容和文字差不多。
线上地址:http://cardgame.xiejingyang.com
github:https://github.com/xieisabug/card-game
正文
首先来做攻击效果,也就是卡牌冲过去,再回来。这属于动画效果,我这里就不造轮子了,直接找一个现成的好用点的动画库,这里我用的是velocity-animate,首先进行安装。
npm install --save velocity-animate
先取到页面上的两个卡牌容器,用于遍历双方桌面上的卡牌dom:
this.myCardAreaDom = document.querySelector(".my-card-area"); this.otherCardAreaDom = document.querySelector(".other-card-area");
之后要寻找出需要攻击的卡牌,思路是:在mouseup的监听事件中,如果处于选择攻击目标的状态,那么判断松开鼠标的点是在哪个卡牌的box里,就是攻击的哪个卡牌。
window.onmouseup = (e) => { if (window.isAttackDrag) { window.isAttackDrag = false; this.showCanvas = false; this.canvasContext.clearRect(0, 0, this.windowWidth, this.windowHeight); let x = e.pageX, // 鼠标松开的x y = e.pageY, // 鼠标松开的y k = -1; // 用于记录找到的卡牌的index this.otherCardAreaDom.childNodes.forEach(cd => { // 循环遍历对手的卡牌dom let top = cd.offsetTop, width = cd.offsetWidth, left = cd.offsetLeft, height = cd.offsetHeight; if (x > left && x < (left + width) && y > top && y < (top + height)) { // 边缘检测 k = cd.dataset.index; } }); } }
找到要攻击的卡牌之后,写出动画代码,卡牌位移冲过去,击中后返回原位置,其中就是要找到translate的偏移量,然后做css动画:
attackAnimate(from, to) { let myDom = this.myCardAreaDom.childNodes[from]; let otherDom = this.otherCardAreaDom.childNodes[to]; let h = otherDom.offsetLeft - myDom.offsetLeft; // 偏移x let v = otherDom.offsetTop + otherDom.offsetHeight - myDom.offsetTop - myDom.parentElement.offsetTop; // 偏移y Velocity(myDom, { translateX: h, translateY: v }, { easing: 'ease-in', duration: 200, begin: () => { myDom.style['transition'] = 'all 0s'; } }).then(el => { return Velocity(el, { translateX: 0, translateY: 0 }, { easing: 'ease-out', duration: 300, complete: () => { myDom.style['transition'] = 'all 0.2s'; } }) }) },
在边缘检测完毕的时候,运行动画:
if (x > left && x < (left + width) && y > top && y < (top + height)) { // 边缘检测 k = cd.dataset.index; this.attackAnimate(0, k); }
但是这样仅仅只是单机上完成了,不仅对方不知道你进行了攻击,也将逻辑放在了客户端,这样很不安全,现在就需要服务端来进行数据的计算,客户端只做效果的展示。
那么先处理一下攻击事件,在客户端发起攻击的时候,暂时不需要做过多的处理,注释之前的动画代码,直接发送攻击事件给后端:
attackCard(k) { if (this.isMyTurn && this.currentTableCardK !== -1) { this.socket.emit("COMMAND", { // 使用COMMAND,集中处理 type: "ATTACK_CARD", r: this.roomNumber, myK: this.currentTableCardK, attackK: k }); this.resetAllCurrentCard(); } },
if (x > left && x < (left + width) && y > top && y < (top + height)) { // 边缘检测 k = cd.dataset.index; // this.attackAnimate(0, k); this.attackCard(k); }
这里我将socket的事件,统一用”COMMAND”进行处理,这样后台进行处理的时候,代码比较好管理。
后端修改之前的代码,处理COMMAND命令:
socketServer.on('connection', function (socket) { console.log("connect one on :" + new Date().toLocaleString()); socket.on('COMMAND', function () { let args = Array.prototype.slice.call(arguments); args.push(socket, socketServer); handleSynchronousClient.apply(this, args); }); // other code …… });
module.exports = function handleSynchronousClient(args, socket, socketServer) { switch (args.type) { case "CONNECT": connect(args, socket, socketServer); break; case "ATTACK_CARD": attackCard(args, socket); break; } };
可以看到,修改之后用COMMAND命令之后可以用switch判断type,集中的在一块地方处理对战的所有指令。connect就是将之前的CONNECT命令的处理拷贝过来,attackCard是处理攻击的逻辑,由于对战的卡牌等数据都没有设计,所以使用最简单的转发,将攻击卡牌的事件分发给对战的双方:
function attackCard(args, socket) { let roomNumber = args.r, myK = args.myK, attackK = args.attackK, card, attackCard; if (!memoryData[roomNumber]) { return } let belong = memoryData[roomNumber]["one"].socket.id === socket.id ? "one" : "two"; // 判断当前是哪个玩家出牌 let other = memoryData[roomNumber]["one"].socket.id !== socket.id ? "one" : "two"; memoryData[roomNumber][belong].socket.emit("ATTACK_CARD", { k: attackK }); memoryData[roomNumber][other].socket.emit("ATTACK_CARD", { k: attackK }); }
前端处理后端的转发:
this.socket.on("ATTACK_CARD", (param) => { this.attackAnimate(0, param.k) });
效果:
这样就完成了一次攻击,但是卡牌的血量还没有扣减,这就涉及到要设计卡牌了,下一章着重讲一下卡牌的数据设计和发牌的逻辑。
0 条评论