pythonpygamepygame-surfaceboids

Boids Algorithm. pygame.Rect shape not displayed properly in the window


I am trying to rewrite some code to take advantage of pygame sprites in Boids Algorithm implementation. Currently the goal is to create multiple sprites on screen (number determined by "flock" variable) and get them moving in random directions, avoiding the edges.

I have succeeded in doing so with a single sprite, as well as just using non-pygame circles. But now, instead of multiple moving rectangles, I seem to only get one.

I tried calling the redraw function and/or updating the surface in various different loops, assuming that the issue was in the order of execution. The results vary from only one well behaving sprite to none at all to a random tantrum of squares.

I assume the problem is in the way or order I append the objects to the parent array and drawing them.

def redraw():
  for boids in birds:
    boids.draw(win)
  pygame.display.update()

#Creating a parent array with all boids objects
birds = pygame.sprite.Group()
for i in range (flock):
  #Randomizing the starting position of the new boid within the margins before adding it to the array 
  posx = random.randint(l_margin, r_margin)
  posy = random.randint(t_margin, b_margin)
  #Creating the new boid and adding it to the array
  boids = boid(posx, posy, turnf, avel_x, avel_y, margins)
  birds.add(boids)

OR the problem may lie somewhere in the boid class that governs the sprite behaviour. Full code below.

import pygame
import random

pygame.init()
pygame.mixer.init()

#Screen size 
d_width = 400
d_height = 400
margins = 50

#Determinig the boundaries of the play area
l_margin = d_width - (d_width - margins)
r_margin = d_width - margins
t_margin = d_height - (d_height - margins)
b_margin = d_height - margins

win = pygame.display.set_mode((d_width, d_height))
pygame.display.set_caption("Boids Algorithm")
win.fill((0,0,0))

#Asigning a random integer between -6 and 6 for boid velocity
avel_x = random.randint(-6, 6)
avel_y = random.randint(-6, 6)
#Asigning values to the turn factor of the boid and number of boids in a flock
turnf = 2
flock = 10 

FPS = 10
clock = pygame.time.Clock()

#Class containing all properties of boids
class boid(pygame.sprite.Sprite):
  def __init__(self, x, y, turnfactor, vel_x, vel_y, margin):
    
    pygame.sprite.Sprite.__init__(self)
    self.image = pygame.Surface((10,10))
    self.image.fill((255,255,255))
    self.rect = pygame.Rect(x, y, 5,5)
    
    self.rect.x = x
    self.rect.y = y
    #self.rect.radius = radius
    self.turnfactor = turnfactor
    self.velx = vel_x
    self.vely = vel_y
    self.margin = margin
    
  def move (self):
    #Updating position for every frame 
    self.rect.x = self.rect.x + self.velx
    self.rect.y = self.rect.y + self.vely

    #Checking if the new position of the boid is past the margin. If true, aply a turn factor to velocity to start turning the boid
    if self.rect.x < l_margin:
     self.velx = self.velx + self.turnfactor
    elif self.rect.x > r_margin:
      self.velx = self.velx - self.turnfactor
    elif self.rect.y < t_margin:
      self.vely = self.vely + self.turnfactor
    elif self.rect.y > b_margin:
      self.vely = self.vely - self.turnfactor
    #A list with updated position 
    self.position = (self.rect.x, self.rect.y)
    
  def draw(self, win):
    
    #Filling the screen with black, obtaining new position for the boid from move() and redrawing at the new position
    win.fill((0,0,0))
    self.move()
    win.blit(self.image, self.position)
    
    #pygame.display.update()
    


def redraw():
  for boids in birds:
    boids.draw(win)
  pygame.display.update()

#Creating a parent array with all boids objects
birds = pygame.sprite.Group()
for i in range (flock):
  #Randomizing the starting position of the new boid within the margins before adding it to the array 
  posx = random.randint(l_margin, r_margin)
  posy = random.randint(t_margin, b_margin)
  #Creating the new boid and adding it to the array
  boids = boid(posx, posy, turnf, avel_x, avel_y, margins)
  birds.add(boids)

run = True
fly = True

while run:
  clock.tick(FPS)
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      run = False
  
  redraw()
     
pygame.quit()

Solution

  • You must delete the display only once, but not for each body. pygame.Surface.fill fills the entire display with a uniform color and hides everything on that surface that was drawn prior to this function.

    class boid(pygame.sprite.Sprite):
        # [...]
    
      def draw(self, win):
        
        #Filling the screen with black, obtaining new position for the boid from move() and redrawing at the new position
        # win.fill((0,0,0))                  <--- DELETE
        self.move()
        win.blit(self.image, self.position)
    
    def redraw():
      win.fill((0,0,0))                    # <--- INSERT
      for boids in birds:
        boids.draw(win)
      pygame.display.update()