2

I have created a sketch code which should move the ball with acceleration after pressing key. The ball would be accelerated in direction of the keyboard arrow, then rebound from active edge of the window. Here is the code:

import pygame, sys
pygame.init()


def main():

    clock = pygame.time.Clock()
    
    pygame.display.set_caption('BasketBall')
    icon = pygame.image.load('/home/michael/game/ballgame.png')
    pygame.display.set_icon(icon)
    
    pygame.mixer.music.load(r'/home/michael/game/soundtrack.mp3')
    pygame.mixer.music.play(-1)
    
    size = width, height = 800, 600
    screen = pygame.display.set_mode(size)
    
    speed = [0, 0]
    accel = [0.1, 0.1]

    
    image = pygame.image.load(r'/home/michael/game/background.png')
    image = pygame.transform.scale(image, size)
    
    surf_center = (
        (width-image.get_width())/2,
        (height-image.get_height())/2
    )
    
    screen.blit(image, surf_center)
    ball = pygame.image.load('/home/michael/game/ball.png')
    ball = pygame.transform.scale(ball, (ball.get_width()//10, ball.get_height()//10))

    screen.blit(ball, (width/10, height/10))
    ballrect = ball.get_rect(center=(width/2, height/2))
    pygame.display.flip()


    while True:
        clock.tick(60)
        pygame.time.delay(50)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: sys.exit()
        keys = pygame.key.get_pressed()
        if keys[pygame.K_ESCAPE]: sys.exit()
        if keys[pygame.K_UP]:

            ballrect = ballrect.move([0,-50])
        elif keys[pygame.K_DOWN]:
            ballrect = ballrect.move([0,50])
        elif keys[pygame.K_LEFT]:
            ballrect = ballrect.move([-50,0])
        elif keys[pygame.K_RIGHT]:
            ballrect = ballrect.move([50,0])
        ballrect = ballrect.move(speed)
        if ballrect.left < 0 or ballrect.right > width:
            speed[0] = -speed[0]
        if ballrect.top < 0 or ballrect.bottom > height:
            speed[1] = -speed[1]

        
        screen.blit(image,surf_center)
        screen.blit(ball,ballrect)
        pygame.display.flip()


if __name__ == '__main__':
    main()
    pygame.quit()
    sys.exit()

It seems complicated, maybe because I'm still studying pygame lib, but I wanted to accelerate ball until the key is pressed. The ball should ideally elastic rebound from active window edges.

Malum Phobos
  • 115
  • 6
  • You are not using `speed` and `accel` anywhere in your code. For your desired output, you should increment `speed` upon input, instead of moving by offset. You set the speed depending on input, then later on the loop, you calculate position based on speed, then you move – Rodrigo Rodrigues Nov 06 '22 at 04:07
  • Related [How to fix character constantly accelerating in both directions after deceleration Pygame?](https://stackoverflow.com/questions/59832445/how-to-fix-character-constantly-accelerating-in-both-directions-after-decelerati/59846286#59846286) and [Acceleration and Deceleration in pygame](https://stackoverflow.com/questions/68333521/acceleration-and-deceleration-in-pygame/68333687#68333687). – Rabbid76 Nov 06 '22 at 06:54

1 Answers1

1

Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

The coordinates for Rect objects are all integers. [...]

If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object.

Create variables for the position, speed, acceleration the ball. Also add a variable for the friction (1 means no friction) and the maximum speed:

ball_rect = ball.get_rect(center = surf_center)
ball_pos = list(ball_rect.center)
speed = [0, 0]
accel = [0.1, 0.1]
max_speed = 10
friction = 0.99

When a button is pressed the speed changes depending on the acceleration and friction:

keys = pygame.key.get_pressed()
dx = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
dy = keys[pygame.K_DOWN] - keys[pygame.K_UP]
        
speed[0] = speed[0] * friction + accel[0] * dx
speed[1] = speed[1] * friction + accel[1] * dy

Limit the speed by the maximum speed:

speed[0] = max(-max_speed, min(max_speed, speed[0]))
speed[1] = max(-max_speed, min(max_speed, speed[1]))

Move the ball depending on the speed:

ball_pos[0] += speed[0]
ball_pos[1] += speed[1]
ball_rect.center = round(ball_pos[0]), round(ball_pos[1])

Be careful when bouncing the ball from the walls (see Sometimes the ball doesn't bounce off the paddle in pong game):

border_rect = screen.get_rect()
if not border_rect.contains(ball_rect):
    if ball_rect.left < 0 or ball_rect.right > width:
        speed[0] = -speed[0]
    if ball_rect.top < 0 or ball_rect.bottom > height:
        speed[1] = -speed[1]
    border_rect.clamp_ip(border_rect)
    ball_pos = list(ball_rect.center)

Complete application loop:

def main():
    # [...]
    
    ball_rect = ball.get_rect(center = surf_center)
    ball_pos = list(ball_rect.center)
    speed = [0, 0]
    accel = [0.1, 0.1]
    max_speed = 10
    friction = 0.99

    run = True
    while run:
        clock.tick(60)
        pygame.time.delay(50)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: 
                run = False

        keys = pygame.key.get_pressed()
        dx = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
        dy = keys[pygame.K_DOWN] - keys[pygame.K_UP]
        
        speed[0] = speed[0] * friction + accel[0] * dx
        speed[1] = speed[1] * friction + accel[1] * dy
        speed[0] = max(-max_speed, min(max_speed, speed[0]))
        speed[1] = max(-max_speed, min(max_speed, speed[1]))

        ball_pos[0] += speed[0]
        ball_pos[1] += speed[1]
        ball_rect.center = round(ball_pos[0]), round(ball_pos[1])
        
        border_rect = screen.get_rect()
        if not border_rect.contains(ball_rect):
            if ball_rect.left < 0 or ball_rect.right > width:
                speed[0] = -speed[0]
            if ball_rect.top < 0 or ball_rect.bottom > height:
                speed[1] = -speed[1]
            border_rect.clamp_ip(border_rect)
            ball_pos = list(ball_rect.center)

        screen.blit(image, (0, 0))
        screen.blit(ball, ball_rect)
        pygame.display.flip()

Minimal example:

import pygame, sys
pygame.init()

def main():
    clock = pygame.time.Clock()
    screen = pygame.display.set_mode((400, 400))
    image = pygame.Surface(screen.get_size())
    surf_center = screen.get_rect().center
    ball = pygame.Surface((30, 30), pygame.SRCALPHA)
    pygame.draw.circle(ball, "white", (15, 15), 15)
    
    ball_rect = ball.get_rect(center = surf_center)
    ball_pos = list(ball_rect.center)
    speed = [0, 0]
    accel = [0.5, 0.5]
    max_speed = 20
    friction = 0.99

    run = True
    while run:
        clock.tick(60)
        pygame.time.delay(50)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: 
                run = False
            if event == pygame.KEYDOWN  and event.key == pygame.K_ESCAPE:
                run = False

        keys = pygame.key.get_pressed()
        dx = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]
        dy = keys[pygame.K_DOWN] - keys[pygame.K_UP]
        
        speed[0] = speed[0] * friction + accel[0] * dx
        speed[1] = speed[1] * friction + accel[1] * dy
        speed[0] = max(-max_speed, min(max_speed, speed[0]))
        speed[1] = max(-max_speed, min(max_speed, speed[1]))

        ball_pos[0] += speed[0]
        ball_pos[1] += speed[1]
        ball_rect.center = round(ball_pos[0]), round(ball_pos[1])
        
        border_rect = screen.get_rect().inflate(-50, -50)
        if not border_rect.contains(ball_rect):
            if ball_rect.left < border_rect.left or ball_rect.right > border_rect.right:
                speed[0] = -speed[0]
            if ball_rect.top < border_rect.top or ball_rect.bottom > border_rect.bottom:
                speed[1] = -speed[1]
            border_rect.clamp_ip(border_rect)
            ball_pos = list(ball_rect.center)

        screen.blit(image, (0, 0))
        pygame.draw.rect(screen, "red", border_rect, 3)
        screen.blit(ball, ball_rect)
        pygame.display.flip()

if __name__ == '__main__':
    main()
    pygame.quit()
    sys.exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174