Search code examples
pythonpygamespritecounting

Python count the number of sprites from a group on screen


Wonder if anyone could help. I am making a very simple pygame game. A number of balls fall from the top and can hit a player or be caught. A ball will disappear after a certain number of bounces

I want to be able to control the number of balls on screen at once. I can record when a ball 'dies' or is caught but not sure how to count the number present on screen.

I apologies if the code example is insufficient. I have created a ball sprite class which handles individual ball position and animation and a level class which handles creating and populated the ball sprite group and then has a method which calls the ball animation method for each ball in the group. Obviously this level method runs in the main 'while True' loop to allow the animations to play out.

Here is the level class:

class Level():
    
    def __init__(self, tot_balls = 20, max_onscreen = 5):
        
        self.tot_balls = tot_balls
        self.max_onscreen = max_onscreen
        self.ball_group = pygame.sprite.Group()
        self.current_onscreen = 0
    
        for _ in range (self.tot_balls):
            
            ball = Ball(random.randint(0, 800), random.randint(-50,50), random.randint(-1,1))
            self.ball_group.add(ball)
        

        
    def launch_balls(self):
        
        #while self.current_onscreen < self.max_onscreen:
            
        for ball in self.ball_group:
            ball.ball_animation()
            
            if ball.killed:
                self.current_onscreen -= 1

and the ball class:

class Ball(pygame.sprite.Sprite):

    def __init__(self, pos_x, pos_y, x_move=0):
        super().__init__()
        self.image = pygame.image.load('new_ball.png')
        self.rect = self.image.get_rect()
        self.rect.center = [pos_x, pos_y]
        self.bounces = 0
        self.gravity = 0
        self.x_move = x_move
        self.killed = False
        self.present = 1
        
    def ball_animation(self):
        
        self.gravity +=1
        self.rect.centery += self.gravity
        self.rect.centerx += self.x_move
        #print(f'Gravity is {self.gravity}. Bottom is {self.rect.bottom}')
        
        if self.rect.bottom >= 300 and self.bounces > 4:
            self.kill()
            self.present = 0
            self.killed = True
            
            
        elif self.rect.bottom > 300: 
            self.rect.bottom = 300
            
            self.bounces +=1
            
            self.gravity *= -1

At the moment, all the ball are launched and animate all at once. I will try to put a timer in, but I really want to limit the number of balls on screen at once (you can see the commented out condition for when the current_onscreen < max_balls, but when I had python print the current onscreen it was enormous as the loop runs at 60fps. I was wondering if there is a way to count the 'launch' of a sprite only once?


Solution

  • One option is to use a timer event. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. The time has to be set in milliseconds.
    For a timer event you need to define a unique user events id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event. Receive the event in the event loop:

    timer_interval = 1000 # 1.0 seconds
    timer_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer(timer_event_id, timer_interval)
    
    running = True
    while running:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
             elif event.type == timer_event_id:             
                 level.add_one_new_ball()
    
    class Level():
        
        def __init__(self, tot_balls = 20, max_onscreen = 5):
            
            self.tot_balls = tot_balls
            self.max_onscreen = max_onscreen
            self.ball_group = pygame.sprite.Group()
            self.current_onscreen = 0   
                   
        def add_one_new_ball(self):
            if len(self.ball_group) < self.tot_balls:
                ball = Ball(random.randint(0, 800), random.randint(-50,50), random.randint(-1,1))
                self.ball_group.add(ball)
    

    See also Spawning multiple instances of the same object concurrently in python