如何处理 pygame 中的像素?
How do I deal with pixels in pygame?
在 pygame 中使用 sprite 时,我最终意识到(由于差异很小)不能将 sprite 移动一个像素的一小部分。尽管这是一个合乎逻辑的过程,但我得出的结论是,在不同角度下无法获得相同的速度。
例如:
xmove = 5
ymove = -5
我定义了精灵 ball
的两个坐标的移动增量,该精灵离开屏幕底部的平台以 45° 角移动。我希望这个方向相对于它会遇到的障碍而改变。
下面判断精灵与屏幕"walls"的碰撞:
if ball.rect.left<=0:
xmove = 5
elif ball.rect.right>=700:
xmove = -5
elif ball.rect.top<=0:
ymove = 5
elif ball.rect.bottom>=400:
ymove = -5
这里有一些代码决定在检测到与 player
精灵(平台)发生碰撞后会发生什么:
if pygame.sprite.spritecollide(ball, player_sprite, False):# and ball.rect.bottom>=player.rect.top:
ymove=-5
if ball.rect.bottomright [0] in range (player.rect.topleft [0], player.rect.topleft [0] + 15):
xmove = -5
elif ball.rect.bottomleft [0] in range (player.rect.topright [0] - 14, player.rect.topright [0] + 1):
xmove = +5
elif ball.rect.bottomright [0] in range (player.rect.topleft [0] + 15, player.rect.topleft [0] + 26):
xmove = -4
ymove = -5.83
elif ball.rect.bottomleft [0] in range (player.rect.topright [0] - 26, player.rect.topright [0] - 15):
xmove = +4
ymove = -5.83
The idea here is to have the ball bounce at different angles based on where it hits the platform, all while keeping the same velocity (distance traveled per tick)
所以这里的问题是为了补偿x坐标上一个像素的减少我使用勾股定理输出y坐标(-5.83)。由于 pygame 没有记录像素的分数,这实际上会显着降低球的速度。
所以我的问题是,我该如何解决这个问题(因为我不想被限制在 45° 角...)?
您应该跟踪对象的 "real" 位置,并且仅在您在屏幕上绘制对象的那一刻左右。这可以防止舍入误差累加。
另外,考虑使用矢量来跟踪对象的方向,因为它可以很容易地随角度改变方向并保持恒定速度。
这里有一个小例子 class 你可以使用:
import math
# some simple vector helper functions, stolen from
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
class Ball(object):
def __init__(self):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self._direction = (1, 0)
# the "real" position of the object
@property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
@property
def int_pos(self):
return map(int, self.pos)
def set_dir(self, direction):
self._direction = normalize(direction)
def set_dir_d(self, degrees):
self.set_dir_r(math.radians(degrees))
def set_dir_r(self, radians):
self._direction = (math.cos(radians), math.sin(radians))
def update(self):
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in self._direction]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(screen, self.color, self.int_pos, 4)
这样,您可以通过调用 set_dir
(提供向量)、set_dir_d
(以度为单位提供所需的方向)或 set_dir_r
(以弧度为单位提供所需的方向)。
调用 update
将根据当前方向以恒定速度(speed
字段)移动球。
因为 Pygame 的 Rect
是用来处理像素的,而像素基本上是不可分割的,所以你必须使用浮点数来存储 ball
的精确位置。 pygame.Rect
将 接受 浮动,但仅 存储 整数下限值。
考虑提供 ball
新的 float
属性 ball.x
和 ball.y
(如果尚未分配)以保持精确(十进制)位置,然后应用您的 xmove
和 ymove
值更新为那些值。在每次移动后(可能是同一更新的一部分),修改 ball.rect
使其 topleft
匹配 x 和 y 值,使用您最喜欢的舍入方法给出 int
值。您还可以将 ball.rect
设为 property
,它会在每次调用时生成一个新的 Rect
。考虑:
class MyBall:
@property
def rect(self):
return pygame.Rect(round(self.x), round(self.y), *self.size)
def __init__(self, startingLoc, size, *args, **kwargs):
self.x = float(startingLoc[0])
self.y = float(startingLoc[1])
self.size = size # Size does not need to be floats, but can be.
# ...
请注意,如果您将 ball.rect
设为 property
,就地 pygame.Rect
操作 将不起作用, 因为 ball.rect
是现场生成的,它的属性没有被ball
保存。矩形移动只能通过改变 ball.x
和 y
来完成,但只要你不直接对 ball.rect
采取行动(或调用 pygame
这样做),没关系,您将获得 ball
.
的精确 x 和 y 位置
如果你想要超级精确,你甚至可以在 player
-ball
碰撞检测中使用浮点数,方法是将 if ball.rect.bottomright [0] in range (player.rect.topleft [0], player.rect.topleft [0] + 15):
替换为 if player.left <= ball.x <= player.left + 15:
之类的东西(等等)。请注意,range(..)
不能可靠地处理 float
值,因为该范围仅包含整数。
当您 blit
ball
时,您必须为其 xy 位置发送 int
值。 ball.rect.topleft
仍然可能是要走的路(因为它间接地从 ball.x
和 y
现场提取这些值)。
xmove
和 ymove
不需要 是浮点数;只要操作中至少有一个元素是 float
,结果也将是 float
。根据球的速度和方向计算出球的x和y位移后,你可以将这些float值发送到ball.x
和y
,它会保留帧之间的精度。
在 pygame 中使用 sprite 时,我最终意识到(由于差异很小)不能将 sprite 移动一个像素的一小部分。尽管这是一个合乎逻辑的过程,但我得出的结论是,在不同角度下无法获得相同的速度。
例如:
xmove = 5
ymove = -5
我定义了精灵 ball
的两个坐标的移动增量,该精灵离开屏幕底部的平台以 45° 角移动。我希望这个方向相对于它会遇到的障碍而改变。
下面判断精灵与屏幕"walls"的碰撞:
if ball.rect.left<=0:
xmove = 5
elif ball.rect.right>=700:
xmove = -5
elif ball.rect.top<=0:
ymove = 5
elif ball.rect.bottom>=400:
ymove = -5
这里有一些代码决定在检测到与 player
精灵(平台)发生碰撞后会发生什么:
if pygame.sprite.spritecollide(ball, player_sprite, False):# and ball.rect.bottom>=player.rect.top:
ymove=-5
if ball.rect.bottomright [0] in range (player.rect.topleft [0], player.rect.topleft [0] + 15):
xmove = -5
elif ball.rect.bottomleft [0] in range (player.rect.topright [0] - 14, player.rect.topright [0] + 1):
xmove = +5
elif ball.rect.bottomright [0] in range (player.rect.topleft [0] + 15, player.rect.topleft [0] + 26):
xmove = -4
ymove = -5.83
elif ball.rect.bottomleft [0] in range (player.rect.topright [0] - 26, player.rect.topright [0] - 15):
xmove = +4
ymove = -5.83
The idea here is to have the ball bounce at different angles based on where it hits the platform, all while keeping the same velocity (distance traveled per tick)
所以这里的问题是为了补偿x坐标上一个像素的减少我使用勾股定理输出y坐标(-5.83)。由于 pygame 没有记录像素的分数,这实际上会显着降低球的速度。
所以我的问题是,我该如何解决这个问题(因为我不想被限制在 45° 角...)?
您应该跟踪对象的 "real" 位置,并且仅在您在屏幕上绘制对象的那一刻左右。这可以防止舍入误差累加。
另外,考虑使用矢量来跟踪对象的方向,因为它可以很容易地随角度改变方向并保持恒定速度。
这里有一个小例子 class 你可以使用:
import math
# some simple vector helper functions, stolen from
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
class Ball(object):
def __init__(self):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self._direction = (1, 0)
# the "real" position of the object
@property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
@property
def int_pos(self):
return map(int, self.pos)
def set_dir(self, direction):
self._direction = normalize(direction)
def set_dir_d(self, degrees):
self.set_dir_r(math.radians(degrees))
def set_dir_r(self, radians):
self._direction = (math.cos(radians), math.sin(radians))
def update(self):
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in self._direction]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(screen, self.color, self.int_pos, 4)
这样,您可以通过调用 set_dir
(提供向量)、set_dir_d
(以度为单位提供所需的方向)或 set_dir_r
(以弧度为单位提供所需的方向)。
调用 update
将根据当前方向以恒定速度(speed
字段)移动球。
因为 Pygame 的 Rect
是用来处理像素的,而像素基本上是不可分割的,所以你必须使用浮点数来存储 ball
的精确位置。 pygame.Rect
将 接受 浮动,但仅 存储 整数下限值。
考虑提供 ball
新的 float
属性 ball.x
和 ball.y
(如果尚未分配)以保持精确(十进制)位置,然后应用您的 xmove
和 ymove
值更新为那些值。在每次移动后(可能是同一更新的一部分),修改 ball.rect
使其 topleft
匹配 x 和 y 值,使用您最喜欢的舍入方法给出 int
值。您还可以将 ball.rect
设为 property
,它会在每次调用时生成一个新的 Rect
。考虑:
class MyBall:
@property
def rect(self):
return pygame.Rect(round(self.x), round(self.y), *self.size)
def __init__(self, startingLoc, size, *args, **kwargs):
self.x = float(startingLoc[0])
self.y = float(startingLoc[1])
self.size = size # Size does not need to be floats, but can be.
# ...
请注意,如果您将 ball.rect
设为 property
,就地 pygame.Rect
操作 将不起作用, 因为 ball.rect
是现场生成的,它的属性没有被ball
保存。矩形移动只能通过改变 ball.x
和 y
来完成,但只要你不直接对 ball.rect
采取行动(或调用 pygame
这样做),没关系,您将获得 ball
.
如果你想要超级精确,你甚至可以在 player
-ball
碰撞检测中使用浮点数,方法是将 if ball.rect.bottomright [0] in range (player.rect.topleft [0], player.rect.topleft [0] + 15):
替换为 if player.left <= ball.x <= player.left + 15:
之类的东西(等等)。请注意,range(..)
不能可靠地处理 float
值,因为该范围仅包含整数。
当您 blit
ball
时,您必须为其 xy 位置发送 int
值。 ball.rect.topleft
仍然可能是要走的路(因为它间接地从 ball.x
和 y
现场提取这些值)。
xmove
和 ymove
不需要 是浮点数;只要操作中至少有一个元素是 float
,结果也将是 float
。根据球的速度和方向计算出球的x和y位移后,你可以将这些float值发送到ball.x
和y
,它会保留帧之间的精度。