<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML5 俄罗斯方块</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #f0f0f0;
padding: 20px;
}
h1 {
color: #333;
margin-bottom: 20px;
text-align: center;
}
.game-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
canvas {
background-color: #111;
border-radius: 5px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
.info-panel {
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 200px;
}
.score-container {
margin-bottom: 20px;
}
.next-piece {
margin-bottom: 20px;
}
.controls {
margin-bottom: 20px;
}
h2 {
font-size: 18px;
color: #444;
margin-bottom: 10px;
}
p {
font-size: 16px;
color: #666;
margin-bottom: 5px;
}
.game-over {
position: absolute;
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
display: none;
}
.restart-btn {
margin-top: 15px;
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.restart-btn:hover {
background-color: #45a049;
}
.website-link {
display: inline-block;
margin-top: 20px;
padding: 12px 24px;
background: linear-gradient(to right, #FF416C, #FF4B2B);
color: white;
text-decoration: none;
border-radius: 30px;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
}
.website-link:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
</style>
</head>
<body>
<h1>HTML5 Canvas 俄罗斯方块</h1>
<div class="game-container">
<canvas id="tetris" width="300" height="600"></canvas>
<div class="info-panel">
<div class="score-container">
<h2>分数</h2>
<p id="score">0</p>
</div>
<div class="next-piece">
<h2>下一个方块</h2>
<canvas id="nextPiece" width="120" height="120"></canvas>
</div>
<div class="controls">
<h2>操作说明</h2>
<p>↑ - 旋转</p>
<p>↓ - 加速下落</p>
<p>← → - 左右移动</p>
<p>空格 - 直接下落</p>
<p>P - 暂停游戏</p>
</div>
</div>
</div>
<div class="game-over" id="gameOver">
<h2>游戏结束!</h2>
<p>最终分数: <span id="finalScore">0</span></p>
<button class="restart-btn" id="restartBtn">重新开始</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取Canvas元素和上下文
const canvas = document.getElementById('tetris');
const ctx = canvas.getContext('2d');
const nextCanvas = document.getElementById('nextPiece');
const nextCtx = nextCanvas.getContext('2d');
// 设置方块大小和游戏区域大小
const blockSize = 30;
const cols = canvas.width / blockSize;
const rows = canvas.height / blockSize;
// 游戏状态变量
let score = 0;
let isGameOver = false;
let isPaused = false;
let gameSpeed = 1000; // 初始下落速度(毫秒)
let dropStart = Date.now();
// 游戏板 - 20行10列
let board = Array.from(Array(rows), () => Array(cols).fill(0));
// 方块形状和颜色
const shapes = [
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], // I
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0], // J
[0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0], // L
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], // O
[0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0], // S
[0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // T
[1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] // Z
];
const colors = [
'#00FFFF', // I - 青色
'#0000FF', // J - 蓝色
'#FF7F00', // L - 橙色
'#FFFF00', // O - 黄色
'#00FF00', // S - 绿色
'#800080', // T - 紫色
'#FF0000' // Z - 红色
];
// 当前方块和下一个方块
let currentPiece = null;
let nextPiece = null;
// 初始化游戏
function init() {
board = Array.from(Array(rows), () => Array(cols).fill(0));
score = 0;
isGameOver = false;
isPaused = false;
gameSpeed = 1000;
updateScore();
document.getElementById('gameOver').style.display = 'none';
// 生成新方块
generatePiece();
// 开始游戏循环
if (!isPaused) {
requestAnimationFrame(gameLoop);
}
}
// 生成新方块
function generatePiece() {
if (!nextPiece) {
const rand = Math.floor(Math.random() * shapes.length);
nextPiece = {
shape: shapes[rand],
color: colors[rand],
x: Math.floor(cols / 2) - 2,
y: -2
};
}
currentPiece = nextPiece;
const rand = Math.floor(Math.random() * shapes.length);
nextPiece = {
shape: shapes[rand],
color: colors[rand],
x: Math.floor(cols / 2) - 2,
y: -2
};
// 绘制下一个方块预览
drawNextPiece();
// 检查游戏是否结束
if (collision(0, 0)) {
gameOver();
}
}
// 绘制下一个方块预览
function drawNextPiece() {
nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
const size = nextCanvas.width / 4;
for (let i = 0; i < 16; i++) {
if (nextPiece.shape[i]) {
nextCtx.fillStyle = nextPiece.color;
nextCtx.fillRect((i % 4) * size, Math.floor(i / 4) * size, size, size);
nextCtx.strokeStyle = '#111';
nextCtx.strokeRect((i % 4) * size, Math.floor(i / 4) * size, size, size);
}
}
}
// 游戏主循环
function gameLoop() {
const now = Date.now();
const delta = now - dropStart;
if (delta > gameSpeed && !isPaused) {
drop();
dropStart = now;
}
draw();
if (!isGameOver && !isPaused) {
requestAnimationFrame(gameLoop);
}
}
// 绘制游戏
function draw() {
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制已落下的方块
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
if (board[y][x]) {
drawBlock(x, y, colors[board[y][x] - 1]);
}
}
}
// 绘制当前方块
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
if (currentPiece.shape[y * 4 + x]) {
drawBlock(
currentPiece.x + x,
currentPiece.y + y,
currentPiece.color
);
}
}
}
}
// 绘制单个方块
function drawBlock(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * blockSize, y * blockSize, blockSize, blockSize);
ctx.strokeStyle = '#111';
ctx.strokeRect(x * blockSize, y * blockSize, blockSize, blockSize);
}
// 碰撞检测
function collision(offsetX, offsetY, newShape) {
const shape = newShape || currentPiece.shape;
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
if (shape[y * 4 + x]) {
const newX = currentPiece.x + x + offsetX;
const newY = currentPiece.y + y + offsetY;
if (newX < 0 || newX >= cols || newY >= rows) {
return true;
}
if (newY < 0) {
continue;
}
if (board[newY][newX]) {
return true;
}
}
}
}
return false;
}
// 下落方块
function drop() {
if (!collision(0, 1)) {
currentPiece.y++;
return;
}
// 方块已经不能下落,固定在游戏板上
lockPiece();
// 检查是否有完整的行
checkRows();
// 生成新方块
generatePiece();
}
// 锁定方块到游戏板
function lockPiece() {
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
if (currentPiece.shape[y * 4 + x]) {
board[currentPiece.y + y][currentPiece.x + x] = colors.indexOf(currentPiece.color) + 1;
}
}
}
}
// 检查是否有完整的行
function checkRows() {
let linesCleared = 0;
for (let y = rows - 1; y >= 0; y--) {
if (board[y].every(cell => cell !== 0)) {
// 移除该行并在顶部添加新行
board.splice(y, 1);
board.unshift(Array(cols).fill(0));
linesCleared++;
y++; // 重新检查当前行
}
}
if (linesCleared > 0) {
// 计算得分
const points = [0, 40, 100, 300, 1200][linesCleared] * (Math.floor(score / 1000) + 1);
score += points;
// 提高游戏速度
gameSpeed = Math.max(100, 1000 - Math.floor(score / 500) * 50);
updateScore();
}
}
// 旋转方块
function rotate() {
const newShape = Array(16).fill(0);
// 转置矩阵
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
newShape[x * 4 + (3 - y)] = currentPiece.shape[y * 4 + x];
}
}
// 检查旋转后是否会碰撞
if (!collision(0, 0, newShape)) {
currentPiece.shape = newShape;
} else {
// 尝试移动后再旋转
if (!collision(-1, 0, newShape)) {
currentPiece.shape = newShape;
currentPiece.x--;
} else if (!collision(1, 0, newShape)) {
currentPiece.shape = newShape;
currentPiece.x++;
}
}
}
// 移动方块
function move(dir) {
if (!collision(dir, 0)) {
currentPiece.x += dir;
}
}
// 硬降落 - 直接落到底部
function hardDrop() {
while (!collision(0, 1)) {
currentPiece.y++;
}
drop(); // 锁定方块并生成新方块
}
// 更新分数显示
function updateScore() {
document.getElementById('score').textContent = score;
}
// 游戏结束
function gameOver() {
isGameOver = true;
document.getElementById('finalScore').textContent = score;
document.getElementById('gameOver').style.display = 'block';
}
// 暂停游戏
function togglePause() {
isPaused = !isPaused;
if (!isPaused && !isGameOver) {
dropStart = Date.now();
gameLoop();
}
}
// 键盘控制
document.addEventListener('keydown', function(e) {
if (isGameOver) return;
switch (e.keyCode) {
case 37: // 左箭头
move(-1);
break;
case 39: // 右箭头
move(1);
break;
case 40: // 下箭头
drop();
break;
case 38: // 上箭头
rotate();
break;
case 32: // 空格
hardDrop();
break;
case 80: // P键
togglePause();
break;
}
});
// 重新开始按钮
document.getElementById('restartBtn').addEventListener('click', init);
// 开始游戏
init();
});
</script>
</body>
</html>
HTML5 俄罗斯方块
于 2025-04-24 16:30:55 首次发布
该文章已生成可运行项目,
本文章已经生成可运行项目
2900

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



