James Newton
2008-01-11 16:53:35 UTC
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()
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()