本篇文章主要讲使用HTML、CSS和JavaScript实现一个简单的俄罗斯方块游戏,包含基本的游戏逻辑、行消除功能以及暂停和继续游戏的控制。
使用工具
本篇文章有用到ChatGPT-4o代码纠错,国内免翻且稳定,感兴趣的大佬试试。
传送门:363Ai工具箱
HTML部分
html
<div id="game"></div>
<div id="message"></div>
<div id="controls">
<button onclick="startGame()">继续游戏</button>
<button onclick="pauseGame()">暂停游戏</button>
</div>
解释:
- <div id="game">:游戏界面,使用CSS Grid布局。
- <div id="message">:显示游戏失败信息。
- 按钮:用于控制游戏的继续和暂停。
CSS部分
css
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
#game {
display: grid;
grid-template-columns: repeat(10, 30px);
grid-template-rows: repeat(20, 30px);
gap: 1px;
background-color: #333;
}
.cell {
width: 30px;
height: 30px;
background-color: #fff;
}
解释:
- 布局:使用Flexbox和Grid布局设计游戏界面。
- 方块样式:不同形状的方块用不同的颜色表示。
JavaScript部分
游戏逻辑主要使用JavaScript实现。
初始化
javascript
const rows = 20;
const cols = 10;
let board = Array.from({ length: rows }, () => Array(cols).fill(''));
let gameInterval;
let isPaused = false;
let dropInterval = 1000;
解释:
- 游戏板:一个二维数组表示20行和10列的游戏区域。
- 游戏状态:gameInterval用于控制方块下落的定时器,isPaused表示游戏是否暂停。
方块定义
javascript
const tetrominoes = {
I: [[1, 1, 1, 1]],
J: [[1, 0, 0], [1, 1, 1]],
// 其他形状...
};
解释:
- 定义了所有俄罗斯方块的形状。
绘制函数
javascript
function drawBoard() {
game.innerHTML = '';
board.forEach(row => {
row.forEach(cell => {
const div = document.createElement('div');
div.className = `cell ${cell}`;
game.appendChild(div);
});
});
}
解释:
- drawBoard():根据board数组更新游戏界面。
方块移动与旋转
javascript
function canMove(position, shape) {
return shape.every((row, i) =>
row.every((cell, j) =>
!cell || (board[position.y + i] && board[position.y + i][position.x + j] === '')
)
);
}
function rotate(shape) {
return shape[0].map((_, index) => shape.map(row => row[index]).reverse());
}
解释:
- canMove():检查方块是否可以移动到指定位置。
- rotate():旋转方块的矩阵。
行消除功能
javascript
function clearFullRows() {
board = board.reduce((acc, row) => {
if (row.every(cell => cell !== '')) {
acc.unshift(Array(cols).fill(''));
} else {
acc.push(row);
}
return acc;
}, []);
}
解释:
- clearFullRows()函数:遍历游戏板,检查是否有行被填满。
- 若有,则移除该行,并在顶部添加一行空行。
游戏循环
javascript
function drop() {
if (isPaused) return;
clearTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
currentTetromino.position.y++;
if (!canMove(currentTetromino.position, currentTetromino.shape)) {
currentTetromino.position.y--;
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
if (currentTetromino.position.y === 0) {
message.textContent = '你失败啦';
clearInterval(gameInterval);
} else {
clearFullRows();
currentTetromino = {
type: Object.keys(tetrominoes)[Math.floor(Math.random() * 7)],
position: { x: 3, y: 0 },
shape: tetrominoes[Object.keys(tetrominoes)[Math.floor(Math.random() * 7)]]
};
}
}
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
drawBoard();
}
加速下降功能
- 按键事件:在keydown事件中,添加对ArrowDown的监听。
- 加速下降实现:当按下下箭头键时,调用drop()函数,使方块立即下降一格。
用户交互
javascript
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
rotateTetromino();
} else if (e.key === 'ArrowLeft') {
moveLeft();
} else if (e.key === 'ArrowRight') {
moveRight();
} else if (e.key === 'ArrowDown') {
drop();
}
});
- 使用键盘箭头键控制方块的移动、旋转和加速下落。
运行界面:
以下是完整代码:
html
<!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>
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
#game {
display: grid;
grid-template-columns: repeat(10, 30px);
grid-template-rows: repeat(20, 30px);
gap: 1px;
background-color: #333;
}
.cell {
width: 30px;
height: 30px;
background-color: #fff;
}
.I { background-color: #00f0f0; }
.J { background-color: #0000f0; }
.L { background-color: #f0a000; }
.O { background-color: #f0f000; }
.S { background-color: #00f000; }
.T { background-color: #a000f0; }
.Z { background-color: #f00000; }
#message, #score {
margin-top: 20px;
font-size: 24px;
}
#message {
color: red;
}
#controls {
margin-top: 10px;
}
button {
padding: 10px;
font-size: 16px;
margin: 5px;
}
</style>
</head>
<body>
<div id="game"></div>
<div id="message"></div>
<div id="score"></div>
<div id="controls">
<button onclick="startGame()">开始游戏</button>
<button onclick="pauseGame()">暂停游戏</button>
<button onclick="restartGame()">重新开始</button>
</div>
<script>
const game = document.getElementById('game');
const message = document.getElementById('message');
const scoreDisplay = document.getElementById('score');
const rows = 20;
const cols = 10;
let board, gameInterval, isPaused, dropInterval, score, level, currentTetromino;
const tetrominoes = {
I: [[1, 1, 1, 1]],
J: [[1, 0, 0], [1, 1, 1]],
L: [[0, 0, 1], [1, 1, 1]],
O: [[1, 1], [1, 1]],
S: [[0, 1, 1], [1, 1, 0]],
T: [[0, 1, 0], [1, 1, 1]],
Z: [[1, 1, 0], [0, 1, 1]]
};
function initGame() {
board = Array.from({ length: rows }, () => Array(cols).fill(''));
currentTetromino = {
type: Object.keys(tetrominoes)[Math.floor(Math.random() * 7)],
position: { x: 3, y: 0 },
shape: tetrominoes[Object.keys(tetrominoes)[Math.floor(Math.random() * 7)]]
};
message.textContent = '';
score = 0;
level = 1;
dropInterval = 1000;
updateScore();
drawBoard();
}
function drawBoard() {
game.innerHTML = '';
board.forEach(row => {
row.forEach(cell => {
const div = document.createElement('div');
div.className = `cell ${cell}`;
game.appendChild(div);
});
});
}
function drawTetromino(type, position, shape) {
shape.forEach((row, i) => {
row.forEach((cell, j) => {
if (cell) {
board[position.y + i][position.x + j] = type;
}
});
});
}
function clearTetromino(type, position, shape) {
shape.forEach((row, i) => {
row.forEach((cell, j) => {
if (cell) {
board[position.y + i][position.x + j] = '';
}
});
});
}
function canMove(position, shape) {
return shape.every((row, i) =>
row.every((cell, j) =>
!cell || (board[position.y + i] && board[position.y + i][position.x + j] === '')
)
);
}
function rotate(shape) {
return shape[0].map((_, index) => shape.map(row => row[index]).reverse());
}
function clearFullRows() {
let linesCleared = 0;
board = board.reduce((acc, row) => {
if (row.every(cell => cell !== '')) {
acc.unshift(Array(cols).fill(''));
linesCleared++;
} else {
acc.push(row);
}
return acc;
}, []);
if (linesCleared > 0) {
score += linesCleared * 100;
if (score >= level * 1000) {
level++;
dropInterval = Math.max(100, dropInterval - 100);
clearInterval(gameInterval);
startGame();
}
updateScore();
}
}
function updateScore() {
scoreDisplay.textContent = `得分: ${score} 级别: ${level}`;
}
function drop() {
if (isPaused) return;
clearTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
currentTetromino.position.y++;
if (!canMove(currentTetromino.position, currentTetromino.shape)) {
currentTetromino.position.y--;
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
if (currentTetromino.position.y === 0) {
message.textContent = '你失败啦';
setTimeout(restartGame, 2000);
clearInterval(gameInterval);
} else {
clearFullRows();
currentTetromino = {
type: Object.keys(tetrominoes)[Math.floor(Math.random() * 7)],
position: { x: 3, y: 0 },
shape: tetrominoes[Object.keys(tetrominoes)[Math.floor(Math.random() * 7)]]
};
}
}
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
drawBoard();
}
function rotateTetromino() {
if (isPaused) return;
clearTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
const rotatedShape = rotate(currentTetromino.shape);
if (canMove(currentTetromino.position, rotatedShape)) {
currentTetromino.shape = rotatedShape;
}
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
drawBoard();
}
function moveLeft() {
if (isPaused) return;
clearTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
currentTetromino.position.x--;
if (!canMove(currentTetromino.position, currentTetromino.shape)) {
currentTetromino.position.x++;
}
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
drawBoard();
}
function moveRight() {
if (isPaused) return;
clearTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
currentTetromino.position.x++;
if (!canMove(currentTetromino.position, currentTetromino.shape)) {
currentTetromino.position.x--;
}
drawTetromino(currentTetromino.type, currentTetromino.position, currentTetromino.shape);
drawBoard();
}
function startGame() {
isPaused = false;
clearInterval(gameInterval);
gameInterval = setInterval(drop, dropInterval);
}
function pauseGame() {
isPaused = true;
clearInterval(gameInterval);
}
function restartGame() {
initGame();
clearInterval(gameInterval);
}
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
rotateTetromino();
} else if (e.key === 'ArrowLeft') {
moveLeft();
} else if (e.key === 'ArrowRight') {
moveRight();
} else if (e.key === 'ArrowDown') {
drop();
}
});
initGame();
</script>
</body>
</html>
这段代码展示了如何用JavaScript实现一个基础的俄罗斯方块游戏,支持基本的方块操作、行消除和游戏状态控制。可以在此基础上添加更多功能,比如计分系统、关卡设计等。
对于编程新手或正在学习新知识的程序员,可以使用ChatGPT来做助手,减少出错性和重复性代码的时间。
感谢阅读!!!