1

I'm trying to make multiple sprites that all come from the same pygame sprite class.

class animatedSprites(pygame.sprite.Sprite):
    def __init__(self, spriteName):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((width,height))
        self.images = []
        self.images.append(spriteName[0])
        self.rect = self.image.get_rect() #sets sprites size and pos
        self.rect.center = (POSITIONx1[4], POSITIONy1[4])
        self.animation_frames = 12
        self.current_frame = 0
        self.previous_positiony = self.rect.y
        self.previous_positionx = self.rect.x

    def update(self):
        global MOUSEPRESSED
        if (MOUSEPRESSED == 1):
            self.rect = self.image.get_rect()
            self.rect.y = POSITIONy1[get_square_under_mousey()] - 25
            self.rect.x = POSITIONx1[get_square_under_mousex()] - 25
        if (MOUSEPRESSED == 2):
            if collide(plant, bought2):
                self.rect.y = self.previous_positiony
                self.rect.x = self.previous_positionx
            else:
                self.rect = self.image.get_rect()
                self.rect.y = POSITIONy1[get_square_under_mousey()] + 35#pulled x and y pos from func
                self.rect.x = POSITIONx1[get_square_under_mousex()] - 25
                self.previous_positiony = self.rect.y
                self.previous_positionx = self.rect.x
            MOUSEPRESSED = 0

I use this class to create a sprite on the screen, then use the def update() to control the sprite. The way the sprite is controlled is that when the user clicks on the sprite they can drag it around with their mouse and move it where ever they'd like on the screen. The problem is, if I use this class to create a second sprite and have two simultaneously on the screen, when the user "picks up" one sprite, they both move to the mouse position. I would like to make only one move depending on which is being clicked.

I'm assuming they're both moving to the same position because they both use the same def update() to decide their movement, so my question is, is there anyway in pygame or python to make the one being clicked on to move and not both, without creating a second class animatedSprites(pygame.sprite.Sprite): to make it. I would like to have multiple sprites on the screen at once but don't want to make dozens of individual classes and update() defs to control each one separately.

Sorry if this doesn't make sense, I'm a beginner to both python and pygame.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 2
    Why would you have to make a separate sprite class for each one that could appear – Natecat Oct 18 '20 at 23:34
  • because I don't know any other way to make multiple sprites that I can move differently in one class, since they all use the same update() function. –  Oct 18 '20 at 23:46
  • Are you confusing object definition versus instantiation? Perhaps answers to this [question](https://stackoverflow.com/q/40345443) might be helpful. – import random Oct 18 '20 at 23:55
  • 1
    Is the logic for each different? It sounds like they all are controlled in the same way. – Natecat Oct 18 '20 at 23:57
  • They are all controlled the same way, but my current setup is controlling them all at once, but I would like to just control one at a time. –  Oct 19 '20 at 00:07
  • You would need to check which sprite the mouse is over, and only move/update that one. – Natecat Oct 19 '20 at 23:46
  • Right, but my question is how do I differentiate which one is being clicked and only update that one when they all use the same update() function? I want them all to move the same way when dragged, but when I drag one they all drag. Everything I've tried to check which is being moused over, still moves all of them or just crashes the program. –  Oct 21 '20 at 01:10

2 Answers2

3

I recommend to create a class DragOperator which can drag an pygame.Rect object:

class DragOperator:
    def __init__(self, sprite):
        self.sprite = sprite
        self.dragging = False
        self.rel_pos = (0, 0)
    def update(self, event_list):
        for event in event_list:
            if event.type == pygame.MOUSEBUTTONDOWN:
                self.dragging = self.sprite.rect.collidepoint(event.pos)
                self.rel_pos = event.pos[0] - self.sprite.rect.x, event.pos[1] - self.sprite.rect.y
            if event.type == pygame.MOUSEBUTTONUP:
                self.dragging = False
            if event.type == pygame.MOUSEMOTION and self.dragging:
                self.sprite.rect.topleft = event.pos[0] - self.rel_pos[0], event.pos[1] - self.rel_pos[1]

The dragging of the rectangle is implemented in the update method. Use this class in a pygame.sprite.Sprite object. Pass the list of events to the update method of the Sprite and delegate it to the drag operator:

class animatedSprites(pygame.sprite.Sprite):
    def __init__(self, spriteName):
        # [...]

        self.drag = DragOperator(self)

    def update(self, event_list):
        self.drag.update(event_list) 

Pass the list of event form the main application loop to the pygame.sprite.Group:

all_sprites = pygame.sprite.Group()
all_sprites.add(animatedSprites("my_sprite"))

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

    all_sprites.update(event_list)

    # [...]

Minmal example: repl.it/@Rabbid76/PyGame-MouseDrag

import pygame

class DragOperator:
    def __init__(self, sprite):
        self.sprite = sprite
        self.dragging = False
        self.rel_pos = (0, 0)
    def update(self, event_list):
        for event in event_list:
            if event.type == pygame.MOUSEBUTTONDOWN:
                self.dragging = self.sprite.rect.collidepoint(event.pos)
                self.rel_pos = event.pos[0] - self.sprite.rect.x, event.pos[1] - self.sprite.rect.y
            if event.type == pygame.MOUSEBUTTONUP:
                self.dragging = False
            if event.type == pygame.MOUSEMOTION and self.dragging:
                self.sprite.rect.topleft = event.pos[0] - self.rel_pos[0], event.pos[1] - self.rel_pos[1]

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, color):
        super().__init__() 
        self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.original_image, color, (25, 25), 25)
        self.drag_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.drag_image, color, (25, 25), 25)
        pygame.draw.circle(self.drag_image, (255, 255, 255), (25, 25), 25, 4)
        self.image = self.original_image 
        self.rect = self.image.get_rect(center = (x, y))
        self.drag = DragOperator(self)
    def update(self, event_list):
        self.drag.update(event_list) 
        self.image = self.drag_image if self.drag.dragging else self.original_image

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

sprite_object = SpriteObject(*window.get_rect().center, (255, 255, 0))
group = pygame.sprite.Group([
    SpriteObject(window.get_width() // 3, window.get_height() // 3, (255, 0, 0)),
    SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 255, 0)),
    SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 255)),
    SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (255, 255, 0)),
])

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(event_list)

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

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
0

For sprite management, a Sprite Group will help you.

So during setup, do something like:

all_sprites = pygame.sprite.Group()
# create five initial sprites
for number in range(5):
    new_sprite = animatedSprites(f"Sprite {number}")
    all_sprites.add(new_sprite)

Then during your event handling:

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        # set termination condition here
        …
    elif event.type == pygame.MOUSEBUTTONDOWN:
        # use the click position in the name
        new_sprite = animatedSprites(f"Sprite X: {event.pos[0]} Y: {event.pos[1]}")
        all_sprites.add(new_sprite)

After processing events:

# update game state
all_sprites.update()
# draw background
…
# draw sprites
all_sprites.draw()
# update display
pygame.display.update()


            
import random
  • 3,054
  • 1
  • 17
  • 22