文章目录
程序员节手把手教你用HTML5 Canvas编写1024游戏
接下来将详细拆解使用HTML5 Canvas编写1024游戏的每个环节,从基础概念到完整实现,让你一步步掌握游戏开发的核心技巧。
游戏效果图如下:

一、游戏概述与规则
1024是一款经典的滑块益智游戏,玩家通过上下左右移动方块,使相同数字的方块碰撞合并,目标是合成1024这个数字。
1. 游戏结构
- 使用HTML5 Canvas绘制游戏界面
- 使用CSS美化界面,包括颜色、字体和布局
- 通过JavaScript实现游戏逻辑
2. 核心功能
- 游戏初始化:创建4x4网格并添加初始方块
- 方块移动:实现上下左右四个方向的移动逻辑
- 方块合并:相同数字的方块在移动时合并
- 分数计算:合并时累加分数
- 游戏状态检测:判断游戏是否胜利或失败
3. 关键算法
- 移动算法:通过收集非零元素、合并相同数字、填充零的方式实现
- 随机生成:在空位置随机生成新方块(90%概率为2,10%概率为4)
- 游戏结束检测:检查是否还有空位置或可合并的相邻方块
4. 用户体验
- 键盘控制(方向键)
- 分数实时显示
- 游戏状态提示(胜利/失败)
- 重新开始功能
5. 游戏规则
- 4×4的网格中,初始有两个数字2或4
- 每次移动后,空白处随机出现一个2或4
- 相同数字的方块碰撞时会合并成它们的和
- 当网格填满且无法合并时游戏结束
- 合成1024即获胜
二、项目结构与初始化
首先创建基础HTML结构,设置Canvas画布和基本样式:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1024游戏 - HTML5 Canvas实现</title>
<style>
body {
font-family: 'Arial', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
background-color: #faf8ef;
color: #776e65;
margin: 0;
padding: 20px;
}
.game-container {
position: relative;
margin: 20px auto;
}
canvas {
border-radius: 6px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<h1>1024游戏</h1>
<div class="game-container">
<canvas id="game-canvas" width="450" height="450"></canvas>
</div>
<script>
// 游戏代码将在这里实现
</script>
</body>
</html>
三、游戏配置与状态管理
在script标签中,首先定义游戏的核心配置和状态变量:
// 游戏配置
const GRID_SIZE = 4; // 网格大小(4x4)
const CELL_SIZE = 100; // 每个单元格的尺寸
const CELL_MARGIN = 10; // 单元格之间的间距
// 方块颜色映射(根据数值不同显示不同颜色)
const TILE_COLORS = {
0: "#cdc1b4", // 空单元格
2: "#eee4da",
4: "#ede0c8",
8: "#f2b179",
16: "#f59563",
32: "#f67c5f",
64: "#f65e3b",
128: "#edcf72",
256: "#edcc61",
512: "#edc850",
1024: "#edc53f",
2048: "#edc22e"
};
// 方块文字颜色
const TEXT_COLORS = {
light: "#f9f6f2", // 浅色文字(用于深色背景)
dark: "#776e65" // 深色文字(用于浅色背景)
};
// 游戏状态
let grid = []; // 游戏网格
let score = 0; // 当前分数
let gameOver = false; // 游戏是否结束
let won = false; // 是否获胜
let moved = false; // 本次移动是否有方块移动
// 获取Canvas上下文
const canvas = document.getElementById("game-canvas");
const ctx = canvas.getContext("2d");
四、游戏初始化
实现游戏初始化函数,创建空网格并添加初始方块:
// 初始化游戏
function initGame() {
// 重置游戏状态
grid = Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(0));
score = 0;
gameOver = false;
won = false;
moved = false;
// 添加初始的两个方块
addRandomTile();
addRandomTile();
// 绘制游戏界面
drawGame();
}
// 在随机空位置添加一个方块(90%概率为2,10%概率为4)
function addRandomTile() {
// 获取所有空单元格的位置
const emptyCells = [];
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] === 0) {
emptyCells.push({ row, col });
}
}
}
// 如果有空位置,随机选择一个并放置新方块
if (emptyCells.length > 0) {
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
grid[randomCell.row][randomCell.col] = Math.random() < 0.9 ? 2 : 4;
}
}
五、游戏界面绘制
实现绘制游戏界面的核心函数:
// 绘制游戏界面
function drawGame() {
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制背景
ctx.fillStyle = "#bbada0";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制所有方块
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
drawTile(row, col, grid[row][col]);
}
}
}
// 绘制单个方块
function drawTile(row, col, value) {
// 计算方块位置
const x = col * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN;
const y = row * (CELL_SIZE + CELL_MARGIN) + CELL_MARGIN;
// 绘制方块背景
ctx.fillStyle = TILE_COLORS[value] || TILE_COLORS[0];
ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
// 如果方块有值,绘制数字
if (value > 0) {
// 根据数值大小调整字体大小
const fontSize = value < 100 ? 36 : value < 1000 ? 32 : 24;
ctx.font = `bold ${fontSize}px Arial`;
// 根据背景颜色深浅选择文字颜色
ctx.fillStyle = value <= 4 ? TEXT_COLORS.dark : TEXT_COLORS.light;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// 绘制数字
ctx.fillText(
value.toString(),
x + CELL_SIZE / 2,
y + CELL_SIZE / 2
);
}
}
六、核心移动逻辑
实现四个方向的移动逻辑,这是游戏最核心的部分:
// 移动方块(核心逻辑)
function move(direction) {
// 如果游戏已结束,不处理移动
if (gameOver) return;
// 保存移动前的网格状态,用于检测是否发生了移动
const prevGrid = JSON.parse(JSON.stringify(grid));
// 根据方向移动方块
switch (direction) {
case "left":
moveLeft();
break;
case "right":
moveRight();
break;
case "up":
moveUp();
break;
case "down":
moveDown();
break;
}
// 检查网格是否发生了变化
moved = !isEqual(grid, prevGrid);
// 如果发生了移动,添加新方块并检查游戏状态
if (moved) {
addRandomTile();
checkGameState();
drawGame();
}
}
// 向左移动
function moveLeft() {
for (let row = 0; row < GRID_SIZE; row++) {
// 处理当前行
let newRow = [];
// 收集非零元素
for (let col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] !== 0) {
newRow.push(grid[row][col]);
}
}
// 合并相同数字
for (let i = 0; i < newRow.length - 1; i++) {
if (newRow[i] === newRow[i + 1]) {
newRow[i] *= 2;
newRow.splice(i + 1, 1);
score += newRow[i]; // 增加分数
}
}
// 填充剩余位置为零
while (newRow.length < GRID_SIZE) {
newRow.push(0);
}
// 更新网格
grid[row] = newRow;
}
}
// 向右移动(其他方向类似,只是处理顺序不同)
function moveRight() {
for (let row = 0; row < GRID_SIZE; row++) {
// 处理当前行
let newRow = [];
// 从右向左收集非零元素
for (let col = GRID_SIZE - 1; col >= 0; col--) {
if (grid[row][col] !== 0) {
newRow.unshift(grid[row][col]);
}
}
// 合并相同数字(从右向左)
for (let i = 0; i < newRow.length - 1; i++) {
if (newRow[i] === newRow[i + 1]) {
newRow[i] *= 2;
newRow.splice(i + 1, 1);
score += newRow[i];
}
}
// 填充剩余位置为零(在左侧填充)
while (newRow.length < GRID_SIZE) {
newRow.unshift(0);
}
// 更新网格
grid[row] = newRow;
}
}
// 向上移动
function moveUp() {
for (let col = 0; col < GRID_SIZE; col++) {
// 处理当前列
let newCol = [];
// 收集非零元素
for (let row = 0; row < GRID_SIZE; row++) {
if (grid[row][col] !== 0) {
newCol.push(grid[row][col]);
}
}
// 合并相同数字
for (let i = 0; i < newCol.length - 1; i++) {
if (newCol[i] === newCol[i + 1]) {
newCol[i] *= 2;
newCol.splice(i + 1, 1);
score += newCol[i];
}
}
// 填充剩余位置为零
while (newCol.length < GRID_SIZE) {
newCol.push(0);
}
// 更新网格
for (let row = 0; row < GRID_SIZE; row++) {
grid[row][col] = newCol[row];
}
}
}
// 向下移动
function moveDown() {
for (let col = 0; col < GRID_SIZE; col++) {
// 处理当前列
let newCol = [];
// 从下向上收集非零元素
for (let row = GRID_SIZE - 1; row >= 0; row--) {
if (grid[row][col] !== 0) {
newCol.unshift(grid[row][col]);
}
}
// 合并相同数字
for (let i = 0; i < newCol.length - 1; i++) {
if (newCol[i] === newCol[i + 1]) {
newCol[i] *= 2;
newCol.splice(i + 1, 1);
score += newCol[i];
}
}
// 填充剩余位置为零(在上方填充)
while (newCol.length < GRID_SIZE) {
newCol.unshift(0);
}
// 更新网格
for (let row = 0; row < GRID_SIZE; row++) {
grid[row][col] = newCol[row];
}
}
}
七、游戏状态检测
实现游戏状态检测,包括胜利和失败条件:
// 检查游戏状态(是否获胜或失败)
function checkGameState() {
// 检查是否获胜(有1024方块)
if (!won) {
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] === 1024) {
won = true;
gameOver = true;
showMessage("恭喜你赢了!");
return;
}
}
}
}
// 检查是否还有空位置
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] === 0) {
return; // 还有空位置,游戏继续
}
}
}
// 检查是否还能移动(有相邻相同数字)
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
const current = grid[row][col];
// 检查右侧
if (col < GRID_SIZE - 1 && grid[row][col + 1] === current) {
return; // 可以向右合并
}
// 检查下方
if (row < GRID_SIZE - 1 && grid[row + 1][col] === current) {
return; // 可以向下合并
}
}
}
// 没有空位置且不能合并,游戏结束
gameOver = true;
showMessage("游戏结束!");
}
// 辅助函数:比较两个网格是否相同
function isEqual(grid1, grid2) {
for (let row = 0; row < GRID_SIZE; row++) {
for (let col = 0; col < GRID_SIZE; col++) {
if (grid1[row][col] !== grid2[row][col]) {
return false;
}
}
}
return true;
}
八、用户交互与界面完善
添加用户交互和界面元素:
// 获取DOM元素
const scoreElement = document.getElementById("score");
const restartButton = document.getElementById("restart-button");
const tryAgainButton = document.getElementById("try-again-button");
const gameMessage = document.getElementById("game-message");
const messageText = document.getElementById("message-text");
// 更新分数显示
function updateScore() {
scoreElement.textContent = score;
}
// 显示游戏消息
function showMessage(text) {
messageText.textContent = text;
gameMessage.style.display = "flex";
}
// 键盘事件监听
document.addEventListener("keydown", function(event) {
switch (event.key) {
case "ArrowLeft":
event.preventDefault();
move("left");
break;
case "ArrowRight":
event.preventDefault();
move("right");
break;
case "ArrowUp":
event.preventDefault();
move("up");
break;
case "ArrowDown":
event.preventDefault();
move("down");
break;
}
});
// 重新开始按钮事件
restartButton.addEventListener("click", initGame);
tryAgainButton.addEventListener("click", initGame);
// 初始化游戏
initGame();
九、完整HTML结构
最后,将以上所有代码整合到完整的HTML文件中:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>1024游戏 - HTML5 Canvas实现</title>
<style>
body {
font-family: 'Arial', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
background-color: #faf8ef;
color: #776e65;
margin: 0;
padding: 20px;
}
h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.game-container {
position: relative;
margin: 20px auto;
}
.game-info {
display: flex;
justify-content: space-between;
width: 450px;
margin-bottom: 20px;
}
.score-container {
background: #bbada0;
padding: 10px 20px;
border-radius: 6px;
color: white;
font-weight: bold;
text-align: center;
}
.score-title {
font-size: 0.9em;
}
.score-value {
font-size: 1.5em;
}
.restart-button {
background: #8f7a66;
color: white;
border: none;
border-radius: 6px;
padding: 10px 20px;
font-size: 1em;
cursor: pointer;
font-weight: bold;
}
.game-message {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(238, 228, 218, 0.73);
z-index: 100;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 2em;
font-weight: bold;
}
.game-message p {
margin: 0;
}
.game-message button {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>1024游戏</h1>
<div class="game-info">
<div class="score-container">
<div class="score-title">分数</div>
<div class="score-value" id="score">0</div>
</div>
<button class="restart-button" id="restart-button">重新开始</button>
</div>
<div class="game-container">
<canvas id="game-canvas" width="450" height="450"></canvas>
<div class="game-message" id="game-message">
<p id="message-text"></p>
<button class="restart-button" id="try-again-button">再试一次</button>
</div>
</div>
<script>
// 此处放置所有JavaScript代码
// 包括游戏配置、状态变量和所有函数实现
</script>
</body>
</html>
十、总结与扩展
通过以上步骤,我们完整实现了一个功能齐全的1024游戏。这个实现展示了:
- Canvas绘图:如何使用Canvas API绘制游戏界面
- 游戏状态管理:如何跟踪和管理游戏状态
- 核心算法:如何实现方块移动和合并的逻辑
- 用户交互:如何处理键盘事件和按钮点击
- 游戏逻辑:如何判断游戏胜利和失败条件
扩展思路:
- 添加动画效果,使方块移动更平滑
- 实现最高分记录功能
- 添加音效增强游戏体验
- 实现撤销操作功能
- 添加不同难度级别(如3×3或5×5网格)
通过这个手把手的教程,你不仅学会了如何实现1024游戏,还掌握了使用HTML5 Canvas开发游戏的基本技能。这些知识可以应用到其他类似的游戏开发项目中。

5639

被折叠的 条评论
为什么被折叠?



