views:

563

answers:

4

Here is how I'm implementing my simple pygames now (I'm following a tutorial):

import pygame, sys
from pygame.locals import *

def run_game():
    pygame.init()

    SIZE = (640, 400)
    BG_COLOUR = (0, 0, 0)
    LINE_COLOUR = (255, 255, 255)

    screen = pygame.display.set_mode(SIZE)
    clock = pygame.time.Clock()

    while True:
        time_passed = clock.tick(30)
        for event in pygame.event.get():
                if event.type == QUIT:
                        exit_game()

        screen.fill(BG_COLOUR)
        pygame.draw.aaline(screen, LINE_COLOUR, (1, 1), (639, 399))
        pygame.display.flip()

def exit_game():
    sys.exit()

if __name__ == "__main__"
    run_game()

I've also seen a keeprunning flag being used to exit the main event loop instead, as well as using pygame.event.poll() instead of looping through pygame.event.get(). Any suggestions? Anything at all like case/naming of variables, anything to make it more effective or readable. Thanks.

A: 

This looks like basically every other example I have ever seen. Perhaps you may want to find some open source games written in pygame.

goldenratio
+7  A: 

Whatever the pygame authors recommend, I suggest avoiding from ... import *: it only makes your code harder to read (as the reader has to inspect each and every barename to check whether it's actually assigned locally OR comes from the deuced *). Change that to import pygame.locals as pygl, say, and make every use of a name from it be a qualified one (I think in your code this just means changing QUIT to pygl.QUIT). What short name exactly you choose to use in lieu of pygl doesn't matter much, but that's a general structure I STRONGLY recommend. Remeber: namespaces are a honking great idea -- let's do more of those!-)

Use 4-space indents everywhere, as PEP 8 says: you seem to be mixing some 4-space indents with other which are 8-space or tabs -- don't!

Don't assign to variables you're never ever gonna use, as you're doing it for time_passed.

Code like: if event.type == QUIT: exit_game() is fine as long as you're testing one or very few possibilities (with if/elif), but doesn't "scale up" much, in either readability or efficiency. When you need to cover several cases, use a dict that you can set up before the loop:

dispatch = {pygl.QUIT: exit_game, # whatever else
           }

and instead of if/elif/else, use:

f = dispatch.get(event.type)
if f is None:  # the "else" case"
   ...
else: f()
Alex Martelli
>"I suggest avoiding from ... import *:" . `pygame.locals` exists precisely for this usage. It contains a number of very frequently used constants, mostly keycodes and event types.
TokenMacGuy
The tabs/space 4/8 mix were an accident. I use tabs, not spaces though because in the jEdit with spaces I have to backspace 4 times to go up a level of indentation vs once with tabs. I can easily convert them all to spaces anytime, though.
Mk12
And the dictionary thing, not all cases only have one statement which is a method call, so how would those work? I'm not sure about the importing part, why do they say in the docs: "As a convenience, most of the top-level variables in pygame have been placed inside a module named 'pygame.locals'. This is meant to be used with 'from pygame.locals import *', in addition to 'import pygame'." ? Thanks for taking the time to answer, I'll look over what you said again.
Mk12
I'm fully aware of pygame developers' advice -- that's why I said my own advice to the contrary was given "whatever the pygame authors recommend": I think they're dead wrong. Why make me waste minutes perusing supercarefully to ascertain that QUIT is from pygame while the other uppercase identifiers aren't? "import this" at an interpreter prompt, read and ponder the Zen of Python, and especially the last point that I already quoted. As for "not all cases only have a method call" -- what's to stop you from placing all the code to handle a statement into a local function or method?!
Alex Martelli
What about when the methods would need to assign variables in the run_game method? That wouldn't work, since arguments are passed by value.
Mk12
In Python 2.6 or better, you make those callables local (nested) functions and they use `nonlocal` to indicate the outer function's variables they "need to assign" ("need" is a very relative word: I had no real problem in Python <= 2.5, just used anything BUT barenames local to the outer function for such variables;-).
Alex Martelli
actually nonlocal is in 3.x not 2.6.
Mk12
@Mk12 you're right, `3.*` (one of the few features not backported to 2.6) -- can't edit tho (it's a comment!-). So in `2.*` you use anything BUT barenames for what needs to be assigned in a nested function: a container's items or an object's attributes both work just fine and dandy, it's JUST barenames (plain identifiers without any dots nor indexing whatsoever) that are special, being considered local variables if assigned to.
Alex Martelli
A: 

I am not sure if this is the sort of answer you are looking for, but after
pygame.draw.aaline(screen, LINE_COLOUR, (1, 1), (639, 399))
you should put
pygame.display.flip()
This is so that the display which the player actually sees will get refreshed every frame. If you run this code, then all you will see is a black screen, with no line as the display is never updated.

Nikwin
Thanks, I forgot that. I normally always do that.
Mk12
+2  A: 

Your example is very good . But if you see how worked "pygame.event.poll()" see this simple example:

event = pygame.event.poll()
    while "ok":
     event = pygame.event.wait()
     cursor = pygame.mouse.get_pos()
     if event.type == QUIT: raise SystemExit
            if event.type == MOUSEBUTTONUP and pygame.mouse.get_pressed()[0]:
               print "up"
     if event.type == MOUSEBUTTONDOWN and pygame.mouse.get_pressed()[0]:
               print "down"
     if event.type == KEYDOWN and event.key == K_SPACE: print "space key"
     if event.type == KEYDOWN and event.key == K_BACKSPACE:
               print "backspace key"

you can use all events from pygame:

  • pygame.event.get - get events from the queue get events from the queue
  • pygame.event.poll - get a single event from the queue get a single event from the queue
  • pygame.event.wait - wait for a single event from the queue

or

  • pygame.event.clear([pygame.QUIT, pygame.ACTIVEEVENT, pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION, pygame.JOYBUTTONUP, pygame.USEREVENT]) to remove all events from the queue

Bassicaly you create functions for this events and use it . Like this :

def click_down():
    #print "Event queue:", pygame.event.peek()
    return pygame.event.peek(pygame.MOUSEBUTTONDOWN)

def is_click():
    if not click_down():
        return None

    clicks = pygame.event.get(pygame.MOUSEBUTTONDOWN)
    pygame.event.get(pygame.MOUSEBUTTONUP)
    return clicks[0].pos
def quiting():
    return pygame.event.peek(pygame.QUIT)
def keyboard():
    pygame.event.pump()
    pygame.event.get(pygame.KEYDOWN)

or blocking some events like :

pygame.event.set_blocked((pygame.JOYAXISMOTION))
Catalin Festila