1

I have a sprite, and I want it to be so that when the mouse hovers over it, it places a small border around the sprite, and when it leaves, delete that border. How would I go about doing this?

Sprite Class:

class textureblock(pygame.sprite.Sprite):
  def __init__(self, imagefile):
    super(textureblock, self).__init__()
    self.surf = pygame.image.load(imagefile).convert_alpha()
    self.surf = pygame.transform.scale(self.surf, (48,48))
    self.rect = self.surf.get_rect()
  def die(self):
    self.kill()

I can add more code if required. Thank you!

Samuel
  • 11
  • 10

1 Answers1

2

Copy the image and draw a rectangle around it:

self.surf = pygame.transform.scale(self.surf, (48,48))
self.hover_surf = self.surf.copy()
pygame.draw.rect(self.hover_surf, (255, 255, 0), self.hover_surf.get_rect(), 6)

Check if the mouse is on the image with pygame.Rect.collidepoint and select the image:

self.hover = self.rect.collidepoint(pygame.mouse.get_pos())
self.image = self.hover_surf if self.hover else self.surf

This all is explained in the answers to the question Pygame mouse clicking detection.

Also note that pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.

You've to set the location of the rectangle, either by a keyword argument, e.g:

self.rect = self.surf.get_rect(topleft = (x, y))

Store the mouse position in an attribute of the object. kill the Sprite when the mouse hovers over the Sprite and the mouse position does not change for som frames (e.g. 10):

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, filename):
        # [...]

        self.hover = False
        self.mouse_pos = None
self.count = 0

    def update(self):
        # [...]

        mouse_pos = pygame.mouse.get_pos()
        if self.hover and mouse_pos == self.mouse_pos:
            self.count += 1
            if self.count > 10:
              self.kill()
        else:
            self.count = 0
        self.mouse_pos = mouse_pos

Minimal example

import pygame

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, filename):
        super().__init__() 
        self.original_image = pygame.image.load(filename).convert_alpha()
        self.hover_image = self.original_image.copy()
        pygame.draw.rect(self.hover_image, (255, 255, 0), self.hover_image.get_rect(), 6)
        self.image = self.original_image 
        self.rect = self.image.get_rect(center = (x, y))
        self.hover = False
        self.mouse_pos = None
        self.count = 0

    def update(self):
        mouse_pos = pygame.mouse.get_pos()
        self.hover = self.rect.collidepoint(mouse_pos)
        self.image = self.hover_image if self.hover else self.original_image
        if self.hover and mouse_pos == self.mouse_pos:
            self.count += 1
            if self.count > 10:
              self.kill()
        else:
            self.count = 0
        self.mouse_pos = mouse_pos

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

group = pygame.sprite.Group([
    SpriteObject(window.get_width() // 3, window.get_height() // 3, 'Apple.png'),
    SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, 'Banana.png'),
    SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, 'Pear.png'),
    SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, 'Plums.png'),
])

run = True
while run:
    clock.tick(60)
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            run = False 

    group.update()

    window.fill(0)
    group.draw(window)
    pygame.display.flip()

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • @Samuel [`kill`](https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite.kill) *"remove the Sprite from all Groups"*. You have to use a [`pygame.sprite.Group`](https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group). [`pygame.sprite.Sprite`](https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Sprite) and `pygame.sprite.Group` interact together. – Rabbid76 Dec 16 '21 at 20:44
  • Ahh, ok. I see ```window.fill(0)``` and it draws the sprites using the group. I tried this, the frame rate dropped though, probably from the constant clearing. – Samuel Dec 17 '21 at 13:54
  • @Samuel The frame rate drops when you load the images in every frame. Load the images only once (`pygame.image.load`). What is your frame rate? – Rabbid76 Dec 17 '21 at 13:59
  • 1
    Ok, thanks for the tip! I’ll try that later. – Samuel Dec 17 '21 at 14:05
  • Is it possible to change the image to just a filled square? I tried doing ```self.image = self.image.fill((0,191,255))```, but I got ```TypeError: Source objects must be a surface``` – Samuel Dec 17 '21 at 14:18
  • @Samuel Both images or just the image with the border? – Rabbid76 Dec 17 '21 at 14:29
  • If you just want a filled square you have to create a _Surface_ with a certain size: `self.image = pygame.Surface((100, 100))`. You can fill an existing _Surface_ with `self.image.fill((0,191,255))` (no `self.image =`). – Rabbid76 Dec 17 '21 at 14:33
  • Ahhh, ok. I will try that. – Samuel Dec 17 '21 at 14:36