pythongraphicspygamesimulationboids

Problem with making Circle properly appear on screen in Pygame


I think my understanding of Pygame is a little bit weak. I would appreciate any help in general about the intricacies of the code (since this was given by the teacher) or simply how I can at least make the obstacle visible.

def draw(screen, background, boids, obstaclearray):
    
    #redrawing the whole window
    
    boids.clear(screen, background)
    dirty = boids.draw(screen)
    for element in obstaclearray:
        pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)

    pygame.display.update(dirty)

Above is where I actually do the drawing and attempt to draw the circle. The CircularObstacle class is a very simple class that looks like this:

import pygame
class CircularObstacle():
    def __init__(self, x, y, radius): #MAYBE ADD A SIZE 
        self.x = x
        self.y = y
        self.radius = radius

The problem is that the circle only draws itself when the boids have went over it, which is really weird. I think it has to do with the way the pygame has been setup with and the Surfaces and everything, so below is all the code in main. Of course the obstacle does not work as intended, but I plan to fix that later, first I want to at least get a circle to show. Below is my full code because I believe it is crucial to solving the issue:

import pygame
from pygame.locals import *
import argparse
import sys
from boid import Boid 
from Obstacle import CircularObstacle



def add_boids(boids,num_boids):
    for boid in range (num_boids):
        boids.add(Boid())
        
        


def update(dt, boids):
    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit(0)
        elif event.type == KEYDOWN:
            mods = pygame.key.get_mods()
            if event.key == pygame.K_q:
                # quit
                pygame.quit()
                sys.exit(0)
            elif event.key == pygame.K_UP:
                # add boids
                if mods & pygame.KMOD_SHIFT:
                    add_boids(boids, 100)
                else:
                    add_boids(boids, 10)
            elif event.key == pygame.K_DOWN:
                # remove boids
                if mods & pygame.KMOD_SHIFT:
                    boids.remove(boids.sprites()[:100])
                else:
                    boids.remove(boids.sprites()[:10])

    
    #ADD STUFF LIKE THE SLIDER AND STUFF
    
    for b in boids:
        b.update(dt, boids)
        

def draw(screen, background, boids, obstaclearray):
    
    #redrawing the whole window
    
    boids.clear(screen, background)
    dirty = boids.draw(screen)
    for element in obstaclearray:
        pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)

    pygame.display.update(dirty)
    
    
    
 
default_boids = 0
default_geometry = "1000x1000"

# Initialise pygame.
pygame.init()



pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP])

# keep a good framerate so the graphics are better
fps = 60.0
fpsClock = pygame.time.Clock()

# Set up pygamme window
window_width, window_height = 800,600
flags = DOUBLEBUF

screen = pygame.display.set_mode((window_width, window_height), flags)
screen.set_alpha(None)
background = pygame.Surface(screen.get_size()).convert()
background.fill(pygame.Color('black'))

boids = pygame.sprite.RenderUpdates()


add_boids(boids, default_boids)

obstaclearray = []
defaultcircleobstacle = CircularObstacle(200,200,13)
obstaclearray.append(defaultcircleobstacle)
#The "game loop"
dt = 1/fps  # here dt means the amount of time elapsed since the last frame 

#it seems like thie is a forever loop but in reality this is not since in the update method we provide functinality to quit the program
while True:
    update(dt, boids)
    draw(screen, background, boids, obstaclearray)
    dt = fpsClock.tick(fps)   

Solution

  • When you call pygame.display.update() you have 2 options. You can call it without any parameter. In this case the complete screen is updated.

    pygame.display.update()
    

    Or call it with a list of rectangular regions that need to be updated. In this case, only the rectangular areas will be updated.

    pygame.display.update(rect_list) 
    

    You do the 2nd option, but the areas where the circles are drawn are not in the dirty list, therefore this regions are not updated.

    pygame.display.update(dirty)
    

    Either update the whole screen with pygame.display.update() or add the regions of the circles to the dirty list:

    def draw(screen, background, boids, obstaclearray):
        
        boids.clear(screen, background)
        dirty = boids.draw(screen)
        for element in obstaclearray:
            dirty_rect = pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
            dirty.append(dirty_rect)
    
        pygame.display.update(dirty)