Drag only one sprite in a group with the mouse - python-3.x

The problem that I cannot figure how to sort is that when I drag a piece over another the second piece also is dragged along with the first. I have tried several ways to limit the mouse selection to one piece at at time, but all have failed. Can anyone help - no doubt there is a simple way! The code shorn of all my failed attempts follows:
# In main loop:
# Watch for keyboard and mouse events
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_held = True
if event.type == pygame.MOUSEBUTTONUP:
mouse_held = False
# Update pieces that are in a sprite.Group()
pieces.update(mouse_held)
# In sprite class:
def update(self, mouse_held):
if mouse_held == True:
self.mouse_coordinates = pygame.mouse.get_pos()
if self.rect.collidepoint(self.mouse_coordinates) == True:
self.rect.centerx = self.mouse_coordinates[0]
self.rect.centery = self.mouse_coordinates[1]

Your question is fairly difficult but by doing all of this you should be able t achieve what you want.
You should change your sprite class to have a new variable of type int named depth (the higher the value the 'deeper' it is).
Considering you have a list of all the sprite objects you want to check for clicks called spriteList you should add this:
from operator import attrgetter
then change these lines:
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_held = True
to:
if event.type == pygame.MOUSEBUTTONDOWN:
sprites = []
for sprite in spriteList:
if sprite.rect.collidepoint(event.pos) == True:
sprites.append(sprite)
active = min(lists, key=attrgetter('depth'))
mouse_held = True
You should replace the sprite's update function to:
def update(self):
self.mouse_coordinates = pygame.mouse.get_pos()
if self.rect.collidepoint(self.mouse_coordinates) == True:
self.rect.centerx = self.mouse_coordinates[0]
self.rect.centery = self.mouse_coordinates[1]
And finally when you want to update the sprite's position simply type:
if mouse_held:
active.update()

Related

PyGame. Second composition in modul "mixer.music. doesnt playing, but first plays normally [duplicate]

I tried to use python to display image:
import pygame
win = pygame.display.set_mode((500, 500))
DisplayImage("Prologue.jpg", win)
And when it runs, nothing happened. It also happened for
DisplayImage("Streets.jpg", win)
However, when I tried the exact same thing later on in the code, it ran perfectly.
I checked, the picture was in the same folder as the .py file, and I didn't type the name wrong.
The function is:
def DisplayImage(imageName, screen):
screen.fill((0, 0, 0))
Image = pygame.image.load(imageName).convert()
screen_rect = screen.get_rect()
Image_rect = Image.get_rect().fit(screen_rect)
Image = pygame.transform.scale(Image, Image_rect.size)
screen.blit(Image, [0, 0])
pygame.display.update()
Update:
I commented out all of the lines and copy and pasted that line out so it's the only line that runs. It runs perfectly.
Update 2:
Found the issue. The reason it doesn't work was that the pygame window was "not responding". I don't know what caused it to not respond, but in one of the test runs I didn't make it show "not responding", and the images were loaded fine. The "not responding" always shows up when I type in my player name, and the function looks like this:
def createName():
playerName = input("Enter the player name\n")
desiredName = input("Is "+playerName+" the desired name?[1]Yes/[2]No\n")
if desiredName == "1":
return playerName
elif desiredName == "2":
playerName = createName()
Sometimes when I type the player name nothing happens, and the letters only show up after a while. If this happens, the pygame window is bound to not respond.
You cannot use input in the application loop. input waits for an input. While the system is waiting for input, the application loop will halt and the game will not respond.
Use the KEYDOWN event instead of input:
run = True
while run:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if pygame.key == pygame.K_1:
# [...]
if pygame.key == pygame.K_2:
# [...]
Another option is to get the input in a separate thread.
Minimal example:
import pygame
import threading
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
color = "red"
def get_input():
global color
color = input('enter color (e.g. blue): ')
input_thread = threading.Thread(target=get_input)
input_thread.start()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window_center = window.get_rect().center
window.fill(0)
pygame.draw.circle(window, color, window_center, 100)
pygame.display.flip()
pygame.quit()
exit()

Hi, I'm looking for a fix to having multiple buttons in Pygame

