将一段代码(动画一种矩形图案)从Java转换为Python之后,我注意到代码生成的动画看起来很奇怪。我设法用一个最小的例子重现问题如下:
import pygame
SIZE = 200
pygame.init()
DISPLAYSURF = pygame.display.set_mode((SIZE, SIZE))
D = 70.9
xT = 0.3
yT = 0
#pygame.draw.rect(DISPLAYSURF, (255,0,0), (0, 0, SIZE, SIZE))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (xT, yT, D, D))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (xT+D, yT+D, D, D))
pygame.draw.rect(DISPLAYSURF, (0,0,0), (xT, yT+D, D, D))
pygame.draw.rect(DISPLAYSURF, (0,0,0), (xT+D, yT, D, D))
pygame.display.update()
此代码生成以下图像:
请注意,正方形在中间没有完美排列。取消注释上面代码中的注释行会产生以下图像,这可以进一步说明问题:
似乎在黑白图案中存在像素宽的间隙,即使在代码中可以看到(通过调用pygame.draw.rect()中传递的数据),这不应该不是这样的。这种行为的原因是什么,我该如何解决?
(这不是在Java中发生的,here是与上面的Python代码相对应的一段Java代码。)
答案 0 :(得分:5)
在图像编辑器中查看渲染图片,可以确认像素距离:
扩展函数调用(即手动执行添加),可以看到绘制白色矩形的输入参数的格式为
pygame.draw.rect(DISPLAYSURF, (255,255,255), ( 0.3, 0, 70.9, 70.9))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (71.2, 70.9, 70.9, 70.9))
由于像素的分数在屏幕上没有意义,因此输入必须以某种方式离散化。 Pygame(或SDL,如问题评论中所述)似乎选择截断,这实际上将绘图命令转换为:
pygame.draw.rect(DISPLAYSURF, (255,255,255), ( 0, 0, 70, 70))
pygame.draw.rect(DISPLAYSURF, (255,255,255), (71, 70, 70, 70))
,它对应于渲染图像中的尺寸。如果AWT以不同的方式绘制它,我的猜测是它使用舍入(某种)而不是截断。这可以通过尝试不同的渲染输入或通过挖掘文档进行调查。
如果想要像素完美渲染,则使用浮点作为输入的定义不明确。如果保持整数,结果应该独立于渲染器。
编辑:如果其他人发现这一点,我会扩展一下,因为除了源代码之外,我找不到有关此行为的更多信息。
有问题的函数调用采用以下输入参数(documentation):
pygame.draw.rect(Surface, color, Rect, width=0)
其中Rect
是由左上角坐标,宽度和高度定义的特定对象。根据设计,它只处理整数属性,因为它意味着低级“这是你在屏幕上看到的”数据类型。数据类型通过截断来处理浮点数:
>>> import pygame
>>> r = pygame.Rect((1, 1, 8, 12))
>>> r.bottomright
(9, 13)
>>> r.bottomright = (9.9, 13.5)
>>> r.bottomright
(9, 13)
>>> r.bottomright = (11.9, 13.5)
>>> r.bottomright
(11, 13)
即,已完成常规(int)
演员表。
Rect
对象不是指“存储我的精灵的坐标”对象,而是“这是屏幕将代表的对象”对象。浮点对于前一个目的肯定是有用的,设计者可能希望保留一个内部浮动列表来存储这些信息。否则,通过例如增加屏幕位置。 r.left += 0.8
(其中r
是Rect
个对象)永远不会移动r
。
问题中的问题来自(非常合理地)假设矩形的右x
坐标至少会被计算为x₂ = int(x₁ + width)
,但由于函数调用隐式转换输入在继续之前将元组转换为Rect
对象,并且由于Rect
将截断其输入参数,因此它将计算为x₂ = int(x₁) + int(width)
,这对于float输入并不总是相同。
要使用舍入规则创建Rect
,可以使用例如定义一个包装器,如:
def rect_round(x1, y1, w, h):
"""Returns pygame.Rect object after applying sane rounding rules.
Args:
x1, y1, w, h:
(x1, y1) is the top-left coordinate of the rectangle,
w is width,
h is height.
Returns:
pygame.Rect object.
"""
r_x1 = round(x1)
r_y1 = round(y1)
r_w = round(x1 - r_x1 + w)
r_h = round(y1 - r_y1 + h)
return pygame.Rect(map(int, (r_x1, r_y1, r_w, r_h)))
(或针对其他舍入规则进行修改)然后调用draw函数,例如。
pygame.draw.rect(DISPLAYSURF, (255,255,255), rect_round(71.2, 70.9, 70.9, 70.9))
但是,永远不会绕过这样一个事实:按照定义,像素是屏幕上最小的可寻址单元,所以这个解决方案也可能有它的怪癖。
2005年Pygame邮件列表上的相关主题:Suggestion: make Rect use float coordinates