现代贪吃蛇游戏的进化:从经典玩法到多人在线体验

Hi,我是前端人类学 ! 贪吃蛇游戏自1976年诞生以来,已经从简单的像素游戏发展成为具有丰富功能的现代游戏体验。本文将通过一个功能增强版的贪吃蛇游戏,探讨如何将经典游戏概念与现代Web技术相结合,创造出既保留经典玩法又具备现代特性的游戏体验。

一、技术架构与设计理念

这个增强版贪吃蛇游戏采用HTML5 Canvas作为渲染核心,结合现代CSS布局和JavaScript模块化设计,实现了以下核心功能:

  1. 基础游戏机制:蛇的移动、食物生成、碰撞检测
  2. 多人游戏模式:实时多玩家支持与竞争机制
  3. 成就系统:进度追踪与玩家激励
  4. 关卡编辑器:用户生成内容支持
  5. 资源管理系统:自定义皮肤与背景

二、多人在线模式的实现

多人在线功能是本项目的核心创新之一。通过模拟的网络通信机制,游戏支持2-4名玩家同时参与,并提供三种不同的游戏模式:

javascript 复制代码
// 多人在线模式状态
let multiplayerState = {
  players: [
    { id: 1, name: '玩家1', score: 0, color: '#4CAF50', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 2, name: '玩家2', score: 0, color: '#FF5252', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 3, name: '玩家3', score: 0, color: '#FFC107', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 4, name: '玩家4', score: 0, color: '#9C27B0', alive: true, snake: [], dx: 1, dy: 0 }
  ],
  mode: 'competition',
  status: 'lobby',
  food: []
}

这种设计允许玩家根据偏好选择不同的游戏体验,从合作共嬴到激烈竞争,大大扩展了游戏的可玩性。

三、成就系统的心理激励

成就系统通过提供明确的目标和奖励,有效增强了玩家的参与度和长期投入:

javascript 复制代码
// 成就数据结构
const achievements = [
    {
        id: "first_blood",
        name: "初出茅庐",
        description: "获得100分",
        icon: "fas fa-star",
        progress: 0,
        target: 100,
        unlocked: false
    },
    {
        id: "speed_demon",
        name: "速度之王",
        description: "以最高速度游戏1分钟",
        icon: "fas fa-fire",
        progress: 0,
        target: 60,
        unlocked: false
    }
];

// 成就解锁检查
function checkAchievements() {
    achievements.forEach(achievement => {
        if (!achievement.unlocked && achievement.progress >= achievement.target) {
            unlockAchievement(achievement.id);
        }
    });
}

这种成就系统不仅提供了短期目标,还通过进度可视化给予玩家持续的正向反馈,符合游戏化设计的基本原则。

四、关卡编辑器的创意表达

关卡编辑器功能将玩家从被动的消费者转变为主动的创作者,极大地扩展了游戏的内容生命周期:

javascript 复制代码
canvas.addEventListener('click', e => {
    if (!isEditing) return

    const rect = canvas.getBoundingClientRect()
    const x = e.clientX - rect.left
    const y = e.clientY - rect.top

    const gridX = Math.floor(x / gridSize)
    const gridY = Math.floor(y / gridSize)

    if (currentTool === 'wall') {
      // 添加墙壁
      obstacles.push({ x: gridX, y: gridY })
    } else if (currentTool === 'obstacle') {
      // 添加障碍物
      obstacles.push({ x: gridX, y: gridY })
    } else if (currentTool === 'erase') {
      // 删除墙壁或障碍物
      obstacles = obstacles.filter(obs => !(obs.x === gridX && obs.y === gridY))
    }

    draw()
  })

通过简单的点击交互,玩家可以创建复杂多样的游戏关卡,分享给其他玩家,形成活跃的创作者社区。

五、响应式设计与跨平台体验

游戏采用完全响应式设计,确保在不同设备上都能提供一致的用户体验:

css 复制代码
/* 响应式布局系统 */
.container {
  width: 100%;
  max-width: 1400px;
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 20px;
  margin-top: 20px;
}

@media (max-width: 900px) {
  .container {
    grid-template-columns: 1fr;
  }

  .left-panel,
  .right-panel {
    display: none;
  }

  .mobile-controls {
    display: grid;
  }
}

/* 移动端控制优化 */
.mobile-controls {
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
}

.mobile-controls button {
    height: 70px;
    font-size: 1.5rem;
}

这种设计确保从桌面电脑到移动手机,玩家都能享受完整的游戏功能,包括多人游戏和关卡编辑等高级特性。

六、完整代码实现

**页面结构(snake-game.html) **

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  <link rel="stylesheet" href="snake-game.css">
  <title>高级贪吃蛇游戏</title>
</head>