For context, I've just gotten into Pygame, and I'm trying to learn how to use buttons. Of course, there's making the classes or functions and likewise calling them to work. However, when I open a new screen, I'd like there to be a new button, which then only works there.
My issue comes in after the second scene, where I'm unsure how to call a second button, or a third etc. If I put a return in a function where my button is, and check whether that function has collided with a point, the whole screen becomes a button which isn't ideal. So I'm looking for a fix.
Here's the chunk causing issues, and I'd love any advice on optimising it. My guess is that HeroCreation()[1] is causing issues, but I'm not entirely sure how else to make multiple buttons work simultaenously.
def TextCreation(font, text, coords, screen):
label = font.render(text, True, BLACK)
labelRect = label.get_rect()
labelRect.topright = coords
window.blit(label, labelRect)
return(label, labelRect)
StartButton = TextCreation(largeFont, 'Start Game', (rect_centerx+140, rect_centery - 250), window)
QuitButton = TextCreation(largeFont, 'Quit Game', (rect_centerx+140, rect_centery + 150), window)
def HeroCreation():
window.fill(WHITE)
Greeting = TextCreation(largeFont, 'Welcome, Hero!', (rect_centerx+210, rect_centery-200), window)
Intro1 = TextCreation(paraFont, "I presume you're caught up, but just in case:", (rect_centerx+250, rect_centery + 50), window)
Intro2 = TextCreation(paraFont, "The Rat King's wrecking our kingdom, and you're the only one who can find the Orb of Power.", (rect_centerx+500, rect_centery + 100), window)
Intro3 = TextCreation(paraFont, "Deal with him once and for all! Do us proud, hero.",(rect_centerx+260, rect_centery + 150), window)
pygame.draw.rect(window, LIGHT_GREEN, (rect_centerx - 170, rect_centery-80, 400, 70), 0)
ProceedtoMapButton = TextCreation(paraFont, "Are you ready?", (rect_centerx+110, rect_centery-70), window)
return (ProceedtoMapButton)
pygame.display.flip()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mouse = pygame.mouse.get_pos()
if QuitButton[1].collidepoint(mouse):
pygame.quit()
sys.exit()
if StartButton[1].collidepoint(mouse):
HeroCreation()
if HeroCreation()[1].collidepoint(mouse):
drawGrid(width, height, window)
if event.type == QUIT:
pygame.quit()
sys.exit()
For each screen, create new buttons, then remove them when the screen exits. In your loop, check if the button is on the screen before the collision check.
Try this code:
HeroButton = None
while run:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
mouse = pygame.mouse.get_pos()
if QuitButton[1].collidepoint(mouse): # quit button on every screen ?
pygame.quit()
sys.exit()
if StartButton != None and StartButton[1].collidepoint(mouse):
StartButton = None # done with this button
HeroButton = HeroCreation()
elif HeroButton != None and HeroButton[1].collidepoint(mouse):
HeroButton = None # done with this button
drawGrid(width, height, window) # start game?
if event.type == QUIT:
pygame.quit()
sys.exit()

How to regulate a function in python under condition?

I have a function in pygame, which I have to run when the user presses space. However, the problem is that as soon as space is pressed, the function runs much more than once. I want it to run once, and if the user presses space again, I want the function to run again - once.
Some code below
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
God()
Now God() draws a bunch of shapes at the same time when I want it to draw one shape at a time, everytime the user presses space. God() is only called once. It picks a random number from 1 to 100 in order to make probabilities for each shape.
A Minimal, Complete and Verifiable Example is required for more specific assistance with your issue.
Below is an example that shows the following:
Holding down the Spacebar to continually add sprites (coloured rectangles). The key down events are repeated by calling pygame.key.set_repeat().
Holding down the Enter key will continually add sprites, but limited to once every five seconds
Pressing and releasing the Tab key will generate a single sprite.
Let me know if you have any questions.
import random
import pygame
import time
screen_width, screen_height = 640, 480
def get_random_position():
"""return a random (x,y) position in the screen"""
return (random.randint(0, screen_width - 1), #randint includes both endpoints.
random.randint(0, screen_height - 1))
def get_random_named_color(allow_grey=False):
"""return one of the builtin colors"""
if allow_grey:
return random.choice(all_colors)
else:
return random.choice(non_grey)
def non_grey(color):
"""Return true if the colour is not grey/gray"""
return "grey" not in color[0] and "gray" not in color[0]
all_colors = list(pygame.colordict.THECOLORS.items())
# convert color dictionary to a list for random selection once
non_grey = list(filter(non_grey, all_colors))
class PowerUp(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
width, height = 12, 10
self.color, color = get_random_named_color()
self.image = pygame.Surface([width, height])
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
# Update the position of this object by setting the values of rect.x and rect.y
self.rect = self.image.get_rect().move(*get_random_position())
def update(self):
#move to a random position
self.rect.center = get_random_position()
if __name__ == "__main__":
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Sprite Collision Demo')
clock = pygame.time.Clock() #for limiting FPS
FPS = 60
exit_demo = False
regulating = False
reg_start = 0
pygame.key.set_repeat(300, 200)
#pygame.mouse.set_cursor(*pygame.cursors.ball)
#create a sprite group to track the power ups.
power_ups = pygame.sprite.Group()
for _ in range(10):
power_ups.add(PowerUp()) # create a new power up and add it to the group.
# main loop
while not exit_demo:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_demo = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
exit_demo = True
elif event.key == pygame.K_SPACE:
power_ups.add(PowerUp())
#power_ups.update()
elif event.key == pygame.K_RETURN:
if not regulating:
reg_start = time.time()
power_ups.add(PowerUp())
regulating = True
else:
# limit to five seconds intervals
elapsed_ms = time.time() - reg_start
##print("Regulating", elapsed_ms)
if elapsed_ms > 5:
regulating = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_TAB:
power_ups.add(PowerUp())
elif event.type == pygame.MOUSEBUTTONUP:
for _ in range(10):
power_ups.add(PowerUp())
# check for collision
for p in power_ups:
if p.rect.collidepoint(pygame.mouse.get_pos()):
power_ups.remove(p)
print(f"Removed {p.color} power up")
# clear the screen with a black background
screen.fill(pygame.Color("black"))
# draw sprites
power_ups.draw(screen)
# update the surface
pygame.display.update()
# limit the frame rate
clock.tick(FPS)
pygame.quit()
quit()

how to store multiple mouse click coordinates python

so Ive been trying to find a way to store multiple click x and y's with no luck, Ive worked with pygame and opencv but I cannot find a way to store multiple x and y's without deleting the previous set.
import pygame
pygame.init()
while True:
for e in pygame.event.get():
if e.type == pygame.MOUSEBUTTONDOWN:
print (pygame.mouse.get_pos())
Just append the event.pos or pygame.mouse.get_pos() to a list or collections.deque if the size should be limited.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
clicks = []
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
elif event.type == pg.MOUSEBUTTONDOWN:
clicks.append(event.pos)
print(clicks)
screen.fill((30, 30, 30))
pg.display.flip()
clock.tick(30)
pg.quit()

Issues with surface.fill () in pygame

My program allows the an image to follow my mouse cursor but I am unable to draw the circle with the the Attack method because i have to suface.fill in the move method ( the move method is followMeLittleBoy) I can get the circle to draw for a split second but only while moving and it just barley. This is my full code other than my imports and such
class Hero ():
def __init__(self):
self.dead = False
pygame.mouse.set_visible(False)
self.X_MOVE_AMT = 5
self.Y_MOVE_AMT = 5
self.space = pygame.image.load ("hero_sprite.jpg")
self.spaceRect = self.space.get_rect()
self.spaceRect.topleft = (100,100)
surface.blit (self.space, self.spaceRect)
pygame.display.update()
def followMeLittleBoy (self):
amntTuple = pygame.mouse.get_pos()
self.X_MOVE_AMT = math.ceil((amntTuple[0] - self.spaceRect.left)*0.2)
self.Y_MOVE_AMT = math.ceil((amntTuple[1] - self.spaceRect.top)*0.2)
self.spaceRect.left += self.X_MOVE_AMT
self.spaceRect.top += self.Y_MOVE_AMT
surface.blit (self.space, self.spaceRect)
pygame.display.update()
def Attack (self):
surface.fill ((255,255,255))
amntTuple = pygame.mouse.get_pos()
pygame.draw.circle(surface, pygame.Color(0,0,255), amntTuple, 20, 2)
surface.blit (self.space, self.spaceRect)
var = Hero ()
while True:
surface.fill((255,255,255))
for event in pygame.event.get():
var.followMeLittleBoy ()
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_SPACE:
var.Attack()
In a previous question I recommended you to update the state during events and call the draw routines from the main loop only once per iteration, this is specially important for the surface.fill call.
Now I strongly recommend to follow that aproach, otherwise this kind of problems will continue to arise.
There are few things that you need to fix.
var.followMeLittleBoy() should be called once per loop, not for every event.
You should have a seperate method for drawing in your Hero class.
Call pygame.display.update() only once per loop.
I am not sure what you are trying to accomplish, but you could create a list of circles that are to be drawn, and when you press spacebar, a circle is added to a list.
Then you can loop your circle list,and draw each of them without them dissapearing.

Resources