1
votes

I'm coding a game in pygame while learning OOP and the pygame.sprite.Sprite class, along with the attributes rect comes with, but for some reason, after coding this very simple code, the player doesn't move to the right, it only moves to the left, and without collision detection code, it stops moving once it's coordinates are equal to width. Why is that?

import pygame
import random

width = 800
height = 600

black = (0, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
red = (255, 0, 0)
white = (255, 255, 255)

pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Shmup")
clock = pygame.time.Clock()

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(green)
        self.rect = self.image.get_rect()
        self.rect.centerx = width/2
        self.rect.bottom = height - 10
        self.x_change = 0
        self.speedx = 0.2

    def change_left(self):
        self.x_change += -self.speedx

    def change_right(self):
        self.x_change += self.speedx

    def move_left(self):
        self.rect.x += self.x_change

    def move_right(self):
        self.rect.x += self.x_change

    def stop_move_left(self):
        self.x_change = 0

    def stop_move_right(self):
        self.x_change = 0


player = Player()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)

exitGame = False
while not exitGame:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exitGame = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_a:
                player.change_left()
            if event.key == pygame.K_d:
                player.change_right()
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_a:
                player.stop_move_left()
            if event.key == pygame.K_d:
                player.stop_move_right()

    player.move_left()
    player.move_right()
    screen.fill(white)
    all_sprites.draw(screen)
    pygame.display.update()

pygame.quit()
quit()
2

2 Answers

2
votes

The problem with your code is that you are only incrementing or decrementing player.rect.x by 0.2, and pygame stores positions as integers. If the player.rect.x value is set to 280 and is decremented by 0.2, it should become 279.8, but it actually becomes 279, because of the conversion to an integer! The value does indeed change when moving left, but when moving right, the value is incremented by 0.2, but this just gets rounded back down to the original value.

Note: because of how the int() function works in Python, -0.2 gets rounded to 0, so that is why the block cannot move past the window boundary, even though there isn't any explicit "collision detection" code being executed.

The quick and easy solution to this problem is to change the self.speedx value to 1 or greater:

self.speedx = 1

I hope this answer helped you, and if you have any further questions relating to this answer, please feel free to leave a comment below!

2
votes

If you actually want to use floating point numbers for the movement, you need to store the actual position as a separate attribute, change it first when the sprite moves and afterwards update the rect position as well (because the self.rect is used as the blit position).

I recommend storing the actual position and the velocity as pygame.math.Vector2 objects. Then you can just add the velocity to the position vector and set the rect.center (or one of the other rect attributes) to the new position.

import pygame
from pygame.math import Vector2

width = 800
height = 600

green = (0, 255, 0)
white = (255, 255, 255)

pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 40))
        self.image.fill(green)
        self.rect = self.image.get_rect(midbottom=(width/2, height-10))
        self.pos = Vector2(self.rect.center)
        self.velocity = Vector2(0, 0)

    def update(self):
        self.pos += self.velocity
        self.rect.center = self.pos


player = Player()
all_sprites = pygame.sprite.Group(player)

exitGame = False
while not exitGame:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            exitGame = True
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_a:
                player.velocity.x = -0.8
            elif event.key == pygame.K_d:
                player.velocity.x = 0.8
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_a:
                player.velocity.x = 0
            elif event.key == pygame.K_d:
                player.velocity.x = 0

    all_sprites.update()  # Calls the `update` methods of the sprites.

    screen.fill(white)
    all_sprites.draw(screen)
    pygame.display.update()
    clock.tick(60)

pygame.quit()