<body>
  <header>
    <h1><i class="fas fa-snake"></i> 贪吃蛇游戏 - 多人在线版</h1>
    <div class="tabs">
      <div class="tab active" data-tab="single">单人游戏</div>
      <div class="tab" data-tab="multiplayer">多人游戏</div>
      <div class="tab" data-tab="achievements">成就系统</div>
      <div class="tab" data-tab="level-editor">关卡编辑器</div>
    </div>
    <div class="game-info">
      <div class="info-box">
        <i class="fas fa-star"></i>
        <span>得分: <span id="score">0</span></span>
      </div>
      <div class="info-box">
        <i class="fas fa-tachometer-alt"></i>
        <span>速度: <span id="speed">5</span></span>
      </div>
      <div class="info-box">
        <i class="fas fa-clock"></i>
        <span>时间: <span id="time">00:00</span></span>
      </div>
    </div>
    <div class="multiplayer-status" id="multiplayerStatus">
      <div class="player-indicator">
        <div class="player-color player-1"></div>
        <span>玩家1: <span id="player1Score">0</span></span>
      </div>
      <div class="player-indicator">
        <div class="player-color player-2"></div>
        <span>玩家2: <span id="player2Score">0</span></span>
      </div>
      <div class="player-indicator">
        <div class="player-color player-3"></div>
        <span>玩家3: <span id="player3Score">0</span></span>
      </div>
      <div class="player-indicator">
        <div class="player-color player-4"></div>
        <span>玩家4: <span id="player4Score">0</span></span>
      </div>
    </div>
  </header>

  <div class="container">
    <div class="left-panel panel">
      <h3 class="section-title"><i class="fas fa-cog"></i> 游戏设置</h3>

      <div class="settings-group">
        <label for="gameSpeed">游戏速度</label>
        <input type="range" id="gameSpeed" min="1" max="10" value="5">
        <div class="range-value"><span id="speedValue">5</span>/10</div>
      </div>

      <div class="settings-group">
        <label for="gridSize">网格大小</label>
        <select id="gridSize">
          <option value="15">小 (15x15)</option>
          <option value="20" selected>中 (20x20)</option>
          <option value="25">大 (25x25)</option>
        </select>
      </div>

      <div class="settings-group">
        <label for="wallMode">墙壁模式</label>
        <select id="wallMode">
          <option value="solid">实心墙 (游戏结束)</option>
          <option value="pass-through">穿透 (循环边界)</option>
        </select>
      </div>

      <h3 class="section-title"><i class="fas fa-upload"></i> 上传资源</h3>

      <div class="upload-area" id="uploadBg">
        <i class="fas fa-image"></i>
        <p>上传背景</p>
        <input type="file" id="bgUpload" accept="image/*" style="display: none;">
      </div>

      <div class="upload-area" id="uploadSkin">
        <i class="fas fa-palette"></i>
        <p>上传蛇皮肤</p>
        <input type="file" id="skinUpload" accept="image/*" style="display: none;">
      </div>

      <button class="secondary" id="resetSettings">
        <i class="fas fa-undo"></i> 重置设置
      </button>
    </div>

    <div class="game-area panel">
      <canvas id="gameCanvas" width="600" height="600"></canvas>

      <div class="level-editor" id="levelEditor">
        <h3><i class="fas fa-edit"></i> 关卡编辑器</h3>
        <div class="editor-tools">
          <button id="wallTool"><i class="fas fa-wall"></i> 墙壁</button>
          <button id="obstacleTool"><i class="fas fa-mountain"></i> 障碍物</button>
          <button id="eraseTool"><i class="fas fa-eraser"></i> 擦除</button>
        </div>
        <div>
          <input type="text" id="levelName" placeholder="关卡名称">
          <button id="saveLevel"><i class="fas fa-save"></i> 保存关卡</button>
          <button id="loadLevel"><i class="fas fa-folder-open"></i> 加载关卡</button>
        </div>
      </div>

      <div class="game-over" id="gameOver">
        <h2>游戏结束!</h2>
        <p>得分: <span id="finalScore">0</span></p>
        <div class="controls">
          <button id="restartBtn">
            <i class="fas fa-redo"></i> 重新开始
          </button>
          <button class="secondary" id="menuBtn">
            <i class="fas fa-home"></i> 返回菜单
          </button>
        </div>
      </div>
    </div>

    <div class="right-panel panel">
      <div class="tab-content active" id="singleTab">
        <h3 class="section-title"><i class="fas fa-trophy"></i> 排行榜</h3>

        <ul class="leaderboard" id="leaderboard">
          <li>
            <span><span class="rank">1</span> 玩家1</span>
            <span class="score">450</span>
          </li>
          <li>
            <span><span class="rank">2</span> 玩家2</span>
            <span class="score">320</span>
          </li>
          <li>
            <span><span class="rank">3</span> 玩家3</span>
            <span class="score">280</span>
          </li>
        </ul>

        <h3 class="section-title"><i class="fas fa-gamepad"></i> 游戏控制</h3>

        <div class="controls">
          <button id="startBtn">
            <i class="fas fa-play"></i> 开始游戏
          </button>
          <button class="secondary" id="pauseBtn">
            <i class="fas fa-pause"></i> 暂停
          </button>
          <button class="accent" id="soundBtn">
            <i class="fas fa-volume-up"></i> 音效: 开
          </button>
          <button id="saveBtn">
            <i class="fas fa-save"></i> 保存设置
          </button>
        </div>

        <p style="margin-top: 15px; text-align: center;">
          使用 <i class="fas fa-arrow-up"></i> <i class="fas fa-arrow-down"></i>
          <i class="fas fa-arrow-left"></i> <i class="fas fa-arrow-right"></i> 方向键控制
        </p>
      </div>

      <div class="tab-content" id="multiplayerTab">
        <h3 class="section-title"><i class="fas fa-users"></i> 多人游戏</h3>

        <div class="settings-group">
          <label for="playerCount">玩家数量</label>
          <select id="playerCount">
            <option value="2">2 玩家</option>
            <option value="3">3 玩家</option>
            <option value="4">4 玩家</option>
          </select>
        </div>

        <div class="settings-group">
          <label for="gameMode">游戏模式</label>
          <select id="gameMode">
            <option value="coop">合作模式</option>
            <option value="competition">竞争模式</option>
            <option value="last-standing">生存模式</option>
          </select>
        </div>

        <div class="player-controls">
          <div class="player-control">
            <div class="online-indicator"></div>
            <span>玩家1</span>
            <select>
              <option value="human">人类玩家</option>
              <option value="easy">简单AI</option>
              <option value="medium">中等AI</option>
              <option value="hard">困难AI</option>
            </select>
          </div>
          <div class="player-control">
            <div class="online-indicator"></div>
            <span>玩家2</span>
            <select>
              <option value="human">人类玩家</option>
              <option value="easy">简单AI</option>
              <option value="medium">中等AI</option>
              <option value="hard">困难AI</option>
            </select>
          </div>
          <div class="player-control">
            <div class="online-indicator offline"></div>
            <span>玩家3</span>
            <select>
              <option value="none">无玩家</option>
              <option value="human">人类玩家</option>
              <option value="easy">简单AI</option>
              <option value="medium">中等AI</option>
              <option value="hard">困难AI</option>
            </select>
          </div>
          <div class="player-control">
            <div class="online-indicator offline"></div>
            <span>玩家4</span>
            <select>
              <option value="none">无玩家</option>
              <option value="human">人类玩家</option>
              <option value="easy">简单AI</option>
              <option value="medium">中等AI</option>
              <option value="hard">困难AI</option>
            </select>
          </div>
        </div>

        <button id="createLobby">
          <i class="fas fa-plus"></i> 创建房间
        </button>

        <button id="joinLobby">
          <i class="fas fa-sign-in-alt"></i> 加入房间
        </button>

        <div id="lobbyList" style="margin-top: 15px;">
          <h4>可用房间</h4>
          <ul style="list-style: none;">
            <li>房间1 (2/4玩家)</li>
            <li>房间2 (1/2玩家)</li>
          </ul>
        </div>
      </div>

      <div class="tab-content" id="achievementsTab">
        <h3 class="section-title"><i class="fas fa-trophy"></i> 成就系统</h3>

        <div class="achievements">
          <div class="achievement unlocked">
            <i class="fas fa-star"></i>
            <h4>初出茅庐</h4>
            <p>获得100分</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 100%"></div>
            </div>
          </div>

          <div class="achievement">
            <i class="fas fa-fire"></i>
            <h4>速度之王</h4>
            <p>以最高速度游戏1分钟</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 30%"></div>
            </div>
          </div>

          <div class="achievement unlocked">
            <i class="fas fa-users"></i>
            <h4>团队合作</h4>
            <p>完成一局合作模式</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 100%"></div>
            </div>
          </div>

          <div class="achievement">
            <i class="fas fa-ghost"></i>
            <h4>幽灵模式</h4>
            <p>在穿透模式下获得500分</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 65%"></div>
            </div>
          </div>

          <div class="achievement">
            <i class="fas fa-infinity"></i>
            <h4>无限挑战</h4>
            <p>蛇身长度达到50节</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 40%"></div>
            </div>
          </div>

          <div class="achievement">
            <i class="fas fa-crown"></i>
            <h4>贪吃蛇大师</h4>
            <p>解锁所有成就</p>
            <div class="achievement-progress">
              <div class="achievement-progress-bar" style="width: 25%"></div>
            </div>
          </div>
        </div>
      </div>

      <div class="tab-content" id="levelEditorTab">
        <h3 class="section-title"><i class="fas fa-edit"></i> 我的关卡</h3>

        <div style="margin-bottom: 15px;">
          <input type="text" placeholder="搜索关卡..." style="width: 100%;">
        </div>

        <ul class="leaderboard">
          <li>
            <span>迷宫挑战</span>
            <span><i class="fas fa-play"></i> 游玩</span>
          </li>
          <li>
            <span>极限模式</span>
            <span><i class="fas fa-play"></i> 游玩</span>
          </li>
          <li>
            <span>合作关卡</span>
            <span><i class="fas fa-play"></i> 游玩</span>
          </li>
        </ul>

        <button style="margin-top: 15px;">
          <i class="fas fa-share"></i> 分享关卡
        </button>
      </div>
    </div>
  </div>

  <div class="mobile-controls">
    <button id="upBtn"><i class="fas fa-arrow-up"></i></button>
    <button id="leftBtn"><i class="fas fa-arrow-left"></i></button>
    <button id="downBtn"><i class="fas fa-arrow-down"></i></button>
    <button id="rightBtn"><i class="fas fa-arrow-right"></i></button>
  </div>

  <script src="snake-game.js"></script>
