user3780831
user3780831

Reputation: 70

Having trouble getting clicked sprites in Pygame

I'm sorry if this question is a duplicate, but I've followed numerous pages on this site trying to find an answer, and I haven't been able to get one to work yet. Some of the pages I've used are:

Pygame mouse clicking detection

self.rect not found?

how does collide.rect work in pygame

It may be my lack of coding comprehension, and I apologize if that's the case. However, I found the most "helpful" page to be the first link. Using Sloth's first answer, it seemed very simple and something I would be able to do. Once his method did not work, I tried searching around even more for on YouTube and other sites. I also couldn't find an answer.

All I want to do is simply print "clicked" when a sprite is clicked. Here is my code.

import pygame
size = (700,500)
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
itunes = pygame.image.load('itunes.png')
screen.blit(itunes,(200,200))
sprites = [itunes]
while not done:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            clicked_sprites = [s for s in sprites if s.rect.collidepoint(pos)]
            for i in clicked_sprites:
                print('clicked sprite')
    pygame.display.flip()
    clock.tick(60)

I've learned very little of the Python language, but I found myself getting bored making the same text-based programs and would like to move on.

EDIT: I'm sorry, I should have stated what error I was getting. I'm getting: AttributeError: 'pygame.Surface' object has no attribute 'rect'

EDIT: Having trouble drawing the image on the screen

import pygame
size = (700,500)
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
white = (0,0,0)
image = 'itunes.png'
class Thing(pygame.sprite.Sprite):
    def __init__(self, image, x, y):
        self.image = pygame.image.load('itunes.png')
        self.x = x
        self.y = y

    @property
    def rect(self):
        return self.image.get_rect(topleft=(self.x, self.y))

itunes = Thing(image,200,200)

SpriteGroup = pygame.sprite.Group()    
SpriteGroup.add(itunes)                

while not done:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            clicked_sprites = [s for s in SpriteGroup if s.rect.collidepoint(pos)]
            for i in clicked_sprites:
                print('clicked sprite')
    screen.fill((0, 0, 0))             
    SpriteGroup.draw(screen)           
    pygame.display.flip()
    clock.tick(60)

Upvotes: 0

Views: 865

Answers (2)

user890167
user890167

Reputation:

Images are pygame.Surface objects, and they don't have a rect as an attribute. You can get one for that Surface object, however, by calling SurfaceObject.get_rect(). This will create a pygame.Rect object that represents the dimensions of the Surface, if not its position.

Depending on how often your object would create a new rect or modify it based on its position in the window, you might subclass pygame.sprite.Sprite and use a property to retrieve the Rect at its correct position. For example:

class Thing(pygame.sprite.Sprite):
    def __init__(self, image, x, y):
        super(Thing, self).__init__()    #don't forget to do this!! (like I did :D, haha)
        self.image = pygame.image.load(image)
        self.x = x
        self.y = y

    @property
    def rect(self):
        return self.image.get_rect(topleft=(self.x, self.y))

The surface.get_rect() call takes keyword arguments that allow you to set attributes of the object as it's being created. In this way, it's basically a one-liner for:

new_rect = self.image.get_rect()
new_rect.x = self.x
new_rect.y = self.y
return new_rect

This whole process returns a rect which is in the correct position in the window, such that it can be used for collision detection or just to see if it's the object that was clicked as in your example.

Another upshot of this approach (using a property for the rect) is that it makes the object work well with the pygame.sprite.Group.draw() method, which draws all Sprite objects in that Group object by blitting their obj.image and obj.rect attributes.

So you take advantage of this by turning sprites from a list to a sprite.Group object, and making the following changes to your code:

SpriteGroup = pygame.sprite.Group()    #use these for working with Sprite objects
SpriteGroup.add(itunes)                #add itunes to sprites Group
while not done:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            pos = pygame.mouse.get_pos()
            clicked_sprites = [s for s in sprites if s.rect.collidepoint(pos)]
            for i in clicked_sprites:
                print('clicked sprite')
    screen.fill((0, 0, 0))             #wipes the screen
    SpriteGroup.draw(screen)           #draws every Sprite object in this Group
    pygame.display.flip()
    clock.tick(60)

Upvotes: 1

Bartlomiej Lewandowski
Bartlomiej Lewandowski

Reputation: 11170

Let's take a look at what you code does.

  • You load a new image into the itunes variable.
  • You blit this image onto the screen at 200,200
  • You add the image into the sprite list.
  • When you click the mouse you loop the sprite list to check if the mouse point lies in the rect of the objects in the sprite list.

Now how do we know the position of the objects that are in the sprite list? The answer is that we don't. There is no information about the position of the object in the surface itself.

To solve this you can make your own Sprite class that will include a rect and a surface, or more preferably use pygame.sprite.Sprite which already includes rect.

Upvotes: 0

Related Questions