[网页五子棋][对战模块]前后端交互接口(建立连接、连接响应、落子请求/响应),客户端开发(实现棋盘/棋子绘制)

文章目录

约定前后端交互接口

对战模块和匹配模块使用的是两套逻辑,使用不同的 websocket 的路径进行处理,做到更好的耦合

建立连接

ws://127.0.0. 1:8080/game

建立连接响应

服务器要生成一些游戏的初始信息,通过这个响应告诉客户端

json 复制代码
{
	message: 'gameReady',  // 消息的类别:游戏就绪
	ok: true,
	reason: '',
	roomId: '12345678',    // 玩家所处在的房间id
	thisUserId: 1,         // 玩家自己的id
	thatUserId: 2,         // 玩家的对手的id
	whiteUser: 1           // 哪个玩家执白字(先手)
};
  • 这些都是玩家匹配成功之后,要由服务器生成的,然后把这个内容返回给浏览器

针对"落子"的请求和响应

请求:

json 复制代码
{
	message: 'putChess',
	userId: 1,
	row: 0,        // 落子的坐标,往哪一行,哪一列来落子
}
  • 建议使用行和列,而不是 xy
    • row => ycol => x
    • 后面的代码中,需要使用二维数组来表示这个棋盘,通过下标取二维数组
    • [row] => [y][col] => [x]
    • 如果使用 xy,就很别扭,和我们日常表示相悖

响应:

json 复制代码
{
	message: 'putChess',
	userId: 1,
	row: 0,
	col: 0,
	winner: 0
}
  • winner 表示当前是否分出胜负
    • 如果 winner0,表示胜负未分,还需要继续往下对战
    • 如果 winner0,表示当前的获胜方的用户 id

以上交互接口的设计,其实也不一定非得按照刚才这样写的这种格式来进行约定,也可以有其他的约定方式

  • 不管是哪种格式,只要能够解决我们的问题,并且编写代码的时候简单方便即可

客户端开发

实现棋盘/棋子绘制

创建 js/app.js

  • 我们不需要理解这部分内容,只需要复制粘贴即可
  • 使用一个二维数组来表示棋盘。虽然胜负是通过服务器判定的,但是客户端的棋盘可以避免"一个位置重复落子"这样的情况
  • oneStep 函数起到的效果是在一个指定的位置上绘制一个棋子,可以区分出绘制白子还是黑子,参数是横坐标和纵坐标,分别对应行和列
  • onlick 来处理用户点击事件,当用户点击的时候通过这个函数来控制绘制棋子
  • me 变量用来表示当前是否轮到我落子;over 变量用来表示游戏结束
  • 这个代码中会用到一个背景图,放到 image 目录中即可
js 复制代码
// 定义全局变量,表示游戏初始化信息
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();

canvasHTML5 引入的一个标签,画布

  • "可以在画布上画画"
  • 此处棋盘和棋子,都是画上去的。canvas 这个标签有一组配套的 jscanvas api,通过这个 api 就可以实现一些"画画"的效果
    • 例如,展示一个棋盘,就画很多的直线,就能构成棋盘的网格
    • 表示一个棋子,就画一个圆圈,并填充上颜色
    • 还需要响应点击事件,在鼠标落子的地方来画圆圈
  • canvas api 里面能做的事情比较多,比较复杂,不是重点

部分逻辑解释

  • 表示当前游戏中的棋盘,通过这个棋盘来表示当前哪个位置上有子了
  • 当玩家点击的时候,如果有子的位置就不能再继续落子了
  • 0 用来表示是空闲位置,非 0 表示已经有子了
  • 针对 chess (棋盘 canvas) 设定了点击回调
  • e 是点击回调中的事件参数,这里就会记录点击的实际位置 (坐标)
  • Math.floor(x/30) 是为了让点击操作能够对应到网格线上
    • 总体的棋盘尺寸是 450px * 450px,整个棋盘上面是 15行15列
    • 每一行每一列占用的尺寸就是 30px
  • oneStep 就是走一步 (里面绘制一个棋子)
  • 标记为 1,就是这个位置有子,不能落子了
相关推荐
DokiDoki之父5 分钟前
多线程—飞机大战排行榜功能(2.0版本)
android·java·开发语言
高山上有一只小老虎11 分钟前
走方格的方案数
java·算法
whatever who cares12 分钟前
Java 中表示数据集的常用集合类
java·开发语言
JavaArchJourney1 小时前
TreeMap 源码分析
java
whitepure1 小时前
万字详解Java中的IO及序列化
java·后端
还梦呦1 小时前
2025年09月计算机二级Java选择题每日一练——第一期
java·开发语言
与火星的孩子对话1 小时前
Unity高级开发:反射原理深入解析与实践指南 C#
java·unity·c#·游戏引擎·lucene·反射
花开富贵ii2 小时前
代码随想录算法训练营四十六天|图论part04
java·数据结构·算法·图论
Miraitowa_cheems2 小时前
LeetCode算法日记 - Day 15: 和为 K 的子数组、和可被 K 整除的子数组
java·数据结构·算法·leetcode·职场和发展·哈希算法
答题卡上的情书2 小时前
java第一个接口
java·开发语言