上一篇是零依赖的裸 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,代码量差不多,体验差在"你要不要自己管双缓冲和键盘映射"。工具选对,真的事半功倍。