0

I am making pong in pygame, and I currently have both paddles and their movements done. I am trying to create the ball, but when the ball is rendered on the screen it is as a rectangle, not a circle. Here is my code:

import pygame

pygame.init()

from pygame.locals import (
K_UP, 
K_DOWN, 
K_s, 
K_w, 
QUIT
)

class Ball(pygame.sprite.Sprite): 
    def __init__(self, color, left, top): # circle is getting drawn in this constructor
        pygame.sprite.Sprite.__init__(self) 
        radius = 40 
        self.surface = pygame.Surface((radius, radius)) 
        self.surface.fill(color) 
        self.rect = pygame.draw.circle(self.surface, color, (left, top), radius)
        self.rect.update(left, top, radius, radius)

    # a temporary implementation of the move function that keeps the ball displayed on the screen 
    def move(self, screen): 
        screen.blit(self.surface, self.rect, self.rect)
        screen.blit(self.surface, self.rect)  


class Paddle(pygame.sprite.Sprite):
    # initializes the paddle surface, rect, pos_y
    def __init__(self, color, left, top): 
        pygame.sprite.Sprite.__init__(self) 
        width, height = 40, 120
        self.surface = pygame.Surface((width, height))
        self.surface.fill(color)
        self.rect = self.surface.get_rect()
        self.rect.update(left, top, width, height)
        self.pos_y = self.rect.top

    # updates the paddles position based on key presses 
    def move(self, pressed_keys, keys, screen):
        dy = 0 
        screen.blit(self.surface, self.rect, self.rect) 
        l_bound, u_bound = 5, 750 - self.rect.height - 5 
        if pressed_keys[keys[0]] and self.rect.top > l_bound: 
            dy = -.2  
            
        if pressed_keys[keys[1]] and self.rect.top < u_bound: 
            dy = .2

        self.pos_y += dy
        self.rect.top = self.pos_y  
        screen.blit(self.surface, self.rect) 

class Main: 
    def __init__(self):
        self.height, self.width = 750, 1000
        self.screen = pygame.display.set_mode((self.width, self.height)) 
        pygame.display.set_caption("pong") 
        self.loop()

    def loop(self): 
        blue = [0, 0, 255]
        red = [255, 0, 0]

        # create the paddles and the ball 
        paddle1 = Paddle(blue, 30, 30) 
        paddle2 = Paddle(blue, 930, 30)
        ball = Ball(red, self.width // 2, self.height // 2)
        
        # the buffer (I think) expands the area updated at the end of the game loop so that it can delete where the old rectangle was and change where the new rectangle is, but I'm not 100% sure 
        BUFFER = .01 

        running = True 
        while running:
            self.screen.fill([0, 0, 0]) 
            for event in pygame.event.get():
            if event.type == QUIT: 
                running = False 
        
            pressed_keys = pygame.key.get_pressed()
     
            paddle1.move(pressed_keys, [K_w, K_s], self.screen)
            paddle2.move(pressed_keys, [K_UP, K_DOWN], self.screen)
            ball.move(self.screen)
            update_rect1 = pygame.Rect(30, paddle1.rect.top - BUFFER, 40, paddle1.rect.top + 150 + BUFFER) 
            update_rect2 = pygame.Rect(930, paddle2.rect.top - BUFFER, 40, paddle2.rect.top + 150 + BUFFER)
            pygame.display.update([update_rect1, update_rect2, ball.rect])

        pygame.quit()


Main()

I've seen this approach to drawing circles online:

 pygame.draw.circle(screen, (0,0,255), (150, 50), 15, 1)

The problem with that is I need an intermediary surface to use when I blit onto the screen, such as with this line of code when I blit the paddle, in this case that surface is self.surface:

screen.blit(self.surface, self.rect) 

Because of this, I have to draw the circle on the Ball's surface field as such:

pygame.draw.circle(self.surface, color, (left, top), radius)

and then I can blit self.surface onto the screen. Something in this approach is causing an issue.

In summary, the approach I'm trying to take is to give each game object its own surface, but that (presumably) is causing problems for trying to draw a circle. I'd love some feedback on whether or not this approach is wrong and how to fix the circle problem. Thanks!

joshblech
  • 27
  • 8

1 Answers1

0

Here is the problem code:

self.surface = pygame.Surface((radius, radius)) 
self.surface.fill(color) 
self.rect = pygame.draw.circle(self.surface, color, (left, top), radius)

You are creating a (square) surface and filling it with the color of the circle, and then drawing the circle on top of it, in the same color. You should make the surface, that is the background, transparent. The best way is with set_colorkey.

Another problem is this line:

self.rect = pygame.draw.circle(self.surface, color, (left, top), radius)

You are drawing this on the self.surface, not on the screen, so you want the center argument to be the center of self.surface, not the center of the screen. Also, since the sides of the surface are radius, the circle will actually take up the entire surface. You were probably thinking of the diameter. You should double the radius when creating self.surface, but here I'll just halve it for the circle:

self.rect = pygame.draw.circle(self.surface, color, ((radius/2),(radius/2)), radius/2)

In summary, this should work:

self.surface = pygame.Surface((radius, radius))
self.surface.set_colorkey((255, 0, 255)) # set it to some color you are not using
self.surface.fill((255, 0, 255)) # fill the surface with that color
self.rect = pygame.draw.circle(self.surface, color, ((radius/2),(radius/2)), radius/2) # draw the circle in the correct color

When you blit this surface onto another, the color passed to set_colorkey will not be included, the circle's "background" will be transparent.

Armadillan
  • 530
  • 3
  • 15
  • This didn't quite work. When I swapped in your code the ball disappeared. When I commented out "self.surface.set_colorkey((255, 0, 255))" I saw a square of the color (255, 0, 255) with no circle in the center. It seems like the circle isn't being drawn at all somehow. – joshblech Apr 11 '21 at 03:59
  • @joshblech Yes, I noticed this. Sorry, I posted my initial answer without testing it. I edited my answer, and tested it, it works now. The problem was that the circle was being drawn to the wrong position, outside of the surface (screen coordinates instead of surface coordinates). – Armadillan Apr 11 '21 at 04:13