基于 HTML5 Canvas 制作一个精美的 2048 小游戏--day 1
基于 HTML5 Canvas 制作一个精美的 2048 小游戏
在这个快节奏的生活中,简单而富有挑战性的游戏总能给我们带来乐趣。2048 是一款受欢迎的益智游戏,不仅考验智力,还能让人回味无穷。今天,我带领大家将一起学习如何使用 HTML5 Canvas 来制作一个精美的 2048 小游戏。
一、了解游戏规则
在深入代码之前,我们需要了解游戏的基本规则:
- 目标:通过合并相同的数字块,最终达到2048。
- 操作:玩家可以通过上下左右的箭头键控制数字块的移动。
- 生成新块:每次成功移动后,会随机生成一个“2”或“4”的数字块。
- 游戏结束:当所有方块被填满且无法进行任何合并时,游戏结束。
二、建立 HTML 结构
首先,我们需要搭建游戏的基础 HTML 结构。在 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>2048 游戏</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<canvas id="gameCanvas"></canvas>
</div>
<script src="script.js"></script>
</body>
</html>
三、样式设计
接下来,我们需要为游戏添加一些样式,使其更具吸引力。我们将在 CSS 文件中设置按钮和画布的样式。
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #faf8ef;
font-family: 'Arial', sans-serif;
}
.container {
position: relative;
}
canvas {
border: 2px solid #bbada0;
background-color: #eee4da;
}
四、游戏逻辑实现
在脚本文件中,我们将编写游戏的核心逻辑,包括初始化游戏、绘制方块、移动操作和合并方块等。
4.1 初始化
首先,我们需要创建一个类似于二维数组的数字格子,并用随机数填充初始状态。
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 4;
const tileSize = 100;
canvas.width = gridSize * tileSize;
canvas.height = gridSize * tileSize;
let board = Array.from({ length: gridSize }, () => Array(gridSize).fill(0));
function initBoard() {
addRandomTile();
addRandomTile();
drawBoard();
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
drawTile(r, c);
}
}
}
function drawTile(r, c) {
const value = board[r][c];
ctx.fillStyle = value !== 0 ? getTileColor(value) : '#ccc0b3';
ctx.fillRect(c * tileSize, r * tileSize, tileSize - 10, tileSize - 10);
if (value !== 0) {
ctx.fillStyle = '#776e65';
ctx.font = 'bold 45px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(value, c * tileSize + tileSize / 2 - 5, r * tileSize + tileSize / 2);
}
}
function getTileColor(value) {
switch (value) {
case 2: return '#eee4da';
case 4: return '#ede0c8';
case 8: return '#f2b179';
case 16: return '#f59563';
case 32: return '#f67c5f';
case 64: return '#f67c5f';
case 128: return '#edcf72';
case 256: return '#edcc61';
case 512: return '#edc850';
case 1024: return '#edc53f';
case 2048: return '#edc22e';
default: return '#ccc0b3';
}
}
function addRandomTile() {
let emptyCells = [];
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
if (board[r][c] === 0) {
emptyCells.push({ r, c });
}
}
}
if (emptyCells.length) {
const { r, c } = emptyCells[Math.floor(Math.random() * emptyCells.length)];
board[r][c] = Math.random() < 0.9 ? 2 : 4;
}
}
4.2 移动与合并方块
通过键盘事件监听来实现方块的移动和合并,我们定义方向常量,结合用户输入的方向实现移动和合并的逻辑。
document.addEventListener('keydown', (event) => {
switch (event.key) {
case 'ArrowUp':
moveUp();
break;
case 'ArrowDown':
moveDown();
break;
case 'ArrowLeft':
moveLeft();
break;
case 'ArrowRight':
moveRight();
break;
}
drawBoard();
});
function canMergeTiles(r1, c1, r2, c2) {
return board[r1][c1] !== 0 && board[r1][c1] === board[r2][c2];
}
function moveUp() {
for (let c = 0; c < gridSize; c++) {
for (let r = 1; r < gridSize; r++) {
if (board[r][c] !== 0) {
let targetRow = r;
while (targetRow > 0 && board[targetRow - 1][c] === 0) {
// 向上移动
board[targetRow - 1][c] = board[targetRow][c];
board[targetRow][c] = 0;
targetRow--;
}
if (targetRow > 0 && canMergeTiles(targetRow - 1, c, targetRow, c)) {
// 合并方块
board[targetRow - 1][c] *= 2;
board[targetRow][c] = 0;
}
}
}
}
}
function moveDown() {
for (let c = 0; c < gridSize; c++) {
for (let r = gridSize - 2; r >= 0; r--) {
if (board[r][c] !== 0) {
let targetRow = r;
while (targetRow < gridSize - 1 && board[targetRow + 1][c] === 0) {
// 向下移动
board[targetRow + 1][c] = board[targetRow][c];
board[targetRow][c] = 0;
targetRow++;
}
if (targetRow < gridSize - 1 && canMergeTiles(targetRow + 1, c, targetRow, c)) {
// 合并方块
board[targetRow + 1][c] *= 2;
board[targetRow][c] = 0;
}
}
}
}
}
function moveLeft() {
for (let r = 0; r < gridSize; r++) {
for (let c = 1; c < gridSize; c++) {
if (board[r][c] !== 0) {
let targetCol = c;
while (targetCol > 0 && board[r][targetCol - 1] === 0) {
// 向左移动
board[r][targetCol - 1] = board[r][targetCol];
board[r][targetCol] = 0;
targetCol--;
}
if (targetCol > 0 && canMergeTiles(r, targetCol - 1, r, targetCol)) {
// 合并方块
board[r][targetCol - 1] *= 2;
board[r][targetCol] = 0;
}
}
}
}
}
function moveRight() {
for (let r = 0; r < gridSize; r++) {
for (let c = gridSize - 2; c >= 0; c--) {
if (board[r][c] !== 0) {
let targetCol = c;
while (targetCol < gridSize - 1 && board[r][targetCol + 1] === 0) {
// 向右移动
board[r][targetCol + 1] = board[r][targetCol];
board[r][targetCol] = 0;
targetCol++;
}
if (targetCol < gridSize - 1 && canMergeTiles(r, targetCol + 1, r, targetCol)) {
// 合并方块
board[r][targetCol + 1] *= 2;
board[r][targetCol] = 0;
}
}
}
}
}
五、完善游戏体验
在游戏逻辑实现后,我们需要添加分数计算、胜利和失败的提示,以及重新开始游戏的功能。这些都将进一步提升游戏体验。
5.1 分数系统
我们为游戏添加分数系统,每次合并方块时更新分数。
let score = 0;
function mergeTiles(r1, c1, r2, c2) {
if (board[r1][c1] === board[r2][c2]) {
board[r1][c1] *= 2;
score += board[r1][c1];
board[r2][c2] = 0;
}
}
5.2 结束提示
当玩家没有可移动的方块时,可以弹出提示框告知游戏结束。
function checkGameOver() {
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
if (board[r][c] === 0) {
return false; // 还有空格
}
if (c < gridSize - 1 && canMergeTiles(r, c, r, c + 1)) {
return false; // 可以合并
}
if (r < gridSize - 1 && canMergeTiles(r, c, r + 1, c)) {
return false; // 可以合并
}
}
}
return true; // 游戏结束
}
function showGameOver() {
alert('游戏结束!您的得分是:' + score);
}
结论
现在,您应该对如何使用 HTML5 Canvas 制作一个 2048 小游戏有了更详细的了解。从简单的 UI 设计到移动和合并方块的逻辑实现,每一步都至关重要。虽然本文未能详细说明所有代码,但希望您能根据提供的思路和示例进行深入探索和实现。创建游戏是一项有趣且富有成就感的工作,快去尝试制作您自己的 2048 小游戏吧!
完整代码
HTML (index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2048 游戏</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<canvas id="gameCanvas"></canvas>
</div>
<script src="script.js"></script>
</body>
</html>
CSS (style.css)
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #faf8ef;
font-family: 'Arial', sans-serif;
}
.container {
position: relative;
}
canvas {
border: 2px solid #bbada0;
background-color: #eee4da;
}
JavaScript (script.js)
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gridSize = 4;
const tileSize = 100;
canvas.width = gridSize * tileSize;
canvas.height = gridSize * tileSize;
let board = Array.from({ length: gridSize }, () => Array(gridSize).fill(0));
let score = 0;
initBoard();
function initBoard() {
addRandomTile();
addRandomTile();
drawBoard();
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
drawTile(r, c);
}
}
// 显示分数
ctx.fillStyle = '#776e65';
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'center';
ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height - 20);
}
function drawTile(r, c) {
const value = board[r][c];
ctx.fillStyle = value !== 0 ? getTileColor(value) : '#ccc0b3';
ctx.fillRect(c * tileSize, r * tileSize, tileSize - 10, tileSize - 10);
if (value !== 0) {
ctx.fillStyle = '#776e65';
ctx.font = 'bold 45px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(value, c * tileSize + tileSize / 2 - 5, r * tileSize + tileSize / 2);
}
}
function getTileColor(value) {
switch (value) {
case 2: return '#eee4da';
case 4: return '#ede0c8';
case 8: return '#f2b179';
case 16: return '#f59563';
case 32: return '#f67c5f';
case 64: return '#f67c5f';
case 128: return '#edcf72';
case 256: return '#edcc61';
case 512: return '#edc850';
case 1024: return '#edc53f';
case 2048: return '#edc22e';
default: return '#ccc0b3';
}
}
function addRandomTile() {
let emptyCells = [];
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
if (board[r][c] === 0) {
emptyCells.push({ r, c });
}
}
}
if (emptyCells.length) {
const { r, c } = emptyCells[Math.floor(Math.random() * emptyCells.length)];
board[r][c] = Math.random() < 0.9 ? 2 : 4;
}
}
document.addEventListener('keydown', (event) => {
let moved = false;
switch (event.key) {
case 'ArrowUp':
moved = moveUp();
break;
case 'ArrowDown':
moved = moveDown();
break;
case 'ArrowLeft':
moved = moveLeft();
break;
case 'ArrowRight':
moved = moveRight();
break;
}
if (moved) {
addRandomTile();
drawBoard();
if (checkGameOver()) {
showGameOver();
}
}
});
function canMergeTiles(r1, c1, r2, c2) {
return board[r1][c1] !== 0 && board[r1][c1] === board[r2][c2];
}
function moveUp() {
let moved = false;
for (let c = 0; c < gridSize; c++) {
for (let r = 1; r < gridSize; r++) {
if (board[r][c] !== 0) {
let targetRow = r;
while (targetRow > 0 && board[targetRow - 1][c] === 0) {
board[targetRow - 1][c] = board[targetRow][c];
board[targetRow][c] = 0;
targetRow--;
moved = true;
}
if (targetRow > 0 && canMergeTiles(targetRow - 1, c, targetRow, c)) {
board[targetRow - 1][c] *= 2;
score += board[targetRow - 1][c];
board[targetRow][c] = 0;
moved = true;
}
}
}
}
return moved;
}
function moveDown() {
let moved = false;
for (let c = 0; c < gridSize; c++) {
for (let r = gridSize - 2; r >= 0; r--) {
if (board[r][c] !== 0) {
let targetRow = r;
while (targetRow < gridSize - 1 && board[targetRow + 1][c] === 0) {
board[targetRow + 1][c] = board[targetRow][c];
board[targetRow][c] = 0;
targetRow++;
moved = true
}
if (targetRow < gridSize - 1 && canMergeTiles(targetRow + 1, c, targetRow, c)) {
board[targetRow + 1][c] *= 2;
score += board[targetRow + 1][c];
board[targetRow][c] = 0;
moved = true;
}
}
}
}
return moved;
}
function moveLeft() {
let moved = false;
for (let r = 0; r < gridSize; r++) {
for (let c = 1; c < gridSize; c++) {
if (board[r][c] !== 0) {
let targetCol = c;
while (targetCol > 0 && board[r][targetCol - 1] === 0) {
board[r][targetCol - 1] = board[r][targetCol];
board[r][targetCol] = 0;
targetCol--;
moved = true;
}
if (targetCol > 0 && canMergeTiles(r, targetCol - 1, r, targetCol)) {
board[r][targetCol - 1] *= 2;
score += board[r][targetCol - 1];
board[r][targetCol] = 0;
moved = true;
}
}
}
}
return moved;
}
function moveRight() {
let moved = false;
for (let r = 0; r < gridSize; r++) {
for (let c = gridSize - 2; c >= 0; c--) {
if (board[r][c] !== 0) {
let targetCol = c;
while (targetCol < gridSize - 1 && board[r][targetCol + 1] === 0) {
board[r][targetCol + 1] = board[r][targetCol];
board[r][targetCol] = 0;
targetCol++;
moved = true;
}
if (targetCol < gridSize - 1 && canMergeTiles(r, targetCol + 1, r, targetCol)) {
board[r][targetCol + 1] *= 2;
score += board[r][targetCol + 1];
board[r][targetCol] = 0;
moved = true;
}
}
}
}
return moved;
}
function checkGameOver() {
for (let r = 0; r < gridSize; r++) {
for (let c = 0; c < gridSize; c++) {
if (board[r][c] === 0) {
return false; // 还有空格
}
if (c < gridSize - 1 && canMergeTiles(r, c, r, c + 1)) {
return false; // 可以合并
}
if (r < gridSize - 1 && canMergeTiles(r, c, r + 1, c)) {
return false; // 可以合并
}
}
}
return true; // 游戏结束
}
function showGameOver() {
alert('游戏结束!您的得分是:' + score);
}
原文地址:https://blog.csdn.net/wxdzuishaui/article/details/145215858
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!