矩阵旋转应提取非零坐标后按newRow=col、newCol=shape.length-1-row变换,旋转后重算包围盒;碰撞检测需将相对坐标转为绝对坐标再判断;渲染要倒序遍历并统一清屏方式;主循环须用固定时间步长避免卡顿。

矩阵旋转怎么写才不翻车
二维数组顺时针旋转 90° 的本质是「转置 + 每行反转」,但直接套公式容易在方块坐标映射时出错——因为俄罗斯方块的 shape 矩阵通常不是满阵(比如 T 块是 3×3,但只有 4 个格子有效),硬转会把空位也当实体处理。
- 先提取所有非零坐标(
row,col),再对每个点做数学变换:newRow = col,newCol = shape.length - 1 - row(假设原矩阵为shape.length × shape[0].length) - 别用
Arrays.deepCopy复制整个二维数组去旋转——它复制了所有0,后续碰撞检测会误判边界 - 旋转后必须重新计算包围矩形(bounding box),否则
drop或move时会越界:遍历新坐标集,取minRow/maxRow/minCol/maxCol
碰撞检测为什么老是漏判或误判
核心问题不在逻辑,而在「检测时机」和「坐标基准」。常见错误是拿旋转后的绝对坐标直接跟游戏板(board)比,却忘了 board 的 (0,0) 是左上角,而方块的 (0,0) 是其包围盒左上角,两者偏移量没对齐。
- 检测前必须把方块所有相对坐标(相对于自身包围盒左上角)加上当前落点
currentX/currentY,得到真实世界坐标 - 只检测「非零格子」的真实坐标是否超出
board范围,或对应位置是否已有方块:if (x = board[0].length || y >= board.length || (y >= 0 && board[y][x] != 0)) - 不要在
rotate()里做碰撞检测——旋转本身不改变位置,检测应放在tryMove()或tryRotate()这类意图明确的操作入口
控制台渲染时方块总“抖”或错位
根本原因是输出时没对齐坐标系:控制台行号从上到下递增,但数组索引 y 也是从上到下递增,看似一致,可一旦方块 y 值变大(往下掉),你却按 board[y] 直接打印,就等于把底部行打在了屏幕下方——人眼看到的是“向上跳”。
- 渲染循环要倒着遍历
board:从board.length-1到0,这样y=0的行才真正在最上面显示 - 每行末尾加
\r\n(Windows)或\n(Unix),别依赖System.out.println()隐式换行——它可能触发缓冲区刷新延迟,造成闪烁 - 清屏别用
\033[2J\033[H这种 ANSI 码:Windows 默认终端不支持,改用Runtime.getRuntime().exec("cls")(Win)或"clear"(Mac/Linux),并包try-catch
为什么一加速就卡死或跳帧
主循环里用 Thread.sleep() 控速,但没考虑逻辑耗时——如果碰撞检测+渲染+输入处理花了 80ms,再睡 20ms,实际帧间隔就是 100ms,远超预期的 50ms(20fps)。更糟的是,sleep 不精准,尤其在 Windows 上误差常达 10–15ms。
立即学习“Java免费学习笔记(深入)”;
- 改用固定时间步长(fixed timestep):记录上一帧时间戳,每帧开头算差值,累计够 50ms 才执行一次游戏逻辑,不足则只渲染(插值可选,控制台版建议跳过)
- 键盘输入别用
Scanner——它阻塞等待回车;改用System.in.available()配合System.in.read()非阻塞读,或者直接用jline3库处理方向键 - 避免在主循环里 new 对象:把
nextPiece、临时坐标列表等做成复用对象,防止频繁 GC 导致卡顿
旋转算法和碰撞检测本身不难,难的是所有坐标系必须统一参考点,且每一步操作都要明确「这是相对坐标还是绝对坐标」「这个值会被谁读、以什么方式读」——漏掉一个隐式转换,整盘就飘了。










