我用纯 HTML+JS 写了个赛博朋克风俄罗斯方块
前言
俄罗斯方块,一个从 1984 年火到现在的经典游戏。最近手痒,用纯原生 HTML + Canvas + JavaScript 撸了一个科技风(赛博朋克)版本,没有用任何框架,单文件就能跑。
先上效果:
项目特点:
- 🎨 3 套主题:科技蓝 / 纯黑 / 极简白
- 📊 完整数据面板:分数、等级、消除行数、最高分
- ⚡ 流畅操作:支持双方向旋转、硬降、软降、暂停
- 🎵 科技感 UI:霓虹边框、粒子星空背景、发光特效
在线体验:TETRIS - 科技风俄罗斯方块
一、整体架构
整个游戏就是一个 index.html 文件,包含三部分:
css
index.html
├── CSS 样式 ------ 科技风 UI、动画、主题切换
├── HTML 结构 ------ 画布、面板、各种模态框
└── JavaScript ------ 游戏核心逻辑 + 交互
二、游戏核心实现
2.1 方块数据结构
经典的 7 种方块(I、O、T、S、Z、L),用二维数组表示形状:
javascript
const SHAPES = {
I: [
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
O: [
[1, 1],
[1, 1]
],
T: [
[0, 1, 0],
[1, 1, 1],
[0, 0, 0]
],
// S、Z、J、L 同理...
};
每种方块对应一种霓虹色,科技感拉满:
javascript
const COLORS = {
I: '#00dcff', // 青色
O: '#ffc832', // 金色
T: '#c800ff', // 紫色
S: '#00ff96', // 绿色
Z: '#ff3264', // 红色
J: '#3264ff', // 蓝色
L: '#ff9600', // 橙色
};
2.2 游戏主循环
用 requestAnimationFrame 做游戏循环,根据等级控制下落速度:
javascript
let dropCounter = 0;
let dropInterval = 1000; // 初始 1 秒下落一格
let lastTime = 0;
function update(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
pieceDrop(); // 方块下落
dropCounter = 0;
}
draw(); // 渲染画面
requestAnimationFrame(update);
}
等级越高,dropInterval 越小,下落越快:
javascript
// 每消除 10 行升一级,速度递增
dropInterval = Math.max(100, 1000 - (level - 1) * 80);
2.3 旋转算法
用矩阵转置 + 行反转实现顺时针旋转:
javascript
function rotate(matrix, dir) {
// 转置
for (let y = 0; y < matrix.length; y++) {
for (let x = 0; x < y; x++) {
[matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
}
}
// 顺时针:反转每一行;逆时针:反转整个数组
if (dir > 0) {
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
旋转时做墙踢检测(Wall Kick),避免贴着墙转不动:
javascript
function pieceRotate(dir) {
const pos = piece.x;
let offset = 1;
rotate(piece.matrix, dir);
// 如果撞墙了,一点点挪回去
while (checkCollision(board, piece)) {
piece.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if (offset > piece.matrix[0].length) {
rotate(piece.matrix, -dir); // 转不回去就复原
piece.x = pos;
return;
}
}
}
2.4 消行与评分
满行检测 + 下移 + 分数计算:
javascript
function lineClear() {
let lines = 0;
outer: for (let y = board.length - 1; y > 0; y--) {
for (let x = 0; x < board[y].length; x++) {
if (board[y][x] === 0) continue outer;
}
// 这行满了,删掉并在顶部补一行空的
const row = board.splice(y, 1)[0].fill(0);
board.unshift(row);
y++; // 重新检查当前行
lines++;
}
// 经典俄罗斯方块计分规则
const scoreTable = [0, 100, 300, 500, 800];
score += scoreTable[lines] * level;
linesCleared += lines;
}
三、科技风 UI 怎么做的
3.1 霓虹发光边框
核心就是多层 box-shadow:
css
canvas {
border: 2px solid #00c8ff;
box-shadow:
0 0 20px rgba(0, 200, 255, 0.3),
inset 0 0 30px rgba(0, 100, 200, 0.1);
}
外发光 + 内发光,赛博朋克味儿一下就出来了。
3.2 星空粒子背景
JS 动态生成 50 颗星星,CSS 动画闪烁:
javascript
function createStars() {
const container = document.getElementById('stars');
for (let i = 0; i < 50; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.width = (Math.random() * 3 + 1) + 'px';
star.style.height = star.style.width;
star.style.animationDelay = Math.random() * 3 + 's';
container.appendChild(star);
}
}
3.3 网格背景
纯 CSS 渐变搞定:
css
body::before {
content: '';
position: fixed;
background-image:
linear-gradient(rgba(20, 40, 80, 0.3) 1px, transparent 1px),
linear-gradient(90deg, rgba(20, 40, 80, 0.3) 1px, transparent 1px);
background-size: 50px 50px;
}
3.4 主题切换
用 CSS 变量 + 类名切换,三套主题一键换:
javascript
function setTheme(theme) {
const themes = {
blue: {
bg: '#080c1c',
text: '#dcf0ff',
accent: '#00dcff',
border: '#00c8ff'
},
dark: { /* ... */ },
white: { /* ... */ }
};
Object.entries(themes[theme]).forEach(([key, val]) => {
document.documentElement.style.setProperty(`--${key}`, val);
});
}
四、操作说明
| 按键 | 功能 |
|---|---|
| ← → / A D | 左右移动 |
| ↑ / W / X | 顺时针旋转 |
| Z / Ctrl | 逆时针旋转 |
| ↓ / S | 加速下落 |
| 空格 | 直接落底 |
| P / ESC | 暂停游戏 |
结语
俄罗斯方块虽然是个老游戏,但每次重写都有新的收获------从游戏循环、碰撞检测到 UI 动效,从头到尾撸一遍真的很锻炼基础。
如果你也喜欢复古游戏 + 科技风,欢迎在评论区探讨!