A PyGame Working Example, continued - Constructing the Main Loop
(Page 4 of 5 )
Now that we've created functions to get things set up for us, we're ready to get into the function containing the main loop of the game. Before we get into our function's actual loop, though, we'll have to do a bit of configuration. First, we'll have to get the size of each position on the screen. This simply involves dividing the height and width of the background by the number of rows and columns. Then, we'll have to define two variables that work with movement. When the user presses a key, the first variable will need to be set to the direction of the movement. When the user releases a key, the second variable will need to be set to True, which will signal movement to stop. This way, a user can hold down a key and see movement rather than have to repeatedly tap the key.
Though we have a list of sprite groups, we also need a list that will store the sprite groups that are actually visible on the screen. This way, we'll know which ones to update. Next, we need to actually draw the player. Finally, we'll have to load custom events into the timer. This is done by setting the timer to trigger our own event IDs at set intervals. The first event will update the player, and the second event will update the objects. The speeds at which things will be updated will be passed as arguments.
Here's all this in action:
def run(playerSpeed = 250, objectSpeed = 1000):
# Get the row and column widths
colWidth = background.get_rect().width / columns
rowHeight = background.get_rect().height / rows
# Define a variable that stores whether an arrow key is
pressed
# This is used for continuous/scrolling movement of the player
moving = False
# We should also define a variable that signals stops
# Otherwise, if a player pushes a key in between updates and
releases
# it, the move will not be registered
movingStop = False
# Create a list to store the visible groups
visible = []
# Blit the player
updateRects = player.draw(screen)
pygame.display.update(updateRects)
# Load a screen update event into the timer
# This will update the player only
pygame.time.set_timer(pygame.USEREVENT + 1, playerSpeed)
# Load a screen update event into the timer
# This will update the objects
pygame.time.set_timer(pygame.USEREVENT + 2, objectSpeed)
With all that done, we're now ready to jump into the main loop. First, we'll need to check for quit events and key presses:
def run(playerSpeed = 250, objectSpeed = 1000):
...
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Check for a key push by the user
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
moving = 'right'
elif event.key == pygame.K_LEFT:
moving = 'left'
# Check by a key release by the user
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT or pygame.K_LEFT:
movingStop = True
The above code simply sets the player to move when a key is pressed, and it sets it to stop moving when a key is released. It also enables the player to exit the game.
The next task ahead of us is moving the player around. To do this, we first need to check to see whether our custom player update event has been triggered. If it has, we then need to move the player in the proper direction, if any direction at all. We finally need to check to see whether the player has collided with any of the objects and then redraw the player:
def run(playerSpeed = 250, objectSpeed = 1000):
...
# Check for the update player event
elif event.type == pygame.USEREVENT + 1:
# Move the player if needed
if moving == 'right':
player.update(colWidth)
if moving == 'left':
player.update(-colWidth)
# Stop movement if needed
if movingStop:
moving = False
movingStop = False
# Collision detection
for group in visible:
if pygame.sprite.spritecollideany(playerSprite,
group):
return False
# Redraw the player
player.clear(screen, erase)
updateRects = player.draw(screen)
pygame.display.update(updateRects)
As I mentioned earlier, the visible list above will contain a list of all the visible sprite groups. To check for collision, we iterate through this list and use pygame.sprite.spritecollideany. There's no need to use a method that kills the colliding sprites since we want to completely back out of the loop. What object the player hits doesn't really matter to us. As you can see, we return False to get out of the loop.
All that's left now is moving the objects around, an extremely simple task. This is done when our custom object update event has been triggered. If anything remains in the layout list, we add it to our visible list. Likewise, we delete any sprite group that has fallen off the screen. Aside from that, we simply update each sprite group, and if the visible list is empty, then the player has won the level.
def run(playerSpeed = 250, objectSpeed = 1000):
...
# Check for the update object event
elif event.type == pygame.USEREVENT + 2:
# Add a row
if layout:
visible.append(layout.pop())
# Delete passed rows
if visible:
if visible[0].y >= screen.get_rect().height:
visible.pop(0)
# If there are no visible rows, the player has won
else:
return True
# Make a list of rectangles to be updated
updateRects = []
# Update each group
for group in visible:
group.clear(screen, erase)
group.update(rowHeight)
group.y = group.y + rowHeight
updateRects.extend(group.draw(screen))
pygame.display.update(updateRects)
Next: The Game in Action >>
More Python Articles
More By Peyton McCullough