The code below draws a black background, a white box and text. However it seems that the drawing order is not determined and I sometimes have the text or the rectangle covered by the background rectangle. I'm trying to understand what the appropriate way to manage this is?
import sdl2.ext
import sdl2.sdlttf
from sdl2 import SDL_Color, SDL_Init
WHITE = SDL_Color(255, 255, 255)
class Entity(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0):
self.sprite = sprite
self.sprite.position = posx, posy
sdl2.ext.init()
window = sdl2.ext.Window("PySDL2", size=(320, 240))
window.show()
world = sdl2.ext.World()
texture_renderer = sdl2.ext.Renderer(window)
spriterenderer = sdl2.ext.TextureSpriteRenderSystem(texture_renderer)
factory = sdl2.ext.SpriteFactory(sdl2.ext.TEXTURE, renderer=texture_renderer)
world.add_system(spriterenderer)
sdl2.sdlttf.TTF_Init()
font = sdl2.sdlttf.TTF_OpenFont('resources/Helvetica.dfont',32)
text_surface = sdl2.sdlttf.TTF_RenderText_Blended(font, 'test', WHITE).contents
sdl2.sdlttf.TTF_CloseFont(font)
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
Entity(world, c, 100, 100)
text_sprite = factory.from_surface(text_surface)
text_entity = Entity(world, text_sprite, 50, 50)
def run():
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
world.process()
run()
sdl2.ext.quit()
The default implementation of the TextureSpriteRenderSystem uses the depth attribute of Sprite object to determine the drawing order. If all sprites feature the same depth, the drawing order of them is undetermined, which results in your unwanted behaviour.
A Sprite with a depth value of 0 will be drawn below (or prior to) a Sprite with a higher depth value, e.g. 10. The default depth for each Sprite being created is 0. You could set your background's and rectangle's depth to a negative value in order to enforce the drawing order:
bg = factory.from_color(sdl2.ext.Color(0, 0, 0), size = (320,240))
bg.depth = -99 # always below everything
Entity(world, bg, 0, 0)
c = factory.from_color(sdl2.ext.Color(255, 255, 255), size = (50,50))
c.depth = -1
Entity(world, c, 100, 100)
You can read more about it in the PySDL2 sprite documentation.
Related
I'm making a game in python with pygame.
I've made a parent class Items with 4 children.
As far as I can see, they should all be moving at speed 'dx' as defined in the Items class.
However, when I run the program, some of them are much faster than others. It seems to be inconsistent as to which colour items are faster, too.
import pygame
from random import randint
WIDTH, HEIGHT = 800, 600
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))
FPS = 60
clock = pygame.time.Clock()
gravity = 15
# Colours
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
CYAN = (0, 255, 255)
def draw_window():
WINDOW.fill(WHITE)
for item in items:
item.draw_item()
pygame.display.update()
def main():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
clock.tick(FPS)
for item in items:
item.movement()
draw_window()
pygame.quit()
class Item:
def __init__(self, x):
self.x = x
self.radius = 10
self.y = randint(100, 500) + self.radius
self.dx = -2
def perform_action(self):
pass
def movement(self):
self.x += self.dx
if self.x < 0 - WIDTH:
self.x = WIDTH + randint(0, 300)
self.y = HEIGHT - 40 - randint(0, 400)
def draw_item(self, colour):
pygame.draw.circle(WINDOW, colour, (self.x, self.y), self.radius)
class GravUp(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(RED)
def perform_action(self):
global gravity
gravity += 3
class GravDown(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(GREEN)
class AgilUp(Item):
def __init__(self, x):
super().__init__(x)
def draw_item(self):
super().draw_item(CYAN)
item_types = {0: GravUp(WIDTH + randint(0, 500)),
1: GravDown(WIDTH + randint(0, 500)),
2: AgilUp(WIDTH + randint(0, 500))}
items = []
for i in range(10):
items.append(item_types[randint(0, 2)])
if __name__ == '__main__':
main()
When printing out the addresses of the created objects you will see why there are different speeds:
<__main__.AgilUp object at 0x0BD30E70>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.GravUp object at 0x0BD30550>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.GravUp object at 0x0BD30550>
<__main__.GravUp object at 0x0BD30550>
<__main__.GravDown object at 0x0BD306F0>
<__main__.GravDown object at 0x0BD306F0>
<__main__.AgilUp object at 0x0BD30E70>
<__main__.AgilUp object at 0x0BD30E70>
So you don't have 10 objects, but only three, and 10 random pointers to one of these. In this example, the movement instructions will call 5 times AgilUp, 3 times GravUp and 2 times GravDown.
Redesigning the object creation process should fix the issue.
The answer to your random speed behavior resides within the following lines of code in your program.
item_types = {0: GravUp(WIDTH + randint(0, 500)),
1: GravDown(WIDTH + randint(0, 500)),
2: AgilUp(WIDTH + randint(0, 500))}
items = []
for i in range(10):
items.append(item_types[randint(0, 2)])
What this bit of code is doing is indeed defining your three colored circles. Then, it is randomly assigning a number of times (one out of ten ) each circle will be moved and redrawn.
When I added in a print operation within the "draw_item" function and ran the program, I was able to determine the number of drawing/redrawing calls each circle was getting by printing out the item's RGB color value.
(0, 255, 0)
(255, 0, 0)
(255, 0, 0)
(255, 0, 0)
(0, 255, 0)
(0, 255, 0)
(0, 255, 255)
(255, 0, 0)
(0, 255, 0)
(0, 255, 255)
If you tally up the quantities, the green circle gets four calls to move and redraw, the red circle gets four calls to move and redraw, and the cyan circle gets two calls to move and redraw. Running this again would probably produce a different mix of calls for each color.
Reviewing your code, I am guessing what you ultimately wanted to do was create ten circles with random colors at random positions. If that is actually what you want to do, you will need to revise the block of code to derive a random number first, and then based upon that number, populate the item group with the random item type.
I hope that clarifies things.
Regards.
I tried to make a 2D texture rendering function in pygame. But it doesn't work. Here is function:
def drawTexture(screen,texture,rect):
last_texture_x, last_texture_y = rect[2],rect[3]
for texture_count_y in range(0,int(rect[3]/texture.get_height())+1):
for texture_count_x in range(0,int(rect[2]/texture.get_width())+1):
screen.blit(texture.subsurface(0,0,min(last_texture_x,texture.get_width()),min(last_texture_y,texture.get_height())),(rect[0]+texture.get_width()*texture_count_x,rect[1]+texture.get_height()*texture_count_y))
last_texture_x -= texture.get_width()
last_texture_y -= texture.get_height()
This function fills a rect with a texture. It fills width well. But it don't fill height when rect height is bigger than texture height. I think problem is texture_count_y variable. When I replace the variable to a number manually, the function works. But variable returns right value when I print it. My head really confused right now. How can I make the function works well?
EDIT:
Red lines mean rect.
Yellow lines mean texture images function uses to fill rect.
It fills columns well but it fills one row.
The problem is simple. last_texture_x must be initialized before the inner loop:
def drawTexture(screen, texture, rect):
last_texture_y = rect[3]
for texture_count_y in range(0,int(rect[3]/texture.get_height())+1):
last_texture_x = rect[2]
for texture_count_x in range(0,int(rect[2]/texture.get_width())+1):
screen.blit(texture.subsurface(0,0,min(last_texture_x,texture.get_width()),min(last_texture_y,texture.get_height())),(rect[0]+texture.get_width()*texture_count_x,rect[1]+texture.get_height()*texture_count_y))
last_texture_x -= texture.get_width()
last_texture_y -= texture.get_height()
However, I recommend to use the features of the pygame.Rect object for this task:
def drawTexture(screen, texture, rect):
target_rect = pygame.Rect(rect)
for x in range(target_rect.left, target_rect.right, texture.get_width()):
for y in range(target_rect.top, target_rect.bottom, texture.get_height()):
clip_rect = texture.get_rect(topleft = (x, y)).clip(target_rect)
screen.blit(texture.subsurface(0, 0, *clip_rect.size), (x, y))
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
def drawTexture(screen, texture, rect):
target_rect = pygame.Rect(rect)
for x in range(target_rect.left, target_rect.right, texture.get_width()):
for y in range(target_rect.top, target_rect.bottom, texture.get_height()):
clip_rect = texture.get_rect(topleft = (x, y)).clip(target_rect)
screen.blit(texture.subsurface(0, 0, *clip_rect.size), (x, y))
target_rect = pygame.Rect(25, 25, 450, 250)
texture = pygame.Surface((200, 200))
texture.fill((255, 255, 0))
pygame.draw.rect(texture, (127, 127, 127), (2, 2, 196, 196))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
pygame.draw.rect(window, (255, 255, 255), target_rect, 5)
drawTexture(window, texture, target_rect)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
from what I understand draw.lines joins each coordinate that is passed to it, so if we have [A, B, C]
will draw a line from coordinate A to coordinate B and from B to C and if closed is True it draws a line from coordinate A to coordinate C therefore it will always join the first coordinate with the last one its right?
what i don't understand is what the rect variable returns..
I think that every time I add a new coordinate it returns (starting_point, rectangle_size) where starting point is the first coordinate and the rectangle size is calculated by the distance of the first coordinate with the last one then draw the rectangle with draw.rect
but the reasoning I don't think is right because if I add a coordinate of this type to the list the rectangle remains unchanged
CODE:
"""Place a polygone line with the clicks of the mouse."""
import pygame
from pygame.locals import *
RED = (255, 0, 0)
GREEN = (0, 255, 0)
GRAY = (150, 150, 150)
pygame.init()
screen = pygame.display.set_mode((640, 240))
drawing = False
points = []
running = True
while running:
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
points.append(event.pos)
drawing = True
elif event.type == MOUSEBUTTONUP:
drawing = False
elif event.type == MOUSEMOTION and drawing:
points[-1] = event.pos
screen.fill(GRAY)
if len(points)>1:
rect = pygame.draw.lines(screen, RED, True, points, 3)
pygame.draw.rect(screen, GREEN, rect, 1)
pygame.display.update()
pygame.quit()
pygame.draw.lines() returns a pygame.Rect object that encloses all the points of the line:
a rect bounding the changed pixels, if nothing is drawn the bounding rect's position will be the position of the first point in the points parameter (float values will be truncated) and its width and height will be 0
The rectangle does not start at any particular point on the line, but is just large enough to enclose all the points along the line.
It returns the same as:
list_x, list_y = zip(*points)
min_x, max_x = min(list_x), max(list_x)
min_y, max_y = min(list_y), max(list_y)
rect = pygame.Rect(min_x, min_y, max_x-min_x, max_y-min_y)
According to the docs:
Returns:
a rect bounding the changed pixels, if nothing is drawn the bounding rect's position will be the position of the first point in the points parameter (float values will be truncated) and its width and height will be 0
this is the reasoning I made:
import pygame
def minore(lista_coordinate, asse):
dimensione = lista_coordinate[0][asse]
for coordinata in lista_coordinate:
if coordinata[asse] < dimensione:
dimensione = coordinata[asse]
return dimensione
def maggiore(lista_coordinate, asse):
dimensione = lista_coordinate[0][asse]
for coordinata in lista_coordinate:
if coordinata[asse] > dimensione:
dimensione = coordinata[asse]
return dimensione
pygame.init()
schermo = pygame.display.set_mode((500, 400))
punti = [(100, 100), (200, 100), (200, 50), (150, 80)]
larghezza_minore = minore(punti, 0)
larghezza_maggiore = maggiore(punti, 0)
larghezza_rettangolo = larghezza_maggiore - larghezza_minore
altezza_minore = minore(punti, 1)
altezza_maggiore = maggiore(punti, 1)
altezza_rettangolo = altezza_maggiore - altezza_minore
dimensioni_rettangolo = (larghezza_rettangolo, altezza_rettangolo)
inizio = (larghezza_minore, altezza_minore)
pygame.draw.lines(schermo, (255, 0, 0), True, punti, 3)
pygame.draw.rect(schermo, (0, 255, 0), (inizio, dimensioni_rettangolo), 1)
pygame.display.update()
i wanted to draw a circle in pygame that is empty, i mean you should see behind surface through it.
i wrote a code that find all the colored pixels in a surface and empty their coordinates in the other surface.
def draw_empty(surf1, surf2, pos):
"""makes surf2 pixels empty on surf1"""
pixobj2 = pygame.PixelArray(surf2)
empty = []
for x in range(len(pixobj2)):
for y in range(len(pixobj2[x])):
if pixobj2[x][y]!=0:
empty.append((x,y))
del pixobj2
pixobj1 = pygame.PixelArray(surf1)
for pix in empty:
pixobj1[pix[0]+pos[0]][pix[1]+pos[1]] = 0
del pixobj1
window = pygame.display.set_mode((600, 600))
white_surf = pygame.surface.Surface((600, 600))
white_surf.fill((255, 255, 255))
circle_surf = pygame.surface.Surface((50, 50))
pygame.draw.circle(circle_surf, (1, 1, 1), circle_surf.get_rect().center, 25)
pic = pygame.image.load('pic.png')
pic = pygame.transform.scale(pic, (600, 600))
done = False
while not done:
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
draw_empty(white_surf, circle_surf, (200, 200))
window.blit(pic, (0, 0))
window.blit(white_surf, (0, 0))
pygame.display.update()
pygame.time.Clock().tick(60)
i expected to see background picture through that circle but the circle i just black, is this even possible to do that? can somebody help me with this?
P.S.
by empty, i mean like a hole in my front surface
Just use the draw() function:
pygame.draw.circle(Surface, color, pos, radius, width=0)
width represents the size of the circumference curve. When it is 0, the circle is solid. If you set it to 1, the edge will be only one pixel wide (an empty circle).
edit: to draw a circle with a sprite, you can use this code†:
import pygame
import pygame.gfxdraw
IMG = pygame.Surface((30, 30), pygame.SRCALPHA)
pygame.gfxdraw.aacircle(IMG, 15, 15, 14, (0, 255, 0))
pygame.gfxdraw.filled_circle(IMG, 15, 15, 14, (0, 255, 0))
class img(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = IMG
self.rect = self.image.get_rect(center=(150, 200))
†Credit for code goes to dunker_wanderer
You can draw the shape the same way you would if it had a color or surface image, and then just set the fill to a transparent color using:
empty = (255,255,255,0)
surface.fill(empty)
You will have to figure out how to add this to your code without breaking it, or rewrite it but, first you will need to make a set with three numbers
white = (255, 255, 255)
next use the function
pygame.draw.rect(canvas_name, set_name, (100, 100, 100, 100))
last use
pygame.display.flip()
#Initializes pygame import pygame, random, sys, time, math pygame.init()
#Creates screen screen = pygame.display.set_mode((400, 350)) pygame.display.set_caption("Game")
#Initializes screen running = True while running: pass
#Colors screen white = (255, 255, 255) screen.fill(white)
#Draws rectangle num1 = random.randrange(0, 255) num2 = random.randrange(0, 255) num3 = random.randrange(0, 255)
color = (num1, num2, num3) pygame.draw.rect(screen, color, pygame.Rect(100, 100, 60, 60), )
pygame.display.flip()
#Made by and only by the kid genius Nathan
I am creating a randomly generated map in with PyGame. However, I've run into an issue, where, if the user scrolls away from the top-left corner of the map and changes the PyGame surface that's displayed, an issue happens.
The problem is, PyGame still starts them on the upper-left of the surface, and will then allow them to scroll off the edges of the surface because the list that keeps track of that, camera_pos, now has incorrect values.
All of the surfaces are the same dimensions and I want to make it so the user is in the same position when they change the displayed surface. However, I'm not sure how to set the position of the user's view when pygame switches surfaces.
How can I switch the position of the user's view back to what it used to be when the surface is switched?
I have made a MCV Example below I hope will help. Instead of displaying maps, it just draws a border around a solid color. I apologize for how long it is. I'm not sure how to make it much shorter.
In this example, scrolling is done with the arrow keys. You can press r, g, or b on the keyboard to display the different colored surfaces.
import pygame
import numpy as np
import sys
def scroll_y(display_surface, offset):
"""
Handles vertical scrolling.
:param display_surface: A pyGame surface object.
:param offset: The speed of the scroll
"""
width, height = display_surface.get_size()
map_copy = display_surface.copy()
display_surface.blit(map_copy, (0, offset))
# handle scrolling down
if offset < 0:
display_surface.blit(map_copy,
(0, height + offset),
(0, 0, width, -offset))
# handle scrolling up
else:
display_surface.blit(map_copy,
(0, 0),
(0, height - offset, width, offset))
def scroll_x(display_surface, offset):
"""
Handles horizontal scrolling.
:param display_surface: A pyGame surface object.
:param offset: The speed of the scroll
"""
width, height = display_surface.get_size()
map_copy = display_surface.copy()
display_surface.blit(map_copy, (offset, 0))
# handle scrolling right
if offset < 0:
display_surface.blit(map_copy,
(width + offset, 0),
(0, 0, -offset, height))
# handle scrolling left
else:
display_surface.blit(map_copy,
(0, 0),
(width - offset, 0, offset, height))
def main():
"""
This function displays the three surfaces.
Press r to show the red surface (which is displayed by default).
Press g to show the green surface.
Press b to show the blue surface.
"""
pygame.init()
window = pygame.display.set_mode((1600, 900))
red_surface = pygame.Surface([3200, 1800]).convert(window)
green_surface = pygame.Surface([3200, 1800]).convert(window)
blue_surface = pygame.Surface([3200, 1800]).convert(window)
red_surface.fill((255, 145, 145))
green_surface.fill((145, 255, 145))
blue_surface.fill((145, 145, 255))
# draw thick black lines on surface borders
pygame.draw.rect(red_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
pygame.draw.rect(green_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
pygame.draw.rect(blue_surface, (0, 0, 0), (0, 0, 3200, 1800), 40)
display_surface = red_surface.copy()
camera_pos = np.array([0, 0])
while True: # <-- the pyGame loop
event = pygame.event.poll()
pressed = pygame.key.get_pressed()
# handle closing the window
if event.type == pygame.QUIT:
break
window.blit(display_surface, (0, 0))
# handle switching display modes
if pressed[pygame.K_g]:
display_surface = green_surface
elif pressed[pygame.K_b]:
display_surface = blue_surface
elif pressed[pygame.K_r]:
display_surface = red_surface
# handle scrolling, make sure you can't scroll past the borders
if pressed[pygame.K_UP] and camera_pos[1] > 0:
scroll_y(display_surface, 5)
camera_pos[1] -= 5
elif pressed[pygame.K_DOWN] and camera_pos[1] < (1800 / 2):
scroll_y(display_surface, -5)
camera_pos[1] += 5
elif pressed[pygame.K_LEFT] and camera_pos[0] > 0:
scroll_x(display_surface, 5)
camera_pos[0] -= 5
elif pressed[pygame.K_RIGHT] and camera_pos[0] < (3200 / 2):
scroll_x(display_surface, -5)
camera_pos[0] += 5
# updates what the window displays
pygame.display.update()
pygame.quit()
sys.exit(0)
if __name__ == "__main__":
# runs the pyGame loop
main()
Here's what I think is a fairly elegant solution that doesn't require the two scrolling functions, scroll_x() and scroll_y() you have. Because it was so fast not using them, the main loop was detecting the same scrolling key as being pressed multiple times — necessitating the addition of a pygame.time.Clock to slow the frame-rate down to something reasonable.
Instead of scrolling the display surfaces themselves via those scrolling functions, as your code was doing, this version just updates the current "camera" position, then blits the corresponding region of the current display_surface to the window whenever it's modified. The camera's position is constrained by making sure its x and y components stay within some boundary limit constants — MINX,MINY and MAXX,MAXY — which get computed based the values of some other previously defined constants.
The use of symbolic constants rather than hardcoding literal values multiple places in the code is considered a very good programming practice because it makes changing them easier, since doing so only requires a source code change to be done one place.
import pygame
import sys
def main():
"""
This function displays the three surfaces.
Press r to show the red surface (which is displayed by default).
Press g to show the green surface.
Press b to show the blue surface.
"""
FPS = 60 # Frames per second
SURF_WIDTH, SURF_HEIGHT = 3200, 1800
WIN_WIDTH, WIN_HEIGHT = 1600, 900
DX, DY = 5, 5 # Scroll amounts.
MINX, MAXX = DX, SURF_WIDTH - WIN_WIDTH + DX - 1
MINY, MAXY = DY, SURF_HEIGHT - WIN_HEIGHT + DY - 1
pygame.init()
pygame.font.init()
fonts = pygame.font.get_fonts()
clock = pygame.time.Clock()
window = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
red_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
green_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
blue_surface = pygame.Surface([SURF_WIDTH, SURF_HEIGHT]).convert(window)
red_surface.fill((255, 145, 145))
green_surface.fill((145, 255, 145))
blue_surface.fill((145, 145, 255))
# Draw thick black lines on surface borders
pygame.draw.rect(red_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
pygame.draw.rect(green_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
pygame.draw.rect(blue_surface, (0, 0, 0), (0, 0, SURF_WIDTH, SURF_HEIGHT), 40)
# Draw label on each of the surfaces for testing. (ADDED)
font = pygame.font.SysFont(None, 35)
rtext = font.render('red surface', True, (255, 0, 0))
textpos = rtext.get_rect(centerx=300, centery=200) # Reused.
red_surface.blit(rtext, textpos)
rtext = font.render('green surface', True, (0, 192, 0))
green_surface.blit(rtext, textpos)
rtext = font.render('blue surface', True, (0, 0, 255))
blue_surface.blit(rtext, textpos)
display_surface = red_surface
camera_pos = pygame.math.Vector2(0, 0)
update_surface = True
while True: # Game loop
if update_surface:
window.blit(display_surface, (0, 0), (camera_pos[0], camera_pos[1],
WIN_WIDTH, WIN_HEIGHT))
update_surface = False
event = pygame.event.poll()
pressed = pygame.key.get_pressed()
# Close window?
if event.type == pygame.QUIT or pressed[pygame.K_ESCAPE]:
break
# Switch display surface?
if pressed[pygame.K_g]:
display_surface = green_surface
update_surface = True
elif pressed[pygame.K_b]:
display_surface = blue_surface
update_surface = True
elif pressed[pygame.K_r]:
display_surface = red_surface
update_surface = True
# Constrain scrolling to within borders
if pressed[pygame.K_LEFT] and camera_pos[0] >= MINX:
camera_pos[0] -= DX
update_surface = True
elif pressed[pygame.K_RIGHT] and camera_pos[0] <= MAXX:
camera_pos[0] += DX
update_surface = True
elif pressed[pygame.K_UP] and camera_pos[1] >= MINY:
camera_pos[1] -= DY
update_surface = True
elif pressed[pygame.K_DOWN] and camera_pos[1] <= MAXY:
camera_pos[1] += DY
update_surface = True
# updates what the window displays
pygame.display.update()
clock.tick(FPS)
pygame.quit()
sys.exit(0)
if __name__ == "__main__":
main() # runs the pyGame loop