
对战模块和匹配模块使用的是两套逻辑,使用不同的
websocket的路径进行处理,做到更好的耦合
ws://127.0.0. 1:8080/game
服务器要生成一些游戏的初始信息,通过这个响应告诉客户端
{
message: 'gameReady', // 消息的类别:游戏就绪
ok: true,
reason: '',
roomId: '12345678', // 玩家所处在的房间id
thisUserId: 1, // 玩家自己的id
thatUserId: 2, // 玩家的对手的id
whiteUser: 1 // 哪个玩家执白字(先手)
};请求:
{
message: 'putChess',
userId: 1,
row: 0, // 落子的坐标,往哪一行,哪一列来落子
}x 和 y row => y,col => x[row] => [y],[col] => [x]x 和 y,就很别扭,和我们日常表示相悖响应:
{
message: 'putChess',
userId: 1,
row: 0,
col: 0,
winner: 0
}winner 表示当前是否分出胜负 winner 为 0,表示胜负未分,还需要继续往下对战winner 非 0,表示当前的获胜方的用户 id以上交互接口的设计,其实也不一定非得按照刚才这样写的这种格式来进行约定,也可以有其他的约定方式
创建 js/app.js
oneStep 函数起到的效果是在一个指定的位置上绘制一个棋子,可以区分出绘制白子还是黑子,参数是横坐标和纵坐标,分别对应行和列onlick 来处理用户点击事件,当用户点击的时候通过这个函数来控制绘制棋子me 变量用来表示当前是否轮到我落子;over 变量用来表示游戏结束image 目录中即可// 定义全局变量,表示游戏初始化信息
let gameInfo = {
roomId: null,
thisUserId: null,
thatUserId: null,
isWhite: true,
}
//
// 设定界面显示相关操作
//
function setScreenText(me) {
let screen = document.querySelector('#screen');
if (me) {
screen.innerHTML = "轮到你落子了!";
} else {
screen.innerHTML = "轮到对方落子了!";
}
}
//
// 初始化 websocket//
// TODO
//
// 初始化一局游戏
//
function initGame() {
// 是我下还是对方下. 根据服务器分配的先后手情况决定
let me = gameInfo.isWhite;
// 游戏是否结束
let over = false;
let chessBoard = [];
//初始化chessBord数组(表示棋盘的数组)
for (let i = 0; i < 15; i++) {
chessBoard[i] = [];
for (let j = 0; j < 15; j++) {
chessBoard[i][j] = 0;
}
}
let chess = document.querySelector('#chess');
let context = chess.getContext('2d');
context.strokeStyle = "#BFBFBF";
// 背景图片
let logo = new Image();
logo.src = "image/五子棋棋盘.jpg"
logo.onload = function () {
context.drawImage(logo, 0, 0, 450, 450);
initChessBoard();
}
// 绘制棋盘网格
function initChessBoard() {
for (let i = 0; i < 15; i++) {
context.moveTo(15 + i * 30, 15);
context.lineTo(15 + i * 30, 430);
context.stroke();
context.moveTo(15, 15 + i * 30);
context.lineTo(435, 15 + i * 30);
context.stroke();
}
}
// 绘制一个棋子, me 为 true function oneStep(i, j, isWhite) {
context.beginPath();
context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
context.closePath();
var gradient = context.createRadialGradient(15 + i * 30 + 2, 15 + j * 30 - 2, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0);
if (!isWhite) {
gradient.addColorStop(0, "#0A0A0A");
gradient.addColorStop(1, "#636766");
} else {
gradient.addColorStop(0, "#D1D1D1");
gradient.addColorStop(1, "#F9F9F9");
}
context.fillStyle = gradient;
context.fill();
}
chess.onclick = function (e) {
if (over) {
return;
}
if (!me) {
return;
}
let x = e.offsetX;
let y = e.offsetY;
// 注意, 横坐标是列, 纵坐标是行
let col = Math.floor(x / 30);
let row = Math.floor(y / 30);
if (chessBoard[row][col] == 0) {
// 发送坐标给服务器, 服务器要返回结果
send(row, col);
// 留到浏览器收到落子响应的时候再处理(收到响应再来画棋子)
oneStep(col, row, gameInfo.isWhite);
chessBoard[row][col] = 1;
}
}
// TODO 实现发送落子请求逻辑和处理落子响应逻辑
}
initGame();canvas 是 HTML5 引入的一个标签,画布
canvas 这个标签有一组配套的 js 的 canvas api,通过这个 api 就可以实现一些“画画”的效果 canvas api 里面能做的事情比较多,比较复杂,不是重点
0 用来表示是空闲位置,非 0 表示已经有子了
chess (棋盘 canvas) 设定了点击回调e 是点击回调中的事件参数,这里就会记录点击的实际位置 (坐标)Math.floor(x/30) 是为了让点击操作能够对应到网格线上 450px * 450px,整个棋盘上面是 15行,15列30pxoneStep 就是走一步 (里面绘制一个棋子)