用 Socket.io 写一个多人海战游戏:从棋盘生成到实时对战

经典游戏 + 现代技术

海战棋(Battleship)是个经典游戏。但让它支持 4 人同时对战、有武器冷却和连胜加成,就变得很有意思了。

后端架构

Node.js + Express
  → Socket.io(实时通信)
    → 游戏房间管理
      → 棋盘状态同步
        → 炮击逻辑
          → 命中判定 + 连胜加成

棋盘生成与舰船放置

12×12 棋盘,每方配置 5 艘船:航母(5格)、战列舰(4格)、巡洋舰(3格)、驱逐舰(2格)、潜艇(2格)。

const SHIP_DEFS = [
  { name: '航母',   len: 5 },
  { name: '战列舰', len: 4 },
  { name: '巡洋舰', len: 3 },
  { name: '驱逐舰', len: 2 },
  { name: '潜艇',   len: 2 },
];

function randomPlace(ships, gridSize) {
  for (const ship of ships) {
    let placed = false;
    while (!placed) {
      const horizontal = Math.random() > 0.5;
      const row = Math.floor(Math.random() * gridSize);
      const col = Math.floor(Math.random() * gridSize);
      placed = tryPlace(grid, ship, row, col, horizontal);
    }
  }
}

炮击与冷却机制

不是简单地一人一炮。每艘存活的船就是一个"炮台",每次射击后进入冷却:

const BASE_COOLDOWN = 3500;  // 基础冷却 3.5 秒
const STREAK_DISCOUNT = 400;  // 命中一次减少 0.4 秒冷却
const MIN_COOLDOWN = 1200;    // 最低冷却 1.2 秒

这意味着——打得越准,射速越快。这是一个很强的正反馈机制,鼓励玩家认真瞄准而不是乱打。

房间管理

Socket.io 的 room 机制天然适合:

io.on('connection', (socket) => {
  socket.on('join_game', (gameId) => {
    socket.join(gameId);
    // 通知房间内其他玩家
    socket.to(gameId).emit('player_joined', playerInfo);
  });

  socket.on('fire', (target) => {
    const result = gameState.fire(playerId, target);
    io.to(gameId).emit('fire_result', result);
  });
});

状态同步策略

采用"服务器权威"模式:所有游戏逻辑跑在服务端,客户端只负责渲染。每次操作由客户端发起,服务端验证后广播结果。这样杜绝了作弊的可能性。

前端

纯 HTML + CSS + Canvas,用 Socket.io 客户端连接。支持拖拽放置舰船、点击炮击。游戏结束时有击杀统计和 MVP 评选。

4 个人在同一局里互相炮击的混乱场面,非常欢乐 😂