</body>

</html>

页面样式(snake-game.css)

css 复制代码
:root {
  --primary-color: #4CAF50;
  --secondary-color: #2196F3;
  --accent-color: #FF5722;
  --dark-color: #2c3e50;
  --light-color: #ecf0f1;
  --success-color: #2ecc71;
  --danger-color: #e74c3c;
  --warning-color: #f39c12;
  --multiplayer-color-1: #FF5252;
  --multiplayer-color-2: #FFC107;
  --multiplayer-color-3: #9C27B0;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

body {
  background: linear-gradient(135deg, var(--dark-color), #34495e);
  color: var(--light-color);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
}

.container {
  width: 100%;
  max-width: 1400px;
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  gap: 20px;
  margin-top: 20px;
}

header {
  text-align: center;
  margin-bottom: 20px;
  width: 100%;
}

h1 {
  font-size: 2.5rem;
  margin-bottom: 10px;
  color: var(--primary-color);
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}

.tabs {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-bottom: 20px;
}

.tab {
  padding: 10px 20px;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s;
}

.tab.active {
  background: var(--primary-color);
}

.tab-content {
  display: none;
}

.tab-content.active {
  display: block;
}

.game-info {
  display: flex;
  justify-content: center;
  gap: 20px;
  margin-bottom: 15px;
}

.info-box {
  background: rgba(0, 0, 0, 0.3);
  padding: 10px 20px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.info-box i {
  font-size: 1.5rem;
  color: var(--primary-color);
}

.panel {
  background: rgba(0, 0, 0, 0.5);
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
  backdrop-filter: blur(10px);
  max-height: 80vh;
  overflow-y: auto;
}

.left-panel,
.right-panel {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.game-area {
  position: relative;
}

canvas {
  background-color: #1a1a2e;
  border-radius: 12px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
  display: block;
  width: 100%;
}

.controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

button {
  background: var(--primary-color);
  color: white;
  border: none;
  padding: 12px;
  border-radius: 8px;
  cursor: pointer;
  font-weight: bold;
  transition: all 0.3s;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

button:hover {
  background: #3e8e41;
  transform: translateY(-2px);
}

button.secondary {
  background: var(--secondary-color);
}

button.secondary:hover {
  background: #0b7dda;
}

button.accent {
  background: var(--accent-color);
}

button.accent:hover {
  background: #d84315;
}

.section-title {
  font-size: 1.2rem;
  margin-bottom: 15px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--primary-color);
  color: var(--primary-color);
}

.settings-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: 500;
}

input[type="range"] {
  width: 100%;
  margin: 10px 0;
}

.range-value {
  text-align: center;
  font-weight: bold;
  color: var(--primary-color);
}

select,
input[type="text"] {
  width: 100%;
  padding: 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  border: 1px solid rgba(255, 255, 255, 0.2);
  margin-bottom: 10px;
}

.upload-area {
  border: 2px dashed rgba(255, 255, 255, 0.3);
  border-radius: 8px;
  padding: 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s;
  margin-top: 10px;
}

.upload-area:hover {
  border-color: var(--primary-color);
  background: rgba(76, 175, 80, 0.1);
}

.upload-area i {
  font-size: 2rem;
  margin-bottom: 10px;
  color: var(--primary-color);
}

.achievements {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.achievement {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 10px;
  text-align: center;
  position: relative;
}

.achievement.locked {
  opacity: 0.6;
}

.achievement i {
  font-size: 2rem;
  margin-bottom: 5px;
  color: var(--warning-color);
}

.achievement.unlocked i {
  color: gold;
}

.achievement-progress {
  height: 5px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  margin-top: 5px;
  overflow: hidden;
}

.achievement-progress-bar {
  height: 100%;
  background: var(--primary-color);
  border-radius: 3px;
}

.leaderboard {
  list-style: none;
}

.leaderboard li {
  padding: 12px;
  background: rgba(255, 255, 255, 0.1);
  margin-bottom: 8px;
  border-radius: 6px;
  display: flex;
  justify-content: space-between;
}

.leaderboard .rank {
  font-weight: bold;
  color: var(--primary-color);
  margin-right: 10px;
}

.leaderboard .score {
  font-weight: bold;
  color: var(--accent-color);
}

.game-over {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.8);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-radius: 12px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s;
}

.game-over.show {
  opacity: 1;
  pointer-events: all;
}

.game-over h2 {
  font-size: 3rem;
  color: var(--danger-color);
  margin-bottom: 20px;
}

.game-over p {
  font-size: 1.5rem;
  margin-bottom: 30px;
}

.multiplayer-status {
  display: flex;
  gap: 10px;
  margin-bottom: 15px;
}

.player-indicator {
  display: flex;
  align-items: center;
  gap: 5px;
}

.player-color {
  width: 15px;
  height: 15px;
  border-radius: 50%;
}

.player-1 {
  background: var(--primary-color);
}
.player-2 {
  background: var(--multiplayer-color-1);
}
.player-3 {
  background: var(--multiplayer-color-2);
}
.player-4 {
  background: var(--multiplayer-color-3);
}

.level-editor {
  display: none;
  position: absolute;
  top: 10px;
  right: 10px;
  background: rgba(0, 0, 0, 0.7);
  padding: 10px;
  border-radius: 8px;
  z-index: 10;
}

.editor-tools {
  display: flex;
  gap: 5px;
  margin-bottom: 10px;
}

.editor-tools button {
  padding: 8px;
  font-size: 0.9rem;
}

.mobile-controls {
  display: none;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 10px;
  margin-top: 20px;
}

.mobile-controls button {
  height: 70px;
  font-size: 1.5rem;
}

.mobile-controls button:nth-child(1) {
  grid-column: 2;
  grid-row: 1;
}

.mobile-controls button:nth-child(2) {
  grid-column: 1;
  grid-row: 2;
}

.mobile-controls button:nth-child(3) {
  grid-column: 2;
  grid-row: 2;
}

.mobile-controls button:nth-child(4) {
  grid-column: 3;
  grid-row: 2;
}

/* 多人在线模式特定样式 */
.player-controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 15px;
}

.player-control {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 6px;
}

.player-control select {
  flex: 1;
  margin: 0;
}

.online-indicator {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--success-color);
}

.online-indicator.offline {
  background: var(--danger-color);
}

@media (max-width: 900px) {
  .container {
    grid-template-columns: 1fr;
  }

  .left-panel,
  .right-panel {
    display: none;
  }

  .mobile-controls {
    display: grid;
  }
}

业务逻辑(snake-game.js)

javascript 复制代码
// 游戏主要变量
const canvas = document.getElementById('gameCanvas')
const ctx = canvas.getContext('2d')
const scoreElement = document.getElementById('score')
const speedElement = document.getElementById('speed')
const timeElement = document.getElementById('time')
const gameOverElement = document.getElementById('gameOver')
const finalScoreElement = document.getElementById('finalScore')

let gridSize = 20
let tileCount = canvas.width / gridSize
let snake = []
let food = {}
let dx = 0
let dy = 0
let score = 0
let gameSpeed = 150
let gameInterval
let isPaused = false
let isGameOver = false
let gameTime = 0
let timeInterval
let isSoundOn = true
let isMultiplayer = false
let obstacles = []
let currentTool = 'wall'
let isEditing = false

// 多人在线模式状态
let multiplayerState = {
  players: [
    { id: 1, name: '玩家1', score: 0, color: '#4CAF50', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 2, name: '玩家2', score: 0, color: '#FF5252', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 3, name: '玩家3', score: 0, color: '#FFC107', alive: true, snake: [], dx: 1, dy: 0 },
    { id: 4, name: '玩家4', score: 0, color: '#9C27B0', alive: true, snake: [], dx: 1, dy: 0 }
  ],
  mode: 'competition',
  status: 'lobby',
  food: []
}

// 初始化游戏
function initGame() {
  // 初始化蛇
  snake = [
    { x: 10, y: 10 },
    { x: 9, y: 10 },
    { x: 8, y: 10 }
  ]

  // 生成食物
  generateFood()

  // 生成障碍物
  generateObstacles()

  // 重置游戏状态
  score = 0
  dx = 1
  dy = 0
  gameTime = 0
  isGameOver = false

  // 更新UI
  scoreElement.textContent = score
  gameOverElement.classList.remove('show')

  // 绘制初始状态
  draw()

  // 启动计时器
  startTimer()
}

// 初始化多人游戏
function initMultiplayerGame() {
  const playerCount = parseInt(document.getElementById('playerCount').value)
  const gameMode = document.getElementById('gameMode').value

  // 设置玩家数量
  multiplayerState.players = multiplayerState.players.slice(0, playerCount)

  // 初始化每个玩家的蛇
  multiplayerState.players.forEach((player, index) => {
    const startX = 5 + index * 5
    player.snake = [
      { x: startX, y: 10 },
      { x: startX - 1, y: 10 },
      { x: startX - 2, y: 10 }
    ]
    player.score = 0
    player.alive = true

    // 设置初始方向
    player.dx = 1
    player.dy = 0
  })

  // 生成多个食物
  generateMultiplayerFood()

  // 更新玩家状态显示
  updateMultiplayerStatus()

  // 设置游戏模式
  multiplayerState.mode = gameMode
  multiplayerState.status = 'playing'

  // 开始游戏循环
  if (gameInterval) clearInterval(gameInterval)
  gameInterval = setInterval(multiplayerGameLoop, gameSpeed)
}

// 多人游戏循环
function multiplayerGameLoop() {
  if (!isPaused && multiplayerState.status === 'playing') {
    moveMultiplayerSnakes()
    drawMultiplayerGame()
    checkMultiplayerGameOver()
  }
}

// 移动所有玩家的蛇
function moveMultiplayerSnakes() {
  multiplayerState.players.forEach(player => {
    if (player.alive) {
      const head = {
        x: player.snake[0].x + player.dx,
        y: player.snake[0].y + player.dy
      }

      // 墙壁碰撞检测
      const wallMode = document.getElementById('wallMode').value
      if (wallMode === 'solid') {
        if (head.x < 0 || head.y < 0 || head.x >= tileCount || head.y >= tileCount) {
          player.alive = false
          return
        }
      } else {
        if (head.x < 0) head.x = tileCount - 1
        if (head.y < 0) head.y = tileCount - 1
        if (head.x >= tileCount) head.x = 0
        if (head.y >= tileCount) head.y = 0
      }

      // 添加到蛇头
      player.snake.unshift(head)

      // 检查是否吃到食物
      let ateFood = false
      for (let i = 0; i < multiplayerState.food.length; i++) {
        const food = multiplayerState.food[i]
        if (head.x === food.x && head.y === food.y) {
          player.score += 10
          multiplayerState.food.splice(i, 1)
          generateMultiplayerFood() // 生成新食物
          ateFood = true
          break
        }
      }

      // 如果没吃到食物,移除蛇尾
      if (!ateFood) {
        player.snake.pop()
      }

      // 检查碰撞
      checkMultiplayerCollisions(player)
    }
  })
}

// 检查多人游戏碰撞
function checkMultiplayerCollisions(player) {
  const head = player.snake[0]

  // 检查是否撞到自己
  for (let i = 1; i < player.snake.length; i++) {
    if (head.x === player.snake[i].x && head.y === player.snake[i].y) {
      player.alive = false
      return
    }
  }

  // 检查是否撞到障碍物
  for (let obs of obstacles) {
    if (head.x === obs.x && head.y === obs.y) {
      player.alive = false
      return
    }
  }

  // 检查是否撞到其他玩家
  multiplayerState.players.forEach(otherPlayer => {
    if (otherPlayer.id !== player.id && otherPlayer.alive) {
      for (let part of otherPlayer.snake) {
        if (head.x === part.x && head.y === part.y) {
          player.alive = false
          return
        }
      }
    }
  })
}

// 绘制多人游戏
function drawMultiplayerGame() {
  // 清空画布
  ctx.fillStyle = '#1a1a2e'
  ctx.fillRect(0, 0, canvas.width, canvas.height)

  // 绘制网格
  drawGrid()

  // 绘制障碍物
  drawObstacles()

  // 绘制所有玩家的蛇
  multiplayerState.players.forEach(player => {
    if (player.alive) {
      for (let i = 0; i < player.snake.length; i++) {
        const part = player.snake[i]

        if (i === 0) {
          // 蛇头
          ctx.fillStyle = player.color
        } else {
          // 蛇身
          ctx.fillStyle = shadeColor(player.color, -20)
        }

        ctx.fillRect(part.x * gridSize, part.y * gridSize, gridSize - 1, gridSize - 1)

        // 为蛇身添加圆角效果
        ctx.beginPath()
        ctx.arc(part.x * gridSize + gridSize / 2, part.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2)
        ctx.fill()
      }
    }
  })

  // 绘制食物
  multiplayerState.food.forEach(food => {
    ctx.fillStyle = '#FF5252'
    ctx.beginPath()
    ctx.arc(food.x * gridSize + gridSize / 2, food.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2)
    ctx.fill()
  })

  // 更新玩家分数显示
  updateMultiplayerStatus()
}

// 生成多人游戏食物
function generateMultiplayerFood() {
  multiplayerState.food = []
  const foodCount = multiplayerState.players.length * 2

  for (let i = 0; i < foodCount; i++) {
    const food = {
      x: Math.floor(Math.random() * tileCount),
      y: Math.floor(Math.random() * tileCount)
    }

    // 确保食物不会出现在蛇身上或障碍物上
    let validPosition = true

    multiplayerState.players.forEach(player => {
      for (let part of player.snake) {
        if (part.x === food.x && part.y === food.y) {
          validPosition = false
        }
      }
    })

    for (let obs of obstacles) {
      if (obs.x === food.x && obs.y === food.y) {
        validPosition = false
      }
    }

    if (validPosition) {
      multiplayerState.food.push(food)
    } else {
      i-- // 重试
    }
  }
}

// 检查多人游戏是否结束
function checkMultiplayerGameOver() {
  let alivePlayers = 0
  multiplayerState.players.forEach(player => {
    if (player.alive) alivePlayers++
  })

  if (alivePlayers <= 1) {
    multiplayerState.status = 'finished'

    // 显示游戏结束画面
    finalScoreElement.textContent = `玩家${multiplayerState.players.findIndex(p => p.alive) + 1}获胜!`
    gameOverElement.classList.add('show')

    // 播放游戏结束音效
    if (isSoundOn) {
      playSound('gameover')
    }
  }
}

// 更新多人游戏状态显示
function updateMultiplayerStatus() {
  multiplayerState.players.forEach((player, index) => {
    document.getElementById(`player${index + 1}Score`).textContent = player.score
  })
}

// 工具函数:调整颜色亮度
function shadeColor(color, percent) {
  let R = parseInt(color.substring(1, 3), 16)
  let G = parseInt(color.substring(3, 5), 16)
  let B = parseInt(color.substring(5, 7), 16)

  R = parseInt((R * (100 + percent)) / 100)
  G = parseInt((G * (100 + percent)) / 100)
  B = parseInt((B * (100 + percent)) / 100)

  R = R < 255 ? R : 255
  G = G < 255 ? G : 255
  B = B < 255 ? B : 255

  R = R < 0 ? 0 : R
  G = G < 0 ? 0 : G
  B = B < 0 ? 0 : B

  const RR = R.toString(16).length === 1 ? '0' + R.toString(16) : R.toString(16)
  const GG = G.toString(16).length === 1 ? '0' + G.toString(16) : G.toString(16)
  const BB = B.toString(16).length === 1 ? '0' + B.toString(16) : B.toString(16)

  return '#' + RR + GG + BB
}

// 生成食物
function generateFood() {
  food = {
    x: Math.floor(Math.random() * tileCount),
    y: Math.floor(Math.random() * tileCount)
  }

  // 确保食物不会出现在蛇身上或障碍物上
  for (let part of snake) {
    if (part.x === food.x && part.y === food.y) {
      generateFood()
      return
    }
  }

  for (let obs of obstacles) {
    if (obs.x === food.x && obs.y === food.y) {
      generateFood()
      return
    }
  }
}

// 生成障碍物
function generateObstacles() {
  obstacles = []
  const obstacleCount = Math.floor(tileCount * 0.1) // 10%的格子作为障碍物

  for (let i = 0; i < obstacleCount; i++) {
    let obstacle = {
      x: Math.floor(Math.random() * tileCount),
      y: Math.floor(Math.random() * tileCount)
    }

    // 确保障碍物不会出现在蛇的初始位置或食物上
    let validPosition = true
    for (let part of snake) {
      if (part.x === obstacle.x && part.y === obstacle.y) {
        validPosition = false
        break
      }
    }

    if (obstacle.x === food.x && obstacle.y === food.y) {
      validPosition = false
    }

    if (validPosition) {
      obstacles.push(obstacle)
    } else {
      i-- // 重试
    }
  }
}

// 绘制游戏
function draw() {
  // 清空画布
  ctx.fillStyle = '#1a1a2e'
  ctx.fillRect(0, 0, canvas.width, canvas.height)

  // 绘制网格
  drawGrid()

  // 绘制障碍物
  drawObstacles()

  // 绘制蛇
  for (let i = 0; i < snake.length; i++) {
    const part = snake[i]

    // 蛇头用不同颜色
    if (i === 0) {
      ctx.fillStyle = '#4CAF50'
    } else {
      ctx.fillStyle = '#8BC34A'
    }

    ctx.fillRect(part.x * gridSize, part.y * gridSize, gridSize - 1, gridSize - 1)

    // 为蛇身添加圆角效果
    ctx.beginPath()
    ctx.arc(part.x * gridSize + gridSize / 2, part.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2)
    ctx.fill()
  }

  // 绘制食物
  ctx.fillStyle = '#FF5252'
  ctx.beginPath()
  ctx.arc(food.x * gridSize + gridSize / 2, food.y * gridSize + gridSize / 2, gridSize / 2 - 1, 0, Math.PI * 2)
  ctx.fill()
}

// 绘制网格
function drawGrid() {
  ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'
  ctx.lineWidth = 0.5

  for (let i = 0; i < tileCount; i++) {
    // 垂直线
    ctx.beginPath()
    ctx.moveTo(i * gridSize, 0)
    ctx.lineTo(i * gridSize, canvas.height)
    ctx.stroke()

    // 水平线
    ctx.beginPath()
    ctx.moveTo(0, i * gridSize)
    ctx.lineTo(canvas.width, i * gridSize)
    ctx.stroke()
  }
}

// 绘制障碍物
function drawObstacles() {
  ctx.fillStyle = '#607D8B'
  for (let obs of obstacles) {
    ctx.fillRect(obs.x * gridSize, obs.y * gridSize, gridSize - 1, gridSize - 1)

    // 添加纹理效果
    ctx.fillStyle = '#455A64'
    ctx.fillRect(obs.x * gridSize + 2, obs.y * gridSize + 2, gridSize - 5, gridSize - 5)
    ctx.fillStyle = '#607D8B'
  }
}

// 移动蛇
function moveSnake() {
  // 计算新的头部位置
  const head = { x: snake[0].x + dx, y: snake[0].y + dy }

  // 检查墙壁模式
  const wallMode = document.getElementById('wallMode').value
  if (wallMode === 'solid') {
    // 实心墙模式 - 检查是否撞墙
    if (head.x < 0 || head.y < 0 || head.x >= tileCount || head.y >= tileCount) {
      gameOver()
      return
    }
  } else {
    // 穿透模式 - 从对面出现
    if (head.x < 0) head.x = tileCount - 1
    if (head.y < 0) head.y = tileCount - 1
    if (head.x >= tileCount) head.x = 0
    if (head.y >= tileCount) head.y = 0
  }

  // 检查是否撞到自己
  if (isSnakeCollision(head)) {
    gameOver()
    return
  }

  // 检查是否撞到障碍物
  for (let obs of obstacles) {
    if (head.x === obs.x && head.y === obs.y) {
      gameOver()
      return
    }
  }

  // 将新头部添加到蛇的起始位置
  snake.unshift(head)

  // 检查是否吃到食物
  if (head.x === food.x && head.y === food.y) {
    // 增加分数
    score += 10
    scoreElement.textContent = score

    // 检查成就
    checkAchievements()

    // 生成新食物
    generateFood()

    // 播放吃食物音效
    if (isSoundOn) {
      playSound('eat')
    }
  } else {
    // 如果没吃到食物,移除尾部
    snake.pop()
  }
}

// 检查蛇是否撞到自己
function isSnakeCollision(head) {
  for (let i = 1; i < snake.length; i++) {
    if (head.x === snake[i].x && head.y === snake[i].y) {
      return true
    }
  }
  return false
}

// 检查成就
function checkAchievements() {
  // 这里简化处理,实际应用中会有更复杂的成就系统
  if (score >= 100) {
    unlockAchievement('初出茅庐')
  }

  if (snake.length >= 50) {
    unlockAchievement('无限挑战')
  }
}

// 解锁成就
function unlockAchievement(name) {
  console.log(`成就已解锁: ${name}`)
  // 在实际应用中,这里会更新UI并播放成就解锁动画
}

// 游戏循环
function gameLoop() {
  if (!isPaused && !isGameOver) {
    moveSnake()
    draw()
  }
}

// 开始游戏
function startGame() {
  if (isGameOver) {
    initGame()
  }

  if (!gameInterval) {
    isPaused = false
    isGameOver = false
    gameInterval = setInterval(gameLoop, gameSpeed)
    document.getElementById('startBtn').innerHTML = '<i class="fas fa-play"></i> 重新开始'
  } else if (isPaused) {
    isPaused = false
    document.getElementById('pauseBtn').innerHTML = '<i class="fas fa-pause"></i> 暂停'
  }
}

// 暂停游戏
function pauseGame() {
  if (!isGameOver && gameInterval) {
    isPaused = !isPaused
    document.getElementById('pauseBtn').innerHTML = isPaused ? '<i class="fas fa-play"></i> 继续' : '<i class="fas fa-pause"></i> 暂停'
  }
}

// 游戏结束
function gameOver() {
  isGameOver = true
  clearInterval(gameInterval)
  clearInterval(timeInterval)
  gameInterval = null

  finalScoreElement.textContent = score
  gameOverElement.classList.add('show')

  // 播放游戏结束音效
  if (isSoundOn) {
    playSound('gameover')
  }

  // 更新排行榜
  updateLeaderboard('玩家', score)
}

// 播放音效
function playSound(type) {
  // 在实际应用中,这里会播放音频文件
  console.log(`Playing sound: ${type}`)
}

// 更新排行榜
function updateLeaderboard(name, score) {
  // 这里简化处理,实际应用中会保存到本地存储或服务器
  const leaderboard = document.getElementById('leaderboard')
  console.log(`Updating leaderboard with ${name}: ${score}`)
}

// 开始计时器
function startTimer() {
  clearInterval(timeInterval)
  gameTime = 0
  updateTimer()

  timeInterval = setInterval(() => {
    if (!isPaused && !isGameOver) {
      gameTime++
      updateTimer()
    }
  }, 1000)
}

// 更新计时器显示
function updateTimer() {
  const minutes = Math.floor(gameTime / 60)
  const seconds = gameTime % 60
  timeElement.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
}

// 切换音效
function toggleSound() {
  isSoundOn = !isSoundOn
  document.getElementById('soundBtn').innerHTML = isSoundOn ? '<i class="fas fa-volume-up"></i> 音效: 开' : '<i class="fas fa-volume-mute"></i> 音效: 关'
}

// 保存设置
function saveSettings() {
  const settings = {
    speed: document.getElementById('gameSpeed').value,
    gridSize: document.getElementById('gridSize').value,
    wallMode: document.getElementById('wallMode').value,
    sound: isSoundOn
  }

  localStorage.setItem('snakeGameSettings', JSON.stringify(settings))
  alert('设置已保存!')
}

// 加载设置
function loadSettings() {
  const savedSettings = localStorage.getItem('snakeGameSettings')
  if (savedSettings) {
    const settings = JSON.parse(savedSettings)
    document.getElementById('gameSpeed').value = settings.speed
    document.getElementById('speedValue').textContent = settings.speed
    speedElement.textContent = settings.speed
    document.getElementById('gridSize').value = settings.gridSize
    document.getElementById('wallMode').value = settings.wallMode
    isSoundOn = settings.sound

    // 更新音效按钮状态
    document.getElementById('soundBtn').innerHTML = isSoundOn ? '<i class="fas fa-volume-up"></i> 音效: 开' : '<i class="fas fa-volume-mute"></i> 音效: 关'

    // 更新游戏速度
    gameSpeed = 210 - settings.speed * 10
  }
}

// 重置设置
function resetSettings() {
  localStorage.removeItem('snakeGameSettings')
  document.getElementById('gameSpeed').value = 5
  document.getElementById('speedValue').textContent = 5
  speedElement.textContent = 5
  document.getElementById('gridSize').value = 20
  document.getElementById('wallMode').value = 'solid'
  isSoundOn = true

  document.getElementById('soundBtn').innerHTML = '<i class="fas fa-volume-up"></i> 音效: 开'
  gameSpeed = 160

  alert('设置已重置为默认值!')
}

// 切换标签页
function switchTab(tabName) {
  // 隐藏所有标签页
  document.querySelectorAll('.tab-content').forEach(tab => {
    tab.classList.remove('active')
  })

  // 取消所有标签的激活状态
  document.querySelectorAll('.tab').forEach(tab => {
    tab.classList.remove('active')
  })

  // 激活选中的标签页
  document.getElementById(`${tabName}Tab`).classList.add('active')

  // 激活选中的标签
  document.querySelector(`.tab[data-tab="${tabName}"]`).classList.add('active')

  // 根据标签页显示/隐藏编辑器
  if (tabName === 'level-editor') {
    document.getElementById('levelEditor').style.display = 'block'
    isEditing = true
  } else {
    document.getElementById('levelEditor').style.display = 'none'
    isEditing = false
  }

  // 根据标签页显示/隐藏多人游戏状态
  if (tabName === 'multiplayer') {
    document.getElementById('multiplayerStatus').style.display = 'flex'
    isMultiplayer = true
  } else {
    document.getElementById('multiplayerStatus').style.display = 'none'
    isMultiplayer = false
  }
}

// 初始化事件监听
function initEvents() {
  // 键盘控制
  document.addEventListener('keydown', e => {
    // 防止按键滚动页面
    if (e.key.startsWith('Arrow')) {
      e.preventDefault()
    }

    // 根据按键改变方向(防止180度转向)
    switch (e.key) {
      case 'ArrowUp':
        if (dy === 0) {
          dx = 0
          dy = -1
        }
        break
      case 'ArrowDown':
        if (dy === 0) {
          dx = 0
          dy = 1
        }
        break
      case 'ArrowLeft':
        if (dx === 0) {
          dx = -1
          dy = 0
        }
        break
      case 'ArrowRight':
        if (dx === 0) {
          dx = 1
          dy = 0
        }
        break
      case ' ':
        pauseGame()
        break
    }
  })

  // 标签切换事件
  document.querySelectorAll('.tab').forEach(tab => {
    tab.addEventListener('click', () => {
      switchTab(tab.dataset.tab)
    })
  })

  // 按钮事件
  document.getElementById('startBtn').addEventListener('click', startGame)
  document.getElementById('pauseBtn').addEventListener('click', pauseGame)
  document.getElementById('restartBtn').addEventListener('click', startGame)
  document.getElementById('soundBtn').addEventListener('click', toggleSound)
  document.getElementById('saveBtn').addEventListener('click', saveSettings)
  document.getElementById('resetSettings').addEventListener('click', resetSettings)
  document.getElementById('createLobby').addEventListener('click', initMultiplayerGame)

  // 移动端控制按钮
  document.getElementById('upBtn').addEventListener('click', () => {
    if (dy === 0) {
      dx = 0
      dy = -1
    }
  })

  document.getElementById('downBtn').addEventListener('click', () => {
    if (dy === 0) {
      dx = 0
      dy = 1
    }
  })

  document.getElementById('leftBtn').addEventListener('click', () => {
    if (dx === 0) {
      dx = -1
      dy = 0
    }
  })

  document.getElementById('rightBtn').addEventListener('click', () => {
    if (dx === 0) {
      dx = 1
      dy = 0
    }
  })

  // 速度滑块事件
  document.getElementById('gameSpeed').addEventListener('input', e => {
    const speedValue = e.target.value
    document.getElementById('speedValue').textContent = speedValue
    speedElement.textContent = speedValue
    gameSpeed = 210 - speedValue * 10

    if (gameInterval) {
      clearInterval(gameInterval)
      gameInterval = setInterval(gameLoop, gameSpeed)
    }
  })

  // 网格大小改变事件
  document.getElementById('gridSize').addEventListener('change', e => {
    gridSize = parseInt(e.target.value)
    tileCount = canvas.width / gridSize
    initGame()
  })

  // 上传功能
  document.getElementById('uploadBg').addEventListener('click', () => {
    document.getElementById('bgUpload').click()
  })

  document.getElementById('uploadSkin').addEventListener('click', () => {
    document.getElementById('skinUpload').click()
  })

  document.getElementById('bgUpload').addEventListener('change', handleBackgroundUpload)
  document.getElementById('skinUpload').addEventListener('change', handleSkinUpload)

  // 编辑器工具选择
  document.getElementById('wallTool').addEventListener('click', () => {
    currentTool = 'wall'
  })

  document.getElementById('obstacleTool').addEventListener('click', () => {
    currentTool = 'obstacle'
  })

  document.getElementById('eraseTool').addEventListener('click', () => {
    currentTool = 'erase'
  })

  // 画布点击事件(用于关卡编辑器)
  canvas.addEventListener('click', e => {
    if (!isEditing) return

    const rect = canvas.getBoundingClientRect()
    const x = e.clientX - rect.left
    const y = e.clientY - rect.top

    const gridX = Math.floor(x / gridSize)
    const gridY = Math.floor(y / gridSize)

    if (currentTool === 'wall') {
      // 添加墙壁
      obstacles.push({ x: gridX, y: gridY })
    } else if (currentTool === 'obstacle') {
      // 添加障碍物
      obstacles.push({ x: gridX, y: gridY })
    } else if (currentTool === 'erase') {
      // 删除墙壁或障碍物
      obstacles = obstacles.filter(obs => !(obs.x === gridX && obs.y === gridY))
    }

    draw()
  })
}

// 处理背景上传
function handleBackgroundUpload(e) {
  const file = e.target.files[0]
  if (file) {
    const reader = new FileReader()
    reader.onload = function(event) {
      // 创建背景图像
      const bgImage = new Image()
      bgImage.onload = function() {
        // 应用背景
        ctx.drawImage(bgImage, 0, 0, canvas.width, canvas.height)
      }
      bgImage.src = event.target.result
    }
    reader.readAsDataURL(file)
  }
}

// 处理皮肤上传
function handleSkinUpload(e) {
  const file = e.target.files[0]
  if (file) {
    const reader = new FileReader()
    reader.onload = function(event) {
      // 在实际应用中,这里会设置蛇的皮肤
      console.log('Skin uploaded:', file.name)
      alert(`蛇皮肤 "${file.name}" 上传成功!`)
    }
    reader.readAsDataURL(file)
  }
}

// 初始化游戏
function init() {
  loadSettings()
  initEvents()
  initGame()
}

// 启动游戏
window.onload = init

七、性能优化策略

  1. Canvas渲染优化:使用离屏渲染和局部重绘技术
  2. 游戏循环控制:基于requestAnimationFrame的自适应帧率
  3. 内存管理:及时释放不再使用的资源
  4. 数据序列化:高效的游戏状态保存与加载

这个增强版贪吃蛇游戏展示了如何通过现代Web技术赋予经典游戏新的生命力。它不仅保留了原始游戏的简单乐趣,还通过多人模式、成就系统和关卡编辑器等现代游戏元素,创造了深度和广度都大大扩展的游戏体验。 这种开发模式证明了即使是最简单的游戏概念,也可以通过技术创新和用户体验优化,发展成为吸引现代玩家的复杂产品。对于开发者而言,这个项目展示了如何平衡传统与创新,在保留经典魅力的同时引入现代功能。 最终,这个贪吃蛇游戏的进化之旅提醒我们,优秀的游戏设计不仅仅是关于尖端技术或复杂机制,而是关于创造引人入胜的体验,让玩家愿意一次又一次地回来享受游戏的乐趣。

相关推荐
前端康师傅2 小时前
JavaScript 函数高级用法
前端·javascript
前端Hardy2 小时前
惊艳同事的 Canvas 事件流程图,这篇教会你
前端·javascript·css
Async Cipher2 小时前
CSS 居中
前端·css·css3
华仔啊3 小时前
别再被 Stream.toMap() 劝退了!3 个真实避坑案例,建议收藏
javascript·后端
华仔啊3 小时前
别再写 TypeScript enum了!新枚举方式让 bundle 瞬间小20%
javascript·typescript
方安乐3 小时前
vite+vue+js项目使用ts报错
前端·javascript·vue.js
njsgcs3 小时前
网页连接摄像头
javascript·css·html
李明卫杭州3 小时前
JavaScript 中的各种取整方式详解
javascript
子兮曰3 小时前
🚀别再乱写package.json了!这些隐藏技巧让项目管理效率提升300%
前端·javascript·npm