项目实战--网页五子棋(游戏房间)(6)

上期我们基本完成了匹配功能,这期我们来实现游戏房间相关的代码

由于同时会存在多个游戏房间,我们实现游戏房间时可以再实现一个房间管理器,用于管理游戏房间,我们约定房间管理器通过哈希表来管理游戏房间。

1. 游戏房间实体类

java 复制代码
public class Room {
    private String roomId;
    private User user1;
    private User user2;

    public Room() {
        roomId = UUID.randomUUID().toString();
    }
    public String getRoomId() {
        return roomId;
    }

    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }

    public User getUser1() {
        return user1;
    }

    public void setUser1(User user1) {
        this.user1 = user1;
    }

    public User getUser2() {
        return user2;
    }

    public void setUser2(User user2) {
        this.user2 = user2;
    }
}

这里我们使用UUID来作为房间ID,方便存进哈希表时作为键

2. 房间管理器实体类

java 复制代码
package org.ting.j20250110_gobang.game;

import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

@Component
public class RoomManager {
    private ConcurrentHashMap<String, Room> roomIdToRoom = new ConcurrentHashMap<>();
    private ConcurrentHashMap<Integer, String> userIdToRoomId = new ConcurrentHashMap<>();
    public void add(String roomId, Room room, Integer userId1, Integer userId2) {
        roomIdToRoom.put(roomId, room);
        userIdToRoomId.put(userId1, roomId);
        userIdToRoomId.put(userId2, roomId);
    }
    public Room getRoomByRoomId(String roomId) {
        return roomIdToRoom.get(roomId);
    }
    public Room getRoomByUserId(Integer userId) {
        return roomIdToRoom.get(userIdToRoomId.get(userId));
    }
}

这里我们创建了两个哈希表,一个维护房间id到游戏房间的映射,一个维护用户id到游戏房间的映射,此时我们就可以通过add方法把两个用户加入一个游戏房间内

java 复制代码
public void handlerMatch(Queue<User> matchQueue) {
        try {
            //对操作的队列加锁保证线程安全
            synchronized (matchQueue) {
                //1.检测队列中是否有两个元素
                while(matchQueue.size() < 2) {
                    matchQueue.wait();
                }

                //2.从队列中取出两个玩家
                User user1 = matchQueue.poll();
                User user2 = matchQueue.poll();

                //3.获取到两个玩家的会话信息
                WebSocketSession session1 = onlineUserManager.getFromHall(user1.getUserId());
                WebSocketSession session2 = onlineUserManager.getFromHall(user2.getUserId());

                System.out.println("匹配成功," + user1.getUsername() + "," + user2.getUsername() + "正在进入游戏房间");

                //4.把两个玩家放到一个游戏房间中
                Room room = new Room();
                roomManager.add(room.getRoomId(), room, user1.getUserId(), user2.getUserId());

                //5.给用户返回匹配成功的响应
                MatchResponse response = new MatchResponse();
                response.setOk(true);
                response.setMessage("success");
                String json = objectMapper.writeValueAsString(response);
                session1.sendMessage(new TextMessage(json));
                session2.sendMessage(new TextMessage(json));
            }
        }catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

3. 游戏房间前端代码

room.html:

html 复制代码
<!DOCTYPE html>
<html lang="chch">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>游戏房间</title>
    <link rel="stylesheet" href="css/common.css">
    <link rel="stylesheet" href="css/room.css">
</head>
<body>
    <div class="nav">五子棋</div>
    <div class="container">
        <div>
            <!-- 棋盘区域 -->
            <canvas id="chess" width="450px" height="450px">

            </canvas>
            <div id="screen">等待玩家连接</div>
        </div>
    </div>
    <script src="js/script.js"></script>
    <!-- <script src="js/jquery.min.js"></script> -->
</body>
</html>

script.js:

javascript 复制代码
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/sky.jpeg";
    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) {
            // TODO 发送坐标给服务器, 服务器要返回结果

            oneStep(col, row, gameInfo.isWhite);
            chessBoard[row][col] = 1;
        }
    }
}

initGame();
相关推荐
c++之路18 分钟前
C++20概述
java·开发语言·c++20
Championship.23.2423 分钟前
Linux Top 命令族深度解析与实战指南
java·linux·服务器·top·linux调试
橘子海全栈攻城狮38 分钟前
【最新源码】养老院系统管理A013
java·spring boot·后端·web安全·微信小程序
逻辑驱动的ken44 分钟前
Java高频面试考点18
java·开发语言·数据库·算法·面试·职场和发展·哈希算法
冷雨夜中漫步1 小时前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
直奔標竿1 小时前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring
one_love_zfl2 小时前
java面试-微服务组件篇
java·微服务·面试
一只大袋鼠2 小时前
Java进阶:CGLIB动态代理解析
java·开发语言
_守一2 小时前
UE DS+Nakama进行游戏服务器开发(1)源码编译nakama
服务器·游戏
环流_2 小时前
HTTP 协议的基本格式
java·网络协议·http