淘先锋技术网

首页 1 2 3 4 5 6 7

基于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;

});

2a9141cd50e30747661359df64bf7d36.png

56d329bd68a20ae1a21fff574114da13.png

bb33d83755f8e472bd74dce2125ea58d.png