[Tutor] Iterating backwards

James Newton jnewton at fuelindustries.com
Fri Jan 11 17:53:35 CET 2008

Hi Pygamers,

I have created a series of pygame sprites.  Visually, these sprites
represent counters and may overlap.  I want to determine which sprite
the user clicked on.

This seems to me to be a fairly standard feature of a game interface, so
I imagine that there is already a standard technique for achieving it. 

My current implementation (see below) is to use a "for" statement on the
pygame.sprite group that contains the sprites.  This means that I have
to test all the sprites to determine the top-most sprite that is under
the mouse.

The sprites are blitted to the screen in the order they were added to
the sprite group, so the sprite that appears on top is the last one in
the group.  I'd like to start at the top-most sprite and work backwards
through the sprites in the group, and then use "break" to stop as soon
as I find a sprite whose rect collides with the mouse position.

In my code below, I've tried replacing the line...

    for vCounter in counters: # the pygame.sprite.RenderPlain() group

... with:

    vRange = range(len(counters))
    for ii in vRange:
        vCounter = counters[ii]

However this results in an error:

    #TypeError: 'Group' object is unindexable

Is there a way to iterate backwards through a pygame sprite group?  Or
can you recommend an alternative method to achieve the same ends?

My overall aim is to make the sprite the user clicks on jump to the top,
so the order of the sprites in the group will change over time.

Thanks in advance,



Here's my barebones code.  It assumes that there are three 64 x 64 pixel
image files named Counter_1.png through Counter_3.png in a subfolder
named data.

import os
import pygame
# Add QUIT, KEYDOWN and other event names to module namespace
from pygame.locals import *

class Counter(pygame.sprite.Sprite):
    """ Creates a counter sprite from an image and
        places it in a tiled pattern relative to
        other counters

    def __init__(self, aIndex):

        self.index = aIndex

        # Create a "surface" that we need to name "image" to overwrite
        # the Sprite's own .image property, which remains as None
        vFileName  = "Counter_"+str(aIndex + 1)+".png"
        vPath      = os.path.join('data', vFileName)
        self.image = pygame.image.load(vPath).convert()
        self.rect  = self.image.get_rect()

        # Scatter the counters so they overlap
        self.rect.move_ip(aIndex * 24, aIndex * 24)

    def is_opaque_at(self, x, y):
        if self.rect.collidepoint(x, y):
            return True

        return False

def main():
    """ Demonstrates the use of pygames RenderUpdates
        sprite group

    # Initialization
    clock      = pygame.time.Clock()
    screen     = pygame.display.set_mode((120, 120))
    counters   = pygame.sprite.RenderPlain()
    for i in range(0, 3):
        vCounter = Counter(i)
    # <Infinite loop
    while True:
        clock.tick(5) # 5 frames per second is quite sufficient

        for event in pygame.event.get():
            if event.type in (QUIT, KEYDOWN):

            elif event.type == MOUSEBUTTONDOWN:
                vPos   = pygame.mouse.get_pos()

                vIndex = None
                for vCounter in counters:
                    vClicked = vCounter.is_opaque_at(*vPos)
                    if vClicked:
                        vIndex = vCounter.index
                        print "Sprite "+str(vIndex)+" is under the
                        #break  # Uncomment to return the *lowest*

                if vIndex != None:
                    print vIndex
   # Infinite loop>

if __name__ == '__main__': main()

More information about the Tutor mailing list