当前位置: 首页 > news >正文

像素版推箱子游戏

像素版推箱子游戏

下面我将实现一个具有像素风格的推箱子游戏,使用HTML5 Canvas绘制游戏界面并实现游戏逻辑。

设计思路

  • 使用复古像素风格设计游戏界面
  • 实现推箱子的核心游戏逻辑
  • 添加关卡选择功能
  • 设计简洁美观的UI界面
  • 添加游戏状态提示和操作说明

代码实现

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>像素版推箱子</title><style>* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Press Start 2P', cursive, 'Courier New', monospace;}body {display: flex;flex-direction: column;align-items: center;justify-content: center;min-height: 100vh;background: linear-gradient(135deg, #1a1a2e, #16213e);color: #fff;padding: 20px;overflow-x: hidden;}.header {text-align: center;margin-bottom: 20px;text-shadow: 0 0 10px rgba(0, 255, 255, 0.7);}h1 {font-size: 2.8rem;margin-bottom: 10px;color: #4cc9f0;letter-spacing: 3px;}.game-container {display: flex;flex-direction: column;align-items: center;gap: 20px;max-width: 1000px;width: 100%;}.stats {display: flex;justify-content: space-between;width: 100%;background: rgba(0, 30, 60, 0.7);padding: 15px;border-radius: 12px;border: 3px solid #4361ee;box-shadow: 0 0 15px rgba(67, 97, 238, 0.5);}.stat-box {text-align: center;padding: 10px;background: rgba(0, 20, 40, 0.8);border-radius: 8px;flex: 1;margin: 0 10px;border: 2px solid #3a0ca3;}.stat-title {font-size: 0.9rem;color: #f72585;margin-bottom: 5px;}.stat-value {font-size: 1.5rem;color: #4cc9f0;}.canvas-container {position: relative;background: rgba(0, 15, 30, 0.9);border-radius: 15px;border: 4px solid #4361ee;box-shadow: 0 0 20px rgba(67, 97, 238, 0.7);overflow: hidden;}canvas {display: block;background: #0d1b2a;}.controls {display: flex;justify-content: center;gap: 20px;width: 100%;flex-wrap: wrap;}button {padding: 15px 25px;font-size: 1.1rem;background: linear-gradient(45deg, #3a0ca3, #4361ee);color: white;border: none;border-radius: 8px;cursor: pointer;transition: all 0.3s;border: 2px solid #4cc9f0;box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);font-weight: bold;letter-spacing: 1px;}button:hover {transform: translateY(-3px);box-shadow: 0 8px 20px rgba(67, 97, 238, 0.8);background: linear-gradient(45deg, #4361ee, #4cc9f0);}button:active {transform: translateY(1px);}.instructions {background: rgba(0, 30, 60, 0.7);padding: 20px;border-radius: 12px;margin-top: 20px;width: 100%;border: 3px solid #f72585;box-shadow: 0 0 15px rgba(247, 37, 133, 0.4);}.instructions h2 {color: #f72585;margin-bottom: 15px;text-align: center;}.instructions p {margin: 10px 0;line-height: 1.6;color: #a9d6e5;}.key {display: inline-block;padding: 3px 8px;background: rgba(247, 37, 133, 0.3);border-radius: 4px;border: 1px solid #f72585;color: #f8f9fa;}.level-selector {display: flex;gap: 15px;flex-wrap: wrap;justify-content: center;margin-top: 10px;}.level-btn {padding: 10px 15px;background: rgba(58, 12, 163, 0.7);}.level-btn.active {background: linear-gradient(45deg, #f72585, #b5179e);box-shadow: 0 0 15px rgba(247, 37, 133, 0.7);}.game-message {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);background: rgba(0, 0, 0, 0.85);padding: 30px 50px;border-radius: 15px;text-align: center;border: 4px solid #f72585;box-shadow: 0 0 30px rgba(247, 37, 133, 0.8);z-index: 10;display: none;}.game-message h2 {color: #4cc9f0;font-size: 2.5rem;margin-bottom: 20px;text-shadow: 0 0 10px rgba(76, 201, 240, 0.7);}.game-message p {color: #f8f9fa;margin: 15px 0;font-size: 1.2rem;}footer {margin-top: 30px;text-align: center;color: #a9d6e5;font-size: 0.9rem;padding: 15px;}@media (max-width: 768px) {h1 {font-size: 2rem;}.stats {flex-direction: column;gap: 10px;}.controls {flex-direction: column;align-items: center;}button {width: 100%;max-width: 300px;}}</style><link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
</head>
<body><div class="header"><h1>像素版推箱子</h1><p>将箱子推到目标位置完成关卡</p></div><div class="game-container"><div class="stats"><div class="stat-box"><div class="stat-title">当前关卡</div><div class="stat-value" id="level">1/5</div></div><div class="stat-box"><div class="stat-title">移动步数</div><div class="stat-value" id="moves">0</div></div><div class="stat-box"><div class="stat-title">剩余箱子</div><div class="stat-value" id="boxes">4</div></div></div><div class="canvas-container"><canvas id="gameCanvas" width="600" height="450"></canvas><div class="game-message" id="winMessage"><h2>恭喜过关!</h2><p>你用了 <span id="finalMoves">0</span></p><button id="nextLevel">下一关</button></div></div><div class="level-selector"><button class="level-btn active" data-level="0">第1关</button><button class="level-btn" data-level="1">第2关</button><button class="level-btn" data-level="2">第3关</button><button class="level-btn" data-level="3">第4关</button><button class="level-btn" data-level="4">第5关</button></div><div class="controls"><button id="restart">重新开始</button><button id="undo">撤销 (Z)</button><button id="reset">重置关卡</button></div><div class="instructions"><h2>游戏说明</h2><p>使用 <span class="key"></span> <span class="key"></span> <span class="key"></span> <span class="key"></span> 方向键移动角色</p><p>将所有的箱子 <span style="color:#ff9e00"></span> 推到目标位置 <span style="color:#00ff00"></span></p><p>箱子只能推动不能拉动,一次只能推一个箱子</p><p><span class="key">Z</span> 键或点击"撤销"按钮可以撤销上一步操作</p></div></div><footer><p>像素版推箱子游戏 | 使用HTML5 Canvas实现 | &copy; 2023</p></footer><script>// 游戏常量const TILE_SIZE = 40;const WALL_COLOR = '#3a86ff';const FLOOR_COLOR = '#8d99ae';const TARGET_COLOR = '#d90429';const PLAYER_COLOR = '#ff9e00';const BOX_COLOR = '#ff9e00';const BOX_ON_TARGET_COLOR = '#00ff00';const PLAYER_ON_TARGET_COLOR = '#90e0ef';// 游戏元素类型const WALL = 1;const FLOOR = 0;const BOX = 2;const TARGET = 3;const PLAYER = 4;const BOX_ON_TARGET = 5;const PLAYER_ON_TARGET = 6;// 游戏关卡const levels = [// Level 1[[1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 0, 0, 0, 0, 3, 0, 1],[1, 0, 2, 0, 0, 0, 0, 1],[1, 0, 0, 0, 2, 3, 0, 1],[1, 0, 4, 0, 0, 0, 0, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1]],// Level 2[[1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 0, 3, 2, 0, 0, 0, 1],[1, 0, 0, 0, 2, 3, 0, 1],[1, 0, 2, 0, 0, 0, 0, 1],[1, 0, 3, 0, 4, 0, 0, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1]],// Level 3[[1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 1, 0, 0, 1],[1, 0, 2, 0, 0, 0, 0, 1],[1, 0, 0, 0, 2, 3, 0, 1],[1, 1, 0, 2, 0, 0, 0, 1],[1, 3, 0, 0, 0, 4, 0, 1],[1, 0, 0, 0, 3, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1]],// Level 4[[1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 0, 3, 1, 2, 3, 0, 1],[1, 0, 0, 1, 0, 0, 0, 1],[1, 0, 2, 0, 4, 0, 0, 1],[1, 0, 0, 0, 2, 0, 0, 1],[1, 0, 3, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1]],// Level 5[[1, 1, 1, 1, 1, 1, 1, 1],[1, 0, 0, 0, 0, 0, 0, 1],[1, 0, 2, 2, 2, 3, 0, 1],[1, 0, 0, 1, 1, 0, 0, 1],[1, 0, 2, 0, 3, 4, 0, 1],[1, 0, 0, 0, 3, 0, 0, 1],[1, 0, 3, 0, 0, 0, 0, 1],[1, 1, 1, 1, 1, 1, 1, 1]]];// 游戏状态let currentLevel = 0;let gameMap = [];let playerPos = { x: 0, y: 0 };let moveCount = 0;let boxCount = 0;let moveHistory = [];// DOM元素const canvas = document.getElementById('gameCanvas');const ctx = canvas.getContext('2d');const levelDisplay = document.getElementById('level');const movesDisplay = document.getElementById('moves');const boxesDisplay = document.getElementById('boxes');const winMessage = document.getElementById('winMessage');const finalMoves = document.getElementById('finalMoves');const nextLevelBtn = document.getElementById('nextLevel');const restartBtn = document.getElementById('restart');const undoBtn = document.getElementById('undo');const resetBtn = document.getElementById('reset');const levelButtons = document.querySelectorAll('.level-btn');// 初始化游戏function initGame(level) {// 复制关卡地图gameMap = JSON.parse(JSON.stringify(levels[level]));moveCount = 0;moveHistory = [];// 重置玩家位置和箱子计数boxCount = 0;for (let y = 0; y < gameMap.length; y++) {for (let x = 0; x < gameMap[y].length; x++) {if (gameMap[y][x] === PLAYER || gameMap[y][x] === PLAYER_ON_TARGET) {playerPos = { x, y };}if (gameMap[y][x] === BOX || gameMap[y][x] === BOX_ON_TARGET) {boxCount++;}}}updateUI();drawGame();}// 绘制游戏function drawGame() {ctx.clearRect(0, 0, canvas.width, canvas.height);const offsetX = (canvas.width - gameMap[0].length * TILE_SIZE) / 2;const offsetY = (canvas.height - gameMap.length * TILE_SIZE) / 2;for (let y = 0; y < gameMap.length; y++) {for (let x = 0; x < gameMap[y].length; x++) {const tileX = offsetX + x * TILE_SIZE;const tileY = offsetY + y * TILE_SIZE;// 绘制地板ctx.fillStyle = FLOOR_COLOR;ctx.fillRect(tileX, tileY, TILE_SIZE, TILE_SIZE);// 绘制网格ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';ctx.strokeRect(tileX, tileY, TILE_SIZE, TILE_SIZE);// 绘制其他元素switch (gameMap[y][x]) {case WALL:// 绘制像素墙ctx.fillStyle = WALL_COLOR;ctx.fillRect(tileX, tileY, TILE_SIZE, TILE_SIZE);// 墙的纹理ctx.fillStyle = '#2a6f97';for (let i = 0; i < TILE_SIZE; i += 4) {for (let j = 0; j < TILE_SIZE; j += 4) {if ((i + j) % 8 === 0) {ctx.fillRect(tileX + i, tileY + j, 2, 2);}}}break;case TARGET:// 绘制目标位置ctx.fillStyle = TARGET_COLOR;ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2, tileY + TILE_SIZE/2, TILE_SIZE/4, 0, Math.PI * 2);ctx.fill();// 绘制星星效果ctx.strokeStyle = '#ffcc00';ctx.lineWidth = 2;ctx.beginPath();for (let i = 0; i < 5; i++) {const angle = Math.PI * 2 * i / 5 - Math.PI/2;const innerAngle = angle + Math.PI / 5;const outerRadius = TILE_SIZE/3;const innerRadius = TILE_SIZE/6;ctx.lineTo(tileX + TILE_SIZE/2 + Math.cos(angle) * outerRadius,tileY + TILE_SIZE/2 + Math.sin(angle) * outerRadius);ctx.lineTo(tileX + TILE_SIZE/2 + Math.cos(innerAngle) * innerRadius,tileY + TILE_SIZE/2 + Math.sin(innerAngle) * innerRadius);}ctx.closePath();ctx.stroke();break;case BOX:// 绘制箱子ctx.fillStyle = BOX_COLOR;ctx.fillRect(tileX + 3, tileY + 3, TILE_SIZE - 6, TILE_SIZE - 6);// 箱子阴影效果ctx.fillStyle = '#cc7a00';ctx.fillRect(tileX + 3, tileY + 3, TILE_SIZE - 6, 4);ctx.fillRect(tileX + 3, tileY + 3, 4, TILE_SIZE - 6);break;case PLAYER:// 绘制玩家ctx.fillStyle = PLAYER_COLOR;ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2, tileY + TILE_SIZE/2, TILE_SIZE/3, 0, Math.PI * 2);ctx.fill();// 玩家眼睛ctx.fillStyle = '#000';ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2 - 5, tileY + TILE_SIZE/3, 3, 0, Math.PI * 2);ctx.arc(tileX + TILE_SIZE/2 + 5, tileY + TILE_SIZE/3, 3, 0, Math.PI * 2);ctx.fill();break;case BOX_ON_TARGET:// 绘制在目标位置上的箱子ctx.fillStyle = BOX_ON_TARGET_COLOR;ctx.fillRect(tileX + 3, tileY + 3, TILE_SIZE - 6, TILE_SIZE - 6);// 箱子阴影效果ctx.fillStyle = '#00cc00';ctx.fillRect(tileX + 3, tileY + 3, TILE_SIZE - 6, 4);ctx.fillRect(tileX + 3, tileY + 3, 4, TILE_SIZE - 6);// 绘制目标位置ctx.fillStyle = TARGET_COLOR;ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2, tileY + TILE_SIZE/2, TILE_SIZE/4, 0, Math.PI * 2);ctx.fill();break;case PLAYER_ON_TARGET:// 绘制在目标位置上的玩家ctx.fillStyle = PLAYER_ON_TARGET_COLOR;ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2, tileY + TILE_SIZE/2, TILE_SIZE/3, 0, Math.PI * 2);ctx.fill();// 玩家眼睛ctx.fillStyle = '#000';ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2 - 5, tileY + TILE_SIZE/3, 3, 0, Math.PI * 2);ctx.arc(tileX + TILE_SIZE/2 + 5, tileY + TILE_SIZE/3, 3, 0, Math.PI * 2);ctx.fill();// 绘制目标位置ctx.fillStyle = TARGET_COLOR;ctx.beginPath();ctx.arc(tileX + TILE_SIZE/2, tileY + TILE_SIZE/2, TILE_SIZE/4, 0, Math.PI * 2);ctx.fill();break;}}}}// 移动玩家function movePlayer(dx, dy) {const newX = playerPos.x + dx;const newY = playerPos.y + dy;// 检查边界if (newX < 0 || newX >= gameMap[0].length || newY < 0 || newY >= gameMap.length) {return;}// 检查墙壁if (gameMap[newY][newX] === WALL) {return;}// 保存移动前的状态用于撤销const prevMap = JSON.parse(JSON.stringify(gameMap));const prevPlayerPos = { ...playerPos };moveHistory.push({ map: prevMap, playerPos: prevPlayerPos });// 移动玩家或推箱子if (gameMap[newY][newX] === FLOOR || gameMap[newY][newX] === TARGET) {// 移动玩家moveTo(newX, newY);moveCount++;} else if (gameMap[newY][newX] === BOX || gameMap[newY][newX] === BOX_ON_TARGET) {// 推箱子const boxNewX = newX + dx;const boxNewY = newY + dy;// 检查箱子是否可以移动if (gameMap[boxNewY][boxNewX] === FLOOR || gameMap[boxNewY][boxNewX] === TARGET) {// 移动箱子if (gameMap[boxNewY][boxNewX] === FLOOR) {gameMap[boxNewY][boxNewX] = BOX;} else {gameMap[boxNewY][boxNewX] = BOX_ON_TARGET;}// 移动玩家moveTo(newX, newY);moveCount++;}}updateUI();drawGame();// 检查游戏是否胜利if (checkWin()) {showWinMessage();}}// 移动到新位置function moveTo(newX, newY) {// 更新玩家原位置if (gameMap[playerPos.y][playerPos.x] === PLAYER) {gameMap[playerPos.y][playerPos.x] = FLOOR;} else {gameMap[playerPos.y][playerPos.x] = TARGET;}// 更新玩家新位置if (gameMap[newY][newX] === FLOOR) {gameMap[newY][newX] = PLAYER;} else {gameMap[newY][newX] = PLAYER_ON_TARGET;}playerPos.x = newX;playerPos.y = newY;}// 检查是否胜利function checkWin() {for (let y = 0; y < gameMap.length; y++) {for (let x = 0; x < gameMap[y].length; x++) {if (gameMap[y][x] === BOX) {return false;}}}return true;}// 显示胜利消息function showWinMessage() {finalMoves.textContent = moveCount;winMessage.style.display = 'block';}// 更新UIfunction updateUI() {levelDisplay.textContent = `${currentLevel + 1}/${levels.length}`;movesDisplay.textContent = moveCount;// 计算剩余箱子let remainingBoxes = 0;for (let y = 0; y < gameMap.length; y++) {for (let x = 0; x < gameMap[y].length; x++) {if (gameMap[y][x] === BOX) {remainingBoxes++;}}}boxesDisplay.textContent = remainingBoxes;}// 撤销上一步function undoMove() {if (moveHistory.length === 0) return;const prevState = moveHistory.pop();gameMap = prevState.map;playerPos = prevState.playerPos;moveCount--;updateUI();drawGame();}// 事件监听器document.addEventListener('keydown', (e) => {if (winMessage.style.display === 'block') return;switch (e.key) {case 'ArrowUp':movePlayer(0, -1);break;case 'ArrowDown':movePlayer(0, 1);break;case 'ArrowLeft':movePlayer(-1, 0);break;case 'ArrowRight':movePlayer(1, 0);break;case 'z':case 'Z':undoMove();break;}});nextLevelBtn.addEventListener('click', () => {winMessage.style.display = 'none';if (currentLevel < levels.length - 1) {currentLevel++;initGame(currentLevel);updateLevelButtons();}});restartBtn.addEventListener('click', () => {winMessage.style.display = 'none';initGame(currentLevel);});undoBtn.addEventListener('click', undoMove);resetBtn.addEventListener('click', () => {winMessage.style.display = 'none';initGame(currentLevel);});// 关卡选择按钮levelButtons.forEach(button => {button.addEventListener('click', () => {winMessage.style.display = 'none';currentLevel = parseInt(button.dataset.level);initGame(currentLevel);updateLevelButtons();});});// 更新关卡按钮状态function updateLevelButtons() {levelButtons.forEach((button, index) => {if (index === currentLevel) {button.classList.add('active');} else {button.classList.remove('active');}});}// 启动游戏initGame(currentLevel);</script>
</body>
</html>

功能特点

  1. 复古像素风格:使用像素风格的图形元素,包括墙体、箱子、玩家角色和目标点
  2. 多关卡设计:包含5个不同难度的推箱子关卡
  3. 游戏控制
    • 使用方向键移动角色
    • 箱子只能推动不能拉动
    • 一次只能推一个箱子
  4. 游戏状态显示
    • 当前关卡
    • 移动步数
    • 剩余箱子数量
  5. 操作功能
    • 重新开始当前关卡
    • 撤销上一步操作(Z键或按钮)
    • 重置当前关卡
    • 选择不同关卡
  6. 胜利提示:完成关卡后显示胜利信息,包括所用步数
  7. 响应式设计:适应不同屏幕尺寸

使用说明

  1. 使用键盘方向键(↑、↓、←、→)移动角色
  2. 将所有箱子推到目标位置(★)上
  3. 箱子只能推动不能拉动
  4. 按Z键或点击"撤销"按钮可以撤销上一步操作
  5. 完成一关后点击"下一关"继续游戏

这个游戏具有复古像素风格和流畅的操作体验,包含了推箱子游戏的所有核心功能,同时通过精心设计的UI提供了良好的用户体验。

http://www.dtcms.com/a/393055.html

相关文章:

  • 2025年CSP-J认证 普及组初赛真题解析 CCF信息学奥赛C++ 中小学初级组 第一轮真题-选择题解析
  • 【精品资料鉴赏】121页可编辑PPT详解医药集团合规管控规划方案
  • Linux用户权限与进程管理深度解析
  • [数据结构] 反射,枚举与lambda表达式
  • 奇异值:数据科学的数学基石与应用核心
  • Python 2025:安全编程与漏洞防范实战指南
  • ​​[硬件电路-286]:高速轨到轨比较器TLV3603DCKR 功能概述与管脚定义
  • CAR 细胞疗法:破解自身免疫性疾病的 “免疫纠错” 新路径
  • FreeRTOS实战指南 — 5 多任务系统实现流程
  • `css`使单词保持连贯的两种方法
  • 【Vue3 ✨】Vue3 入门之旅 · 第三篇:模板语法与数据绑定
  • 分类预测 | Matlab实现PCA-BP主成分分析结合BP神经网络多特征分类预测
  • 【Linux】进程优先级切换调度
  • Ubuntu24上安装Scrapy框架实战
  • 正向shell,反弹shell学习
  • 一维数组原地更新——力扣119.杨辉三角形II
  • Python语法学习-1
  • Linux基础命令大全
  • 9.21 快速选择
  • 【常见集合】HashMap
  • Docker安装小白教程(阿里yum)
  • MySQL表结构变更详解:ALTER TABLE ADD COLUMN语法、最佳实践与避坑指南
  • 【LeetCode - 每日1题】设计电子表格
  • Spring 中 REQUIRED 事务的回滚机制详解
  • C++框架中基类修改导致兼容性问题的深度分析与总结
  • 学习笔记-SpringBoot项目配置
  • Java数据结构——时间和空间复杂度
  • 如何在接手新项目时快速上手?
  • Zynq开发实践(SDK之自定义IP2)
  • 数据库相关锻炼