上一篇是零依赖的裸 X11,这一篇反方向:机器上正好有 Qt5 开发包,那就用最高层的工具把同一个目标做到最少代码。结果是一个 360 行的单文件,不需要 .pro、不需要 CMake、不需要 moc:
g++ -O2 -fPIC tetris.cpp -o tetris $(pkg-config --cflags --libs Qt5Widgets)
为什么不需要 moc
Qt 的 moc 只在用了 Q_OBJECT 宏(自定义信号/槽)时才需要。单文件程序里只要用 lambda 版本的重载 connect,整个文件就是一个普通 C++ 翻译单元,g++ 直接吃:
QObject::connect(timer, &QTimer::timeout, this, [this]{ step(); });
规则实现
- 7-bag 随机:每 7 块一袋洗匀,避免长序列缺某块。
- 幽灵投影:每帧把当前方块沿重力方向试落到底,画一层半透明轮廓。
- 踢墙旋转:顺时针转 90° 后依次尝试 4 个偏移(左、右、左上、右上),碰到墙和底才放弃。
- 计分:一次消 1/2/3/4 行得 100/300/500/800 × 等级;每 10 行升一级,落速从 800ms 线性压到 80ms。
一个真实的渲染坑
侧边栏用 QPainter::drawText(QRect, ...) 画"分数 / 行数 / 等级"。第一版数值框只有 55px 宽,分数到四位数时千位直接被矩形裁掉——drawText 带矩形参数时严格只在矩形内画。修法是把数值改成整条面板宽度内右对齐:右边缘固定,位数增加时向左生长,永远不会被切。
教训:QPainter 的文本矩形既是排版框也是裁剪框,给数字留够位数,给中文字留够上下伸量。
操作
| 按键 | 功能 |
|---|---|
| ← → | 左右移动 |
| ↑ | 顺时针旋转 |
| ↓ | 软降(加速下落) |
| 空格 | 硬降(直接落底) |
| P | 暂停 / 继续 |
| R | 重新开始 |
两种写法,一个裸 X11 一个 Qt5,代码量差不多,体验差在"你要不要自己管双缓冲和键盘映射"。工具选对,真的事半功倍。