0
\$\begingroup\$

I'm having trouble with a PyGame animation I'm writting. I have a Ball class that defines an object that moves on the screen and bounces on the edges. I initialize the class with a direction attribute that tells the ball in which direction to move. However, I've noticed two problems:

  • The first one is that when setting the direction to Vector2(1, -0.1), the ball moves in the expected direction: up and to the right. However, if I set the direction to Vector2(1, 0.1), the ball just moves horizontally.
  • The second issue is that I'm moving the object doing ball.rect.center += ball.direction * dt * ball.speed, so ball.speed shouldn't change the direction of motion because it is a scalar. However, it does, and I can't figure out why.

I've noticed these two issues when I saw that the ball bounces correctly on all sides except on the top edge (when it reaches that point it just starts moving horizontally). This behaviour happens for a direction equal to Vector2(1, -0.1) and for a speed of 300, but not for other values (at least not that I've noticed).

Here is a minimal working example:

import pygame
from pygame.math import Vector2
import sys


class Ball:
    def __init__(self):
        self.image = pygame.Surface(size=(16, 16))
        self.image.fill("#FFFFFF")
        self.rect = self.image.get_rect()

        self.screen = pygame.display.get_surface()
        self.screen_width = self.screen.get_size()[0]
        self.screen_height = self.screen.get_size()[1]

        self.speed = 300.0
        self.rect.center = Vector2(0.5 * self.screen_width,
                                   0.5 * self.screen_height)
        self.direction = Vector2(1, -0.1)
        self.direction.normalize_ip()

    def render(self):
        self.screen.blit(self.image, self.rect)

    def move(self, dt: float):
        self.rect.topleft += self.direction * dt * self.speed

        # Bounce on the edges of the screen
        if self.rect.right > self.screen_width:
            self.direction.x *= -1
            self.rect.right -= 2 * (self.rect.right - self.screen_width)
        if self.rect.left < 0:
            self.direction.x *= -1
            self.rect.left -= 2 * (self.rect.left - 0)
        if self.rect.top < 0:
            self.direction.y *= -1
            self.rect.top -= 2 * (self.rect.top - 0)
        if self.rect.bottom > self.screen_height:
            self.direction.y *= -1
            self.rect.bottom -= 2 * (self.rect.bottom - self.screen_height)


class Animation:
    def __init__(self):
        pygame.init()

        self.resolution = (1920, 1080)
        self.pixel_depth = 32
        self.window_name =  "Bouncing Ball"
        self.fps = 50

        # Setup screen
        self.screen = pygame.display.set_mode(
            size=self.resolution,
            flags=pygame.FULLSCREEN,
            depth=self.pixel_depth)
        pygame.display.set_caption(self.window_name)

        self.clock = pygame.time.Clock()
        self.dt = 0

        # Initialize game objects
        self.ball = Ball()
        self.movement_range = (0, self.resolution[0])

    @staticmethod
    def _quit() -> None:
        """
        This method quits the animation and closes the window.
        """
        pygame.quit()
        sys.exit()

    def _check_passive_events(self) -> None:
        """
        Handle the events loop.
        """
        for event in pygame.event.get():
            # Quitting
            if event.type == pygame.QUIT:
                self._quit()
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                self._quit()

    def run(self):
        while True:  # Main loop

            # Events
            self._check_passive_events()

            # Game logic
            self.ball.move(self.dt)

            # Screen update
            self.screen.fill("#000000")
            self.ball.render()

            self.dt = self.clock.tick(self.fps) / 1000.0
            pygame.display.flip()


if __name__ == "__main__":
    animation = Animation()
    animation.run()
```
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Okay, so I found the answer in a post from another community. It turns out I was trying to move the Rectangle using distances that were below 1 and since PyGame only allows the use of ints in the position of a Rectangle, the change didn't do anything. More details can be found here.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .