TOC
预览
技术栈
主要利用了websocket,由于ws天生是可以跨域的,所以我将静态页面部署在了gh-page上,而负责后端交互的部分我放在了我的VPS上。
socket.io
是一个优秀的ws调用库,可以同时运行在客户端与服务端。
简单的贴一下服务端的代码
let app = require("http").createServer();
let io = require("socket.io")(app);
let port = 3001;
let clientCount = 0;
let socketMap = {};
app.listen(port);
function bindEvent(socket, event) {
socket.on(event, data => {
if (socket.clientID % 2 === 0) {
// 如果是第二个客户端发来的事件 就发送给第一个客户端
if (socketMap[socket.clientID - 1]) {
socketMap[socket.clientID - 1].emit(event, data);
}
} else {
// 相反
if (socketMap[socket.clientID + 1]) {
socketMap[socket.clientID + 1].emit(event, data);
}
}
});
}
io.on("connection", socket => {
clientCount = clientCount + 1;
socket.clientID = clientCount;
socketMap[clientCount] = socket;
if (clientCount % 2 === 1) {
console.log("wating");
socket.emit("waiting", "等待玩家进入");
} else {
console.log("start");
socket.emit("start");
if (socketMap[clientCount - 1]) {
socketMap[clientCount - 1].emit("start");
} else {
//玩家1进入马上又离开 玩家2进入 玩家2显示离线
socket.emit('leave')
}
}
// 绑定会进行两次 同时建立两个不同的socket对象
bindEvent(socket, "init");
bindEvent(socket, "next");
bindEvent(socket, "rotate");
bindEvent(socket, "right");
bindEvent(socket, "left");
bindEvent(socket, "down");
bindEvent(socket, "line");
bindEvent(socket, "fall");
bindEvent(socket, "addScope");
bindEvent(socket, "lose");
bindEvent(socket, "addLineSucces")
socket.on("disconnect", () => {
//确保删除不报错
if (socket.clientID % 2 === 0) {
if (socketMap[socket.clientID - 1]) {
socketMap[socket.clientID - 1].emit("leave");
}
} else {
if (socketMap[socket.clientID + 1]) {
socketMap[socket.clientID + 1].emit("leave");
}
}
delete socketMap[socket.clientID];
});
});
console.log("ws Server listening to prot " + port);
客户端的原理
俄罗斯方块的本质就是一个二维数组到视图的一个隐射,整体方块槽与一个个方块其实都是一种二维数组。内部都是一些数组,比如0就对应着空,1可能就对应着绿色的一个实体小块。而方块的移动就对应了把小的二维数组插入到大的二维数组的内部,进行一个内部值的更新。当更新完毕我们就通知一些函数按照值来重新刷新视图的class。
要注意的事项
- 在方块移动(比如正常下落,手动旋转,移动)之前要进行一次模拟碰撞检测,作出正确的应对
- 不同颜色的方块应该在生成前加入一随机的值 比如3代表蓝色 那某个独立方块的内部值可能需要3+random number 这样才能做到不予其他蓝色方块穿透。
- 方块的旋转我选择的是手动写这个方块的其他状态(四种二维数组提前写好)。不然旋转算法可能很复杂,没必要
- 如何同时显示两个客户端(一个自己的一个对手的)依赖的就是两个game组件来启动,而remote的game通过远程发送的指令来控制。本地则是用户的操作来控制,每当本地操作产生就向远程发生一个事件来转发给另外一个客户端。
- 在a成功消除一行后会给b增加一行干扰行,不然游戏可能很难结束。
最后,合理的抽象极其重要,这将是你减少代码量和复杂度最直接的方式。
总结
作为一项新技术,websocket很好的弥补了HTTP想要长连接必须使用长伦询的不便。