Drag multiple sprites with different "update ()" methods from the same Sprite class in Pygame
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.
Solution 1:
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()