Puzzled by my sprite's unequal +/ - velocity

前端 未结 2 1749
暗喜
暗喜 2021-01-22 11:53

I have two sprites in my game. The zombie sprite works perfectly, moving in all directions at a velocity of 1.0. My player sprite however, despite moves more slowly in the posit

相关标签:
2条回答
  • 2021-01-22 12:20

    Problem

    The sprite group is drawing your sprite using the sprite's rect attribute. A pygame Rect object can only hold integers, so it'll truncate all floating point numbers.

    Let's say you have a x = 5.

    • If you add 1.1: x += 1.1 <=> x = x + 1.1 <=> x = 5 + 1.1 <=> x = 6.1 which will be truncated to x = 6. It have increased by 1.
    • If you subtract 1.1: x -= 1.1 <=> x = x - 1.1 <=> x = 5 - 1.1 <=> x = 3.9 which will be truncated to x = 3. It have decreased by 2.

    In other words: You'll move faster in the left direction than the right (the same principle applies to negative numbers). Here's an example demonstrating it:

    import pygame
    pygame.init()
    
    
    class Player(pygame.sprite.Sprite):
        def __init__(self, group):
            super(Player, self).__init__(group)
            self.image = pygame.Surface((32, 32))
            self.rect = self.image.get_rect()
    
    
    screen = pygame.display.set_mode((100, 100))
    group = pygame.sprite.Group()
    player = Player(group)
    clock = pygame.time.Clock()
    
    while True:
        clock.tick(10)
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    x = player.rect.x + 1.1
                    print("Actual x:", x)
                    player.rect.x = player.rect.x + 1.1
                    print("Truncated x:", player.rect.x)
                elif event.key == pygame.K_LEFT:
                    x = player.rect.x - 1.1
                    print("Actual x:", x)
                    player.rect.x = player.rect.x - 1.1
                    print("Truncated x:", player.rect.x)
    
        screen.fill((255, 255, 255))
        group.draw(screen)
        pygame.display.update()
    

    Solution

    Using floating point numbers for position is great; it makes it possible to move a sprite less than a pixel every frame (if your game updates 120 times per second and you want your sprite to move only 30 pixels per second).

    However, you have to compensate for the fact that the rect objects cannot hold them. The most straightforward solution is to have an attribute position which keep track of the position of the sprite in floating point precision. Then at every update change the rect to the position of the attribute. Like this:

    import pygame
    pygame.init()
    
    
    class Player(pygame.sprite.Sprite):
        def __init__(self, group):
            super(Player, self).__init__(group)
            self.image = pygame.Surface((32, 32))
            self.rect = self.image.get_rect()
            self.position = self.rect.x  # Or whatever point of the rect you want the position to be.
    
    
    screen = pygame.display.set_mode((100, 100))
    group = pygame.sprite.Group()
    player = Player(group)
    clock = pygame.time.Clock()
    
    while True:
        clock.tick(10)
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    player.position += 1.1
                    player.rect.x = player.position
                elif event.key == pygame.K_LEFT:
                    player.position -= 1.1
                    player.rect.x = player.position
    
        screen.fill((255, 255, 255))
        group.draw(screen)
        pygame.display.update()
    

    I've only showed how this movement works in the x-axis, but it's exactly the same on the y-axis.

    0 讨论(0)
  • 2021-01-22 12:22

    Ok. I think the problem is that the number of pixels that the sprite is moved each update is rounded down, so that 2.25 becomes 2 pixels and -2.25 becomes -3 pixels. Moving by a fractional number of pixels doesn't make sense I think. If you change lines 229 - 233 to

    else:
        # move player in that direction
        player.velocity = calc_velocity(player.direction, 2.0)
        player.velocity.x *= 2.0
        player.velocity.y *= 2.0
    

    The velocity is now an integer and there would be no rounding problems. It is faster though. Is there some reason why you don't just have the velocity as an integer instead of a float squared?

    0 讨论(0)
提交回复
热议问题