在pygame中使用精灵时,我最终意识到(由于差异最小),人们无法将精灵移动一小部分像素。虽然这是一个合乎逻辑的过程,但我得出的结论是,在不同的角度上无法达到相等的速度。
例如:
xmove = 5
ymove = -5
我定义了精灵ball
的两个坐标的移动增量,它们以45°的角度离开屏幕底部的平台。我希望这个方向相对于它将遇到的障碍而改变。
以下确定精灵与屏幕“墙”的碰撞:
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
sprite(平台)发生冲突后会发生什么:
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
这里的想法是让球根据撞击平台的位置以不同的角度反弹,同时保持相同的速度(每个刻度的行进距离)
所以这里的问题是为了补偿x坐标上一个像素的减少,我使用毕达哥拉斯定理来输出y坐标(-5.83)。由于pygame不会记录像素的分数,这实际上会显着降低球的速度。
所以我的问题是,如何解决这个问题(因为我不想限制在45°角度......)?
答案 0 :(得分:2)
您应该跟踪对象的“真实”位置,并且只在您在屏幕上绘制对象的那一刻。这可以防止舍入错误加起来。
另外,考虑使用矢量来跟踪物体的方向,因为它可以很容易地用角度改变方向并保持恒定的速度。
这是一个可以使用的示例类:
import math
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
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
字段)移动球。
答案 1 :(得分:1)
因为Pygame的Rect
用于处理像素,而像素基本上是不可分割的,所以您必须使用浮点数存储ball
的精确位置。 pygame.Rect
接受浮动,但只有存储整数楼层值。
考虑提供ball
个新float
属性ball.x
和ball.y
(如果尚未分配)保留精确(十进制)位置,然后应用{更新后的值为{1}}和xmove
。在每次移动之后(可能是同一更新的一部分),修改ymove
以使其ball.rect
与x和y值匹配,使用您最喜欢的舍入方法来topleft
值。您也可以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
以来-{-1}}不保存该地点及其属性。只能通过改变ball
和ball.x
来完成直线移动,但只要您不直接对y
采取行动(或致电ball.rect
这样做),它不重要,您将拥有pygame
的精确x和y位置。
如果你想要超精确,你甚至可以在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
时,您必须为其xy位置发送ball
值。 int
仍然可能是要走的路(因为它会在ball.rect.topleft
和ball.x
间接地间接提取这些值。
y
和xmove
不需要成为花车;只要操作中至少有一个元素是ymove
,结果也将是float
。根据球的速度和方向计算球的x和y位移后,您可以将这些浮点值发送到float
和ball.x
,它将保持帧之间的精度。