Pytris (Python, Aditya Adiraju, 2021)

来自俄罗斯方块中文维基
Pytris
Pytris (Python, Aditya Adiraju, 2021) boxart.png
开发 Aditya Adiraju
游戏平台 Python(Pygame)
发行时间 2021年4月4日
游戏信息
预览块数 0
场地大小 10 × 24
暂存(可在任意节点时刻发动)
硬降(但有瞬降
旋转系统 专用
[[文件:|125px]]
Pytris (Python, Aditya Adiraju, 2021) ingame.png

Pytris 是一款 Python 四连方块游戏。
该游戏有很多方块运动方面的漏洞,要尽量在开阔处操作才能正常游戏。

玩法

得分 = 总消行数 × 10。
重力恒定,方块每隔 0.1 秒自动下落一行。
死亡判定:重叠死亡
触发死亡时,游戏程序终止。

操作

左右键横移,上键逆时针旋转,空格键瞬降,H 暂存。
这五个操作可以叠加长按,而且有以下四个特性:

  • 叠加状态可跨块(也就可以实现 IRS
  • 暂存可在任意节点时刻发动(可被滥用),但插在旋转长按之后会失效
  • 左右键叠加(或顶牢彩色砖格长按横移)可使方块不锁定
  • 横移和瞬降叠加长按时,方块只会在低于天花板至少 4 行的边区瞬降

这个游戏每 0.1 秒取一次判定变化的节点时刻,各节点之间没有中间状态。
其关键参数在 main.py 第 214 行的 clock.tick() 当中,默认为 10。
每个节点时刻都伴随一次重力处理,所以方块不可能抵达场地的两个顶角位置。
而且,如果单点操作的压键时间恰好都不在节点时刻上,单点操作就会失败。

方块环境配置

基础数据设定

七种方块意在使用 SRS 配色,但 I 配成了淡水绿色(173, 216, 255)。

Tet.pngTet.pngTet.pngTet.png22Tet.pngTet.pngTet.pngTet.png
Tet.pngTet.pngTet.pngTet.png22Tet.pngTet.pngTet.pngTet.png
Tet.pngTet.pngTet.pngTet.png33Tet.pngTet.pngTet.pngTet.png
Tet.pngTet.pngTet.pngTet.png4Tet.pngTet.pngTet.pngTet.pngTet.png
OO
OO
AqgTet.png
AqgTet.png
AqgTet.png
AqgTet.png
Tet.pngJ
Tet.pngJ
JJ
LTet.png
LTet.png
LL
TTet.png
TT
TTet.png
STet.png
SS
Tet.pngS
Tet.pngZ
ZZ
ZTet.png

各方块的入场位置如上图所示。
其中,数字 4 的坐标为 (5,21)。

游戏中的七种方块都以“位宽高心色”五元数据表示,不设碰撞箱信息
位(self.location):方块身上的四个格子所对应的四个坐标联合组成的列表数据。
宽和高(self.width、self.height):方块在 x 和 y 方向上的最大展幅数值。
心(self.center):先按入场位安排、后由具体变化算得的单一坐标列表数据。[注 1]
色(self.color):RGB 元组数据。

变心和碰撞

变心(reset_center)指心坐标值的变化方法:

  • 时刻检查方块身上四个 x 坐标当中的极大和极小值(max_x、min_x),一旦发生变动,数据立刻刷新
  • 心[x] = (min_x + max_x + 1) / 2
  • 心[y] 也是用这个式子计算的,但它不在计算前刷新 max_y 和 min_y 的数据。

碰撞(collide)指方块目标位置与方块堆的重叠情况。
关键代码:if player.grid[i[0]][i[1]] != (0, 0, 0) and player.grid[i[0]][i[1]] != (255, 255, 255)
这一段代码是在按目标位置的格子颜色判断碰撞处理结果,如果不是黑或白(而是彩色的方块堆),碰撞就成立。
黑色格子代表空格(包括场外部分),场内部分显示为灰色是因为有一张“bg.png”灰格背景图叠了上去。
白色格子代表瞬降出来的假阴影块,在 main.py 第 60 行最后改成 ghost=True 时开启,其实质也是空格。
以旋转碰撞为例,方块旋转和场外格子碰撞时旋转失败、静止不动,换成和方块堆碰撞则会不正常地变形和穿越。

旋转的原理

Pytris (Python, Aditya Adiraju, 2021) 方块旋转的逻辑顺序如下:

  1. 对调宽和高的数值
  2. 旧位[yx] - 心[yx] = 临时位[yx]
  3. 新位 = [int(心[y] - 临时位[x]), int(心[x] + 临时位[y])]
  4. 判断碰撞情况
    1. 如果碰撞成立,前三步反序退回
    2. 如果碰撞不成立,就执行变心,心坐标减宽/高的一半,结果四舍五入

方块在实际游戏中旋转不正常的问题出在最后这两步上面:
第五步的退回次序按常理应是 321 却被写成了 231,而且 x 变成被减了两次心[x],返回了错误的宽高和坐标,方块会变形和穿越;
第六步的算式按两个极值为心[yx]取好了平均却继续按方块展幅缩减,心[x]每次计算都会有所亏损,所以方块越转越偏左。[注 2]

最后,这个游戏在 pygame 的按键事件中为“上键”(旋转)添加了三个条件来保证方块不会旋转出界:

  • 心[y] + 半宽 < 24(高于场底)
  • 心[x] + 半高 < 10(在场地右侧墙壁的左边)
  • 心[x] - 半高 ≥ 0(在场地左侧墙壁的右边)

注释

  1. 纵坐标优先表示,也就是 (y,x)。
    坐标的具体数据向左上对齐,它们遵循 pygame 模块的坐标系默认设置(第一象限在原点右下方)。
    入场时,I 的心位于中间两格连接线的中点,O 的心就在最中间,其他五种方块的心位于其 3×2 范围的几何重心。
  2. O 块是例外,它每次旋转的计算结果都是下偏一格,再加上节点时刻的重力处理,就相当于每次旋转下移两格。

外链