基于websocket实现双人联机俄罗斯方块游戏,逻辑思路整理如下
思路整理
1.游戏开始,双方都收到start消息,同时调用start方法
2.start方法中,调用game.init方法,同时发送init消息给server,server收到后会转发给另一个游戏玩家
3.另一个游戏玩家在remote.js中接收init,会驱动对方去调用start方法(next消息同init消息)
整体代码
local.js
remote.js
wsServer.js
script.js
思路整理
1.游戏开始,双方都收到start消息,同时调用start方法
socket.on('start', function () {
document.getElementById('waiting').inneHTML = '';
start();
});
2.start方法中,调用game.init方法,同时发送init消息给server,server收到后会转发给另一个游戏玩家
game.init(doms, type, dir);
socket.emit('init', {
type: type,
dir: dir
});
bindKeyEvent();
var t = generateType();
var d = generateDir();
game.performNext(t, d);
socket.emit('next', {
type: t,
dir: d
})
//wsServer.js中 server收到消息
socket.on('init', function (data) {
// 接收消息后,将其匹配给另一个socket
if (socket.clientCount % 2 == 0) {
socketMap[socket.clientCount - 1].emit('init', data);
} else {
socketMap[socket.clientCount + 1].emit('init', data);
}
});
3.另一个游戏玩家在remote.js中接收init,会驱动对方去调用start方法(next消息同init消息)
var bindEvents = function () {
socket.on('init', function (data) {
// 调用start,接收init消息和传递的参数,实现两个用户相连接(在对方区域中调用了start)
start(data.type, data.dir);
});
socket.on('next', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.performNext(data.type, data.dir);
});
}
整体代码
local.js
var Local = function (socket) {
// 游戏对象
var game;
// 时间间隔 200毫秒
var INTERVAL = 500;
// 定时器
var timer = null;
// 绑定键盘事件
// 时间计数器
var timeConut = 0;
var time = 0;
var bindKeyEvent = function () {
document.onkeydown = function (e) {
if (e.keyCode == 38) {
// 向上
game.rotate();
socket.emit("rotate");
} else if (e.keyCode == 39) {
// 向右
game.right();
socket.emit("right");
} else if (e.keyCode == 40) {
// 向下
game.down();
socket.emit("down");
} else if (e.keyCode == 37) {
// 向左
game.left();
socket.emit("left");
} else if (e.keyCode == 32) {
// 空格键
game.fall();
socket.emit("fall");
}
};
};
// 随机生成干扰行
var generateBotLine = function (lineNum) {
var lines = [];
for (var i = 0; i < lineNum; i++) {
var line = [];
for (var j = 0; j < 10; j++) {
line.push(Math.ceil(Math.random() * 2) - 1); // 生成 0 1 随机数
}
lines.push(line);
}
return lines;
};
// 移动
var move = function () {
timeFunc();
// 不能下降再调用
if (!game.down()) {
// 落下的方块固定
game.fixed();
socket.emit("fixed");
var line = game.checkClear();
if (line) {
// 行数不为0,则传入addScore函数
game.addScore(line);
socket.emit("line", line);
if (line > 1) {
var bottomLines = generataBottomLines(line);
socket.emit('bottomLines', bottomLines);
}
}
var gameOver = game.checkGameOver();
if (gameOver) {
game.gameOver(false);
document.getElementById("remote_gameover").innerHTML = "你赢了";
socket.emit("lose");
stop();
} else {
var t = generateType();
var d = generateDir();
game.performNext(t, d);
socket.emit("next", {
type: t,
dir: d,
});
}
} else {
socket.emit("down");
}
};
// 计时函数
var timeFunc = function () {
timeConut = timeConut + 1;
if (timeConut == 5) {
timeConut = 0;
time = time + 1;
// 将更新的时间传入界面
game.setTime(time);
socket.emit("time", time);
}
};
// 随机生成下一个方块
var generateType = function () {
// 随机生成0-6的整数
return Math.ceil(Math.random() * 7) - 1;
};
// 随机生成旋转次数
var generateDir = function () {
// 随机生成0-3的整数
return Math.ceil(Math.random() * 4) - 1;
};
// 开始
var start = function () {
var doms = {
gameDiv: document.getElementById("local_game"),
nextDiv: document.getElementById("local_next"),
timeDiv: document.getElementById("local_time"),
scoreDiv: document.getElementById("local_score"),
resultDiv: document.getElementById("local_gameover"),
};
game = new Game();
// 定义变量,用websocket将随机方向种类和方向generateType(), generateDir()通过对象形式到init,
var type = generateType();
var dir = generateDir();
game.init(doms, type, dir);
socket.emit("init", {
type: type,
dir: dir,
});
bindKeyEvent();
var t = generateType();
var d = generateDir();
game.performNext(t, d);
socket.emit("next", {
type: t,
dir: d,
});
timer = setInterval(move, INTERVAL);
};
// 结束,关闭计时
var stop = function () {
if (timer) {
clearInterval(timer);
timer = null;
}
document.onkeydown = null;
};
// 不用导出start了,用socket.on监听start,收到后,通过waiting设置页面显示为空;调用start开始游戏
socket.on("start", function () {
document.getElementById("waiting").innerHTML = "游戏开始!";
start();
});
socket.on("lose", function () {
game.gameOver(true);
stop();
});
socket.on("leave", function () {
document.getElementById("local_gameover").innerHTML = "对方掉线啦";
document.getElementById("remote_gameover").innerHTML = "已掉线";
stop();
});
socket.on('bottomLines', function (data) {
game.addTailLines(data);
socket.emit('addTailLines', data);
})
};
remote.js
var Remote = function (socket) {
var game;
var bindEvents = function () {
socket.on('init', function (data) {
// 调用start,接收init消息和传递的参数,实现两个用户相连接(在对方区域中调用了start)
start(data.type, data.dir);
});
socket.on('next', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.performNext(data.type, data.dir);
});
socket.on('rotate', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.rotate();
});
socket.on('right', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.right();
});
socket.on('left', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.left();
});
socket.on('down', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.down();
});
socket.on('line', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.checkClear();
game.addScore(data);
});
socket.on('fall', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.fall();
});
socket.on('fixed', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.fixed();
});
socket.on('time', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.setTime(data);
});
socket.on('lose', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.gameOver(false);
});
socket.on('addTailLines', function (data) {
// 驱动对方游戏区域也调用performNext函数
game.addTailLines(data);
});
}
var start = function (type, dir) {
var doms = {
gameDiv: document.getElementById('remote_game'),
nextDiv: document.getElementById('remote_next'),
timeDiv: document.getElementById('remote_time'),
scoreDiv: document.getElementById('remote_score'),
resultDiv: document.getElementById('remote_gameover')
}
game = new Game();
game.init(doms, type, dir);
}
bindEvents();
}
wsServer.js
var app = require("http").createServer();
var io = require("socket.io")(app);
var PORT = 3000;
// 客户端计数
var clientCount = 0;
// 保存客户端socket
var socketMap = {};
app.listen(PORT);
var bindListener = function (socket, event) {
socket.on(event, function (data) {
if (socket.clientNum % 2 == 0) {
if (socketMap[socket.clientNum - 1])
socketMap[socket.clientNum - 1].emit(event, data);
} else {
if (socketMap[socket.clientNum + 1])
socketMap[socket.clientNum + 1].emit(event, data);
}
});
}
io.on("connection", function (socket) {
// 用户连接进来后先进行客户加1
clientCount++;
socket.clientNum = clientCount;
socketMap[clientCount] = socket;
// 是单数个进来的用户需要等待
if (clientCount % 2 == 1) {
// 发送wait消息
socket.emit("waiting", "等待玩家进入……");
} else {
if (socketMap[clientCount - 1]) {
socket.emit("start");
socketMap[(clientCount - 1)].emit("start");
} else {
socket.emit('leave');
}
}
bindListener(socket, 'init');
bindListener(socket, 'next');
bindListener(socket, 'rotate');
bindListener(socket, 'down');
bindListener(socket, 'right');
bindListener(socket, 'left');
bindListener(socket, 'fall');
bindListener(socket, 'fixed');
bindListener(socket, 'line');
bindListener(socket, 'time');
bindListener(socket, 'lose');
bindListener(socket, 'bottomLines');
bindListener(socket, 'addTailLines');
socket.on('disconnect', function () {
if (socket.clientNum % 2 == 0) {
if (socketMap[socket.clientNum - 1])
socketMap[socket.clientNum - 1].emit('leave');
} else {
if (socketMap[socket.clientNum + 1])
socketMap[socket.clientNum + 1].emit('leave');
}
delete(socketMap[socket.clientNum]);
});
});
console.log("websocket listening on port" + PORT);
script.js
var socket = io('ws://localhost:3000');
// 创建local对象并调用,传入socket对象
var local = new Local(socket);
var remote = new Remote(socket);
socket.on('waiting', function (str) {
document.getElementById('waiting').innerHTML = str;
});