Reputation: 569
I'm attempting to make a near carbon copy of this game:
https://yppedia.puzzlepirates.com/Carpentry
It uses pentominos, which are objects made of 5 blocks. I have a Piece class, that stores each of these blocks in a list. When I click on a Block to move it, I'm also moving every other block that shares the same parent Piece object, so I can drag the entire piece around with my mouse.
The problem I'm having is that when I click one of these blocks, the piece moves away from my cursor. The dragging is fine, but I want it to follow the cursor more precisely.
I am using a Mouse class so I can implement simple collisions between mouse clicks and the Blocks, but I think this is the cause of my problems.
Edit: I could probably resolve this by hardcoding changes to the x and y positions of each block, but ideally I'd prefer a more modular solution because I think I'm misunderstanding how the mouse position works in pygame.
import pygame
import sys
import collections
import random
pygame.init()
FPS = 25
fpsClock = pygame.time.Clock()
# -----------SCREEN----------------#
WIN_WIDTH = 680 # width of window
WIN_HEIGHT = 500 # height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) # variable for screen display
DEPTH = 32 # standard
FLAGS = 0 # standard
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Carpentry')
# ---------------------------------#
# ---------------colours-----------#
WOOD = (182, 155, 76)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
# ---------------------------------#
blocks = pygame.sprite.Group()
pieces = []
class Block(pygame.sprite.Sprite):
def __init__(self, x, y, parent):
super().__init__()
self.image = pygame.Surface([15, 15])
self.colour = WOOD
self.parent = parent
self.image.fill(self.colour)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.original_x = x
self.original_y = y
self.drag = False
class Mouse(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface([15, 15])
self.colour = RED
self.image.fill(self.colour)
self.rect = self.image.get_rect()
self.rect.x = pygame.mouse.get_pos()[0]
self.rect.y = pygame.mouse.get_pos()[1]
def update_mouse(self):
self.rect.x = pygame.mouse.get_pos()[0]
self.rect.y = pygame.mouse.get_pos()[1]
class Piece:
def __init__(self):
self.blocks = []
self.c = 0
def insert(self, template):
for direction in template:
self.blocks.append([])
x = 0
y = 0
for line in direction:
for character in line:
if character == "O":
my_block = Block(x, y, self)
self.blocks[self.c].append(my_block)
blocks.add(my_block)
x += 15
y += 15
x = 0
self.c += 1
J_TEMPLATE = [['..O..',
'..O..',
'..O..',
'.OO..',
'.....']]
templates = {}
my_piece = Piece()
my_piece.insert(J_TEMPLATE)
pieces.append(my_piece)
mouse = Mouse()
while True:
screen.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
bool = pygame.sprite.spritecollide(mouse, blocks, False)
if len(bool) > 0:
target = bool[0]
target.drag = True
for block in blocks:
if block.parent == target.parent: #if blocks are part of the same piece
block.drag = True
else:
for block in blocks:
block.drag = False
mouse.update_mouse()
for block in blocks:
if block.drag == True:
block.rect.x = mouse.rect.x + block.original_x
block.rect.y = mouse.rect.y + block.original_y
blocks.draw(screen)
pygame.display.update()
fpsClock.tick(FPS)
Upvotes: 1
Views: 295
Reputation: 20478
I'd do it in this way: If a block was clicked, assign its parent piece to a variable and calculate the offsets for the blocks like so: block.offset = block.rect.topleft - Vec(event.pos)
. At the top of the file you need to import from pygame.math import Vector2 as Vec
. Now you can check for pygame.MOUSEMOTION
events and move the blocks to the new event.pos + block.offset
. The Mouse
class is not needed anymore.
selected = None
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if selected: # If a piece was selected ...
selected = None # Deselect it.
else:
# Find a colliding block.
collided_sprites = [block for block in blocks
if block.rect.collidepoint(event.pos)]
if collided_sprites: # If we clicked a block ...
# Select the parent of the block.
selected = collided_sprites[0].parent
# Calculate the offsets for the blocks.
for block in selected.blocks[0]:
block.offset = block.rect.topleft - Vec(event.pos)
elif event.type == pygame.MOUSEMOTION:
if selected:
# Update the block positions by adding their offsets to the mouse pos.
for block in selected.blocks[0]:
block.rect.topleft = event.pos + block.offset
screen.fill(BLACK)
blocks.draw(screen)
pygame.display.update()
fpsClock.tick(25)
Upvotes: 1