Discussion:
[Tutor] Iterating backwards
James Newton
2008-01-11 16:53:35 UTC
Permalink
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))
vRange.reverse()
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,

James


-----------------------------------------------------------


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 *



# <COUNTER
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):
pygame.sprite.Sprite.__init__(self)

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
# COUNTER>


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

# Initialization
pygame.init()

clock = pygame.time.Clock()
screen = pygame.display.set_mode((120, 120))

counters = pygame.sprite.RenderPlain()
for i in range(0, 3):
vCounter = Counter(i)
vCounter.add(counters)

# <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):
pygame.quit()
return

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
mouse"
#break # Uncomment to return the *lowest*
sprite

if vIndex != None:
print vIndex

counters.draw(screen)
pygame.display.update()
# Infinite loop>
# MAIN>



if __name__ == '__main__': main()
Kent Johnson
2008-01-11 17:22:07 UTC
Permalink
Post by James Newton
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.
I would think so too, but a quick look at the docs and tutorials didn't
turn up anything....
Post by James Newton
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
vRange = range(len(counters))
vRange.reverse()
vCounter = counters[ii]
#TypeError: 'Group' object is unindexable
I guess counters is iterable but not a sequence. Try this:
for vCounter in reversed(list(counters)):

Kent
James Newton
2008-01-11 17:36:38 UTC
Permalink
Hi Kent,

Thanks for your help. This solves my problem:

vPos = pygame.mouse.get_pos()
for vCounter in reversed(list(counters)):
if vCounter.rect.collidepoint(*vPos):
# vCounter now points to the sprite we want to treat
break

:-)

James

Loading...