Python PyGame draw rectangles using for loop

2019-08-10 22:05发布

问题:

I'm new to PyGame and I am learning using the book Beginning Game Development with Python and PyGame. There is an example (Listing 4-9) where the author says a script will draw ten randomly placed, randomly colored rectangles on the PyGame screen. Here is the code from the book:

import pygame 
from pygame.locals import *
from sys import exit
from random import *

pygame.init()

screen = pygame.display.set_mode((640, 480), 0,32)
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    screen.lock()
    for count in range(10):
        random_color = (randint(0,255), randint(0,255), randint(0,255))
        random_pos = (randint(0,639), randint(0,479))
        random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))
        pygame.draw.rect(screen, random_color, Rect(random_pos, random_size))
    screen.unlock()
    pygame.display.update()

What happens when I do this (and this is what I would expect to happen logically) is that it draws infinitely many rectangles. It just keeps doing the for loop because the while loop is always True. I have searched online about this, and I tried moving the display update around, but those things didn't work. It is driving me crazy!

Thanks!

回答1:

Looks like you already knew why it were drawing infinite with rectangles.

I guess you want to draw 10 random rectangles with random size, pos and color once.

Then you can do like this:

import pygame 
from pygame.locals import *
from sys import exit
from random import *

pygame.init()

screen = pygame.display.set_mode((640, 480), 0,32)

class Rectangle:
    def __init__(self, pos, color, size):
        self.pos = pos
        self.color = color
        self.size = size
    def draw(self):
        pygame.draw.rect(screen, self.color, Rect(self.pos, self.size))

rectangles = []     

for count in range(10):
    random_color = (randint(0,255), randint(0,255), randint(0,255))
    random_pos = (randint(0,639), randint(0,479))
    random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))

    rectangles.append(Rectangle(random_pos, random_color, random_size))



while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    screen.lock()
    for rectangle in rectangles:
        rectangle.draw()
    screen.unlock()
    pygame.display.update()


回答2:

The code does run an infinitely. However, the inner loop does create 10 rectangles. So technically the script does draw ten randomly placed, randomly colored rectangles. It just does that an infinite amount of times.

Note: This is the part that draw ten randomly placed, randomly colored rectangles.

for count in range(10):
        random_color = (randint(0,255), randint(0,255), randint(0,255))
        random_pos = (randint(0,639), randint(0,479))
        random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))
        pygame.draw.rect(screen, random_color, Rect(random_pos, random_size))


回答3:

It will draws many rectangles, and its endless yes.It is not drawing 10 times, because there is no arguments says break the while loop except -sys exit-. So you have to define a variable for that.

Normally there should be an if statement in the event statement, that will change the boolean of while to False and it will stop the loop.

You can simply change your codes to:

import pygame 
from pygame.locals import *
from sys import exit
from random import *

pygame.init()

screen = pygame.display.set_mode((640, 480), 0,32)
count=0 #new variable for stop the loop
while count<10: #new limit
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()
    screen.lock()

    random_color = (randint(0,255), randint(0,255), randint(0,255))
    random_pos = (randint(0,639), randint(0,479))
    random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))
    pygame.draw.rect(screen, random_color, Rect(random_pos, random_size))
    count+=1 #variable updating after drawing each rectangle

    screen.unlock()
    pygame.display.update()

Its maybe a misstyping in the book I dont know, but that loop is infinite so you can change it to this :)



回答4:

Yes, this is a poorly written example, but it's not "broken." Everyone so far has overlooked this bit:

for event in pygame.event.get():
    if event.type == QUIT:
        exit()

PyGame includes the notion of processing events. This first line gets all the events that your game has access to, and looks at each of them. If one of them is a "QUIT" event, then it calls exit() (more commonly known as sys.exit(0)) directly, which immediately ends the application.

The "QUIT" type event is generated when you quit the application, such as by clicking the red X. There are likely other ways to end the application that will also generate this event, but I'm not sure what they are. There are also ways to end the application that will NOT generate this event. So yes, if you never quit the application it will run forever-ish.

A much better way to write this functionality is as follows:

done = False
while not done:
    for event in pygame.event.get():
        if event.type == QUIT: # or other types of events
            done = True
    //do other game logic and drawing stuff after this
    pygame.display.update()

This makes sure that the loop is handled consistently, and that the exit point is always at the same place: the top of the loop. If there's only one place to exit the loop, then you have an easier time knowing how much of the code is getting run (all of it). It also allows you to have some more sophisticated ways of deciding when and whether to end the loop.

However, even this is going to be a very confusing "example" because (just like you and @user3679917 noted) it seems to do something different than described. It seems like it's just drawing random rectangles forever. Let's see if we can make it more predictable...

//pre-roll the random numbers before the while loop
random_attributes = []
for count in range(10):
    random_color = (randint(0,255), randint(0,255), randint(0,255))
    random_pos = (randint(0,639), randint(0,479))
    random_size = (639-randint(random_pos[0], 639), 479-randint(random_pos[1],479))
    random_attributes.append(random_color, random_pos, random_size)

//then when you draw, just draw what you already rolled each time, without rolling new stats
done = False
while not done:
    for event in pygame.event.get():
        if event.type == QUIT: # or other types of events
            done = True
    //do other game logic and drawing stuff after this
    for color, position, size in random_attributes:
        pygame.draw.rect(screen, color, Rect(position, size))
    pygame.display.update()

That will draw the same thing infinitely, until you close the application. Incidentally, this is also what (almost) all visual applications do all the time, i.e. draw everything every time. It's not as if you can draw it on the screen once and it stays. Every time the screen is drawn, the screen asks everyone "Okay guys, what's going on the screen right now," and unless your application says "draw this stuff," then it's not going to get drawn, even if it was drawn last time.

Hope this helps.



标签: python pygame