pygame, getting a sprite to move to cursor position on click - pygame

I was trying to make a tower defense sort of game in pygame. When I click on the screen, I want it to be able to move the tower sprite to the position. Currently, when I run the program, it moves the sprite to the cursor with out clicking, then does not let me move it again.
import pygame
import time
pygame.init()
WINDOWWIDTH = 1800
WINDOWHEIGHT = 1800
screen = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
background_colour = (188,69,80)
GAMETITLE = "Tower Defence"
def main():
pygame.display.set_caption(GAMETITLE)
clock = pygame.time.Clock()
spritegroup =pygame.sprite.Group()
sprite =pygame.sprite.Sprite()
tower = sprite.image = pygame.image.load("tower.png")
sprite.image = tower
sprite.rect = sprite.image.get_rect()
sprite.rect.x = 10
sprite.rect.y = 10
sprite.add(spritegroup)
while True:
screen.fill(background_colour)
pygame.draw.rect(screen, (255,255,255), ((0, 100), (1100, 90)))
pygame.draw.rect(screen, (255, 255,255), ((1010, 100), (100, 600)))
pygame.draw.rect(screen, (255,255,255), ((1010, 700), (2400, 90)))
spritegroup.draw(screen)
pygame.display.update()
clock.tick(30)
if pygame.mouse.get_pressed():
cursorPos = pygame.mouse.get_pos()
sprite.rect.x = cursorPos[0]
sprite.rect.y = cursorPos[1]
main()

what is happening is that pygame.mouse.get_pressed() returns a tuple. if statements return true if they are not null. this means that regardless if it is pressed it will return True. then the pygame event queue will fill with mouse events and never get processed. your event loop should look something like:
for event in pygame.event.get():
if event.type == pygame.event.QUIT:
pygame.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
#stuff to do on click

Related

How can i move the Instances of the game on shifted y axis? [duplicate]

I'm a beginner programmer who is starting with python and I'm starting out by making a game in pygame.
The game basically spawns circles at random positions and when clicked, it gives you points.
Recently I've hit a roadblock when I want to spawn multiple instances of the same object (in this case circles) at the same time.
I've tried stuff like sleep() and some other code related to counters, but it always results in the next circle spawned overriding the previous one (i.e the program spawns circle 1, but when circle 2 comes in, circle 1 disappears).
Does anyone know a solution to this? I would really appreciate your help!
import pygame
import random
import time
pygame.init()
window = pygame.display.set_mode((800,600))
class circle():
def __init__(self, color, x, y, radius, width,):
self.color = color
self.x = x
self.y = y
self.radius = radius
self.width = width
def draw(self, win, outline=None):
pygame.draw.circle(win, self.color, (self.x, self.y, self.radius, self.width), 0)
run=True
while run:
window.fill((0, 0, 0))
pygame.draw.circle(window, (255, 255, 255), (random.randint(0, 800),random.randint(0, 600)), 20, 20)
time.sleep(1)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run=False
pygame.quit()
quit()
It does not work that way. time.sleep, pygame.time.wait() or pygame.time.delay is not the right way to control time and gameplay within an application loop. The game does not respond while you wait. The application loop runs continuously. You have to measure the time in the loop and spawn the objects according to the elapsed time.
pygame.Surface.fill clears the entire screen. Add the newly created objects to a list. Redraw all of the objects and the entire scene in each frame.
See also Time, timer event and clock
You have 2 options. Use pygame.time.get_ticks() to measure the time. Define a time interval after which a new object should appear. Create an object when the point in time is reached and calculate the point in time for the next object:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
next_object_time = 0
while run:
# [...]
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
Minimal example:
repl.it/#Rabbid76/PyGame-TimerSpawnObjects
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
next_object_time = 0
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()
The other option is to use the pygame.event module. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. The time has to be set in milliseconds. e.g.:
object_list = []
time_interval = 500 # 500 milliseconds == 0.1 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
Note, in pygame customer events can be defined. Each event needs a unique id. The ids for the user events have to be between pygame.USEREVENT (24) and pygame.NUMEVENTS (32). In this case pygame.USEREVENT+1 is the event id for the timer event.
Receive the event in the event loop:
while run:
for event in pygame.event.get():
if event.type == timer_event:
object_list.append(Object())
The timer event can be stopped by passing 0 to the time argument of pygame.time.set_timer.
Minimal example:
repl.it/#Rabbid76/PyGame-TimerEventSpawn
import pygame, random
pygame.init()
window = pygame.display.set_mode((300, 300))
class Object:
def __init__(self):
self.radius = 50
self.x = random.randrange(self.radius, window.get_width()-self.radius)
self.y = random.randrange(self.radius, window.get_height()-self.radius)
self.color = pygame.Color(0)
self.color.hsla = (random.randrange(0, 360), 100, 50, 100)
object_list = []
time_interval = 200 # 200 milliseconds == 0.2 seconds
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, time_interval)
run = True
clock = pygame.time.Clock()
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
object_list.append(Object())
window.fill(0)
for object in object_list[:]:
pygame.draw.circle(window, object.color, (object.x, object.y), round(object.radius))
object.radius -= 0.2
if object.radius < 1:
object_list.remove(object)
pygame.display.flip()
pygame.quit()
exit()

How come when I use if .collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0] for a button, it only stays there while I hold it? [duplicate]

I was wondering how to write code that would detect the mouse clicking on a sprite. For example:
if #Function that checks for mouse clicked on Sprite:
print ("You have opened a chest!")
I assume your game has a main loop, and all your sprites are in a list called sprites.
In your main loop, get all events, and check for the MOUSEBUTTONDOWN or MOUSEBUTTONUP event.
while ... # your main loop
# get all events
ev = pygame.event.get()
# proceed events
for event in ev:
# handle MOUSEBUTTONUP
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
# get a list of all sprites that are under the mouse cursor
clicked_sprites = [s for s in sprites if s.rect.collidepoint(pos)]
# do something with the clicked sprites...
So basically you have to check for a click on a sprite yourself every iteration of the mainloop. You'll want to use mouse.get_pos() and rect.collidepoint().
Pygame does not offer event driven programming, as e.g. cocos2d does.
Another way would be to check the position of the mouse cursor and the state of the pressed buttons, but this approach has some issues.
if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()):
print ("You have opened a chest!")
You'll have to introduce some kind of flag if you handled this case, since otherwise this code will print "You have opened a chest!" every iteration of the main loop.
handled = False
while ... // your loop
if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()) and not handled:
print ("You have opened a chest!")
handled = pygame.mouse.get_pressed()[0]
Of course you can subclass Sprite and add a method called is_clicked like this:
class MySprite(Sprite):
...
def is_clicked(self):
return pygame.mouse.get_pressed()[0] and self.rect.collidepoint(pygame.mouse.get_pos())
So, it's better to use the first approach IMHO.
The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released. The pygame.event.Event() object has two attributes that provide information about the mouse event. pos is a tuple that stores the position that was clicked. button stores the button that was clicked. Each mouse button is associated a value. For instance the value of the attributes is 1, 2, 3, 4, 5 for the left mouse button, middle mouse button, right mouse button, mouse wheel up respectively mouse wheel down. When multiple keys are pressed, multiple mouse button events occur. Further explanations can be found in the documentation of the module pygame.event.
Use the rect attribute of the pygame.sprite.Sprite object and the collidepoint method to see if the Sprite was clicked.
Pass the list of events to the update method of the pygame.sprite.Group so that you can process the events in the Sprite class:
class SpriteObject(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
# [...]
my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)
# [...]
run = True
while run:
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
run = False
group.update(event_list)
# [...]
Minimal example: repl.it/#Rabbid76/PyGame-MouseClick
import pygame
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.click_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.click_image, color, (25, 25), 25)
pygame.draw.circle(self.click_image, (255, 255, 255), (25, 25), 25, 4)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.clicked = False
def update(self, event_list):
for event in event_list:
if event.type == pygame.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
self.clicked = not self.clicked
self.image = self.click_image if self.clicked else self.original_image
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 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()
See further Creating multiple sprites with different update()'s from the same sprite class in Pygame
The current position of the mouse can be determined via pygame.mouse.get_pos(). The return value is a tuple that represents the x and y coordinates of the mouse cursor. pygame.mouse.get_pressed() returns a list of Boolean values ​​that represent the state (True or False) of all mouse buttons. The state of a button is True as long as a button is held down. When multiple buttons are pressed, multiple items in the list are True. The 1st, 2nd and 3rd elements in the list represent the left, middle and right mouse buttons.
Detect evaluate the mouse states in the Update method of the pygame.sprite.Sprite object:
class SpriteObject(pygame.sprite.Sprite):
# [...]
def update(self, event_list):
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
if self.rect.collidepoint(mouse_pos) and any(mouse_buttons):
# [...]
my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)
# [...]
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
group.update(event_list)
# [...]
Minimal example: repl.it/#Rabbid76/PyGame-MouseHover
import pygame
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.hover_image = pygame.Surface((50, 50), pygame.SRCALPHA)
pygame.draw.circle(self.hover_image, color, (25, 25), 25)
pygame.draw.circle(self.hover_image, (255, 255, 255), (25, 25), 25, 4)
self.image = self.original_image
self.rect = self.image.get_rect(center = (x, y))
self.hover = False
def update(self):
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
#self.hover = self.rect.collidepoint(mouse_pos)
self.hover = self.rect.collidepoint(mouse_pos) and any(mouse_buttons)
self.image = self.hover_image if self.hover else self.original_image
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 0)),
])
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
group.update()
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
The pygame documentation for mouse events is here.
You can either use the pygame.mouse.get_pressed method in collaboration with the pygame.mouse.get_pos (if needed).
Remember to use the mouse click event via a main event loop. The reason why the event loop is better is due to "short clicks". You may not notice these on normal machines, but computers that use tap-clicks on trackpads have excessively small click periods. Using the mouse events will prevent this.
EDIT:
To perform pixel perfect collisions use pygame.sprite.collide_rect() found on their docs for sprites.
I was looking for the same answer to this question and after much head scratching this is the answer I came up with:
# Python 3.4.3 with Pygame
from sys import exit
import pygame
pygame.init()
WIDTH = HEIGHT = 300
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Crash!')
# Draw Once
rectangle = pygame.draw.rect(window, (255, 0, 0), (100, 100, 100, 100))
pygame.display.update()
# Main Loop
while True:
# Mouse position and button clicking
pos = pygame.mouse.get_pos()
pressed1 = pygame.mouse.get_pressed()[0]
# Check if rectangle collided with pos and if the left mouse button was pressed
if rectangle.collidepoint(pos) and pressed1:
print("You have opened a chest!")
# Quit pygame
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()

AttributeError: 'pygame.Surface' object has no attribute 'get_rect' [duplicate]

This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 1 year ago.
I read an article about how a mouse cursor can detect a rect, and it includes the line ".get_rect()" but somehow it doesnt work
heres the articles code ->
import pygame
pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()
x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare , ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
if redSquare.get_rect().collidepoint(x, y):
print('clicked on image')
#loop over, quite pygame
pygame.quit()
heres my code ->
import pygame
import os
import sys
pygame.init()
width,height = (1100,800)
WIN = pygame.display.set_mode((width,height))
global bcard
bg_filename = os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','background.jpg')
bg = pygame.image.load(bg_filename)
bg = pygame.transform.scale(bg, (width, height)).convert()
card_width=130
card_height=160
blue_card=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','blue_card.png'))
red_card=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','red_card.png'))
bcard=pygame.transform.scale(blue_card,(card_width,card_height)).convert()
rcard=pygame.transform.scale(red_card,(card_width,card_height)).convert()
text=pygame.image.load(os.path.join('C:\\Users\\USER\\Desktop\\Python\\picture match','text.png'))
global clicking
clicking = False
def pictures():
global card1
card1=WIN.blit(bcard,(30,200))
card2=WIN.blit(rcard,(200,200))
card3=WIN.blit(bcard,(370,200))
card4=WIN.blit(rcard,(550,200))
card5=WIN.blit(bcard,(730,200))
card6=WIN.blit(rcard,(900,200))
card7=WIN.blit(rcard,(30,400))
card8=WIN.blit(bcard,(200,400))
card9=WIN.blit(rcard,(370,400))
card10=WIN.blit(bcard,(550,400))
card11=WIN.blit(rcard,(730,400))
card12=WIN.blit(bcard,(900,400))
card13=WIN.blit(bcard,(30,600))
card14=WIN.blit(rcard,(200,600))
card15=WIN.blit(bcard,(370,600))
card16=WIN.blit(rcard,(550,600))
card17=WIN.blit(bcard,(730,600))
card18=WIN.blit(rcard,(900,600))
card1_rect=pygame.Rect(30,200,130,160)
card1_rect=pygame.Rect(200,200,130,160)
card1_rect=pygame.Rect(370,200,130,160)
card1_rect=pygame.Rect(550,200,130,160)
card1_rect=pygame.Rect(730,200,130,160)
card1_rect=pygame.Rect(900,200,130,160)
WIN.blit(text,(25,0))
def draw():
WIN.blit(bg,(0,0))
pictures()
def main():
global clicking
global card1
global bcard
run = True
mx , my = pygame.mouse.get_pos()
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if bcard.get_rect().collidepoint(mx, my):
print('clicked on image')
draw()
pygame.display.flip()
main()
its suppose to be a picture match game btw, heres the error code "AttributeError: 'pygame.Surface' object has no attribute 'get_rect'"
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0) since a Surface object has no position. A Surface is blit at a position on the screen. The position of the rectangle can be specified by a keyword argument. For example, the top left of the rectangle can be specified with the keyword argument topleft:
if bcard.get_rect().collidepoint(mx, my):
if bcard.get_rect(topleft = (30, 200)).collidepoint(mx, my):
print('clicked on image')

How to stop cursor lag in Pygame?

I have created a shooting game using Pygame in which one's cursor turns into a cross-hair and moves around shooting static images. However, the lag on the cross-hair is simply unbearable as the image is continually redrawn and a new background imposed over it every clock tick.
My code is as below:
import pygame, sys, random
class Crosshair(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("ch.png").convert_alpha()
self.rect = self.image.get_rect()
self.gunshot = pygame.mixer.Sound("gs.wav")
def shoot(self):
self.gunshot.play()
pygame.sprite.spritecollide(crosshair, target_group, True)
def update(self):
self.rect.center = pygame.mouse.get_pos()
class Target(pygame.sprite.Sprite):
def __init__(self, pos_x, pos_y):
super().__init__()
self.image = pygame.image.load("al.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = [pos_x, pos_y]
pygame.init()
clock = pygame.time.Clock()
screen_width = 1920
screen_height = 1080
screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.image.load("hoc.png").convert_alpha()
pygame.mouse.set_visible(False)
crosshair = Crosshair()
crosshair_group = pygame.sprite.Group()
crosshair_group.add(crosshair)
target_group = pygame.sprite.Group()
for target in range(20):
new_target = Target(random.randrange(0, screen_width), random.randrange(0, screen_height))
target_group.add(new_target)
Running = True
while Running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
crosshair.shoot()
pygame.display.flip()
screen.blit(background, (0, 0))
target_group.draw(screen)
crosshair_group.draw(screen)
crosshair_group.update()
clock.tick(60)
Is there any way to make the cross-hair motion smoother? Most of the code is taken almost directly from a Youtube tutorial, but for some reason I am experiencing this problem when others are not.
It is a matter of indentation. You must update and draw the scene in the application loop rather than the event loop. The application loop is executed once per frame, but the event loop is only executed when an event occurse. Actually the call of clock.tick(60) slows down your application after each event instead of each frame:
while Running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
crosshair.shoot()
# INDENTATION
#<--|
pygame.display.flip()
screen.blit(background, (0, 0))
target_group.draw(screen)
crosshair_group.draw(screen)
crosshair_group.update()
clock.tick(60)

How to cycle 3 images on a rect button?

Just started learning Python/Pygame watching videos and reading to learn . I would like to see a example code to cycle 3 images on a rect button from a mouse press and return to first image. Really I want the pictures to be three options and return different results. So be able to cycle image be able to select option and option when triggered execute choice.
Example
import pygame
pygame.init()
screen = pygame.display.set_mode((300,200))
# three images
images = [
pygame.Surface((100,100)),
pygame.Surface((100,100)),
pygame.Surface((100,100)),
]
images[0].fill((255,0,0))
images[1].fill((0,255,0))
images[2].fill((0,0,255))
# image size and position
images_rect = images[0].get_rect()
# starting index
index = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# check mouse position and pressed button
if event.button == 1 and images_rect.collidepoint(event.pos):
# cycle index
index = (index+1) % 3
screen.blit(images[index], images_rect)
pygame.display.flip()
pygame.quit()
Example using class - to create many buttons
import pygame
class Button(object):
def __init__(self, position, size):
self._images = [
pygame.Surface(size),
pygame.Surface(size),
pygame.Surface(size),
]
self._images[0].fill((255,0,0))
self._images[1].fill((0,255,0))
self._images[2].fill((0,0,255))
self._rect = pygame.Rect(position, size)
self._index = 0
def draw(self, screen):
screen.blit(self._images[self._index], self._rect)
def event_handler(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and self._rect.collidepoint(event.pos):
self._index = (self._index+1) % 3
pygame.init()
screen = pygame.display.set_mode((320,110))
button1 = Button((5, 5), (100, 100))
button2 = Button((110, 5), (100, 100))
button3 = Button((215, 5), (100, 100))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
button1.event_handler(event)
button2.event_handler(event)
button3.event_handler(event)
button1.draw(screen)
button2.draw(screen)
button3.draw(screen)
pygame.display.flip()
pygame.quit()
If I understood the question correctly, you need a single button that changes the look every time you click on it, and changes its relative function accordingly.
You should be able to solve your problem by creating a class that takes two list and a counter
1) list of images
2) list of functions
3) the counter tells you which image/function is selected.
The functions needs to be built in the class, but you can provide the image that you want in the class argument (actually, you could pass them as arguments, but I don't think is worth it).
Here is the code, I commented some lines with their intended meaning (in line comments)
import pygame
import sys
pygame.init()
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Magic Buttons")
background = pygame.Surface(screen.get_size())
clock = pygame.time.Clock()
class Buttons:
def __init__(self, posX, posY, image1, image2, image3):
self.image_list = [image1, image2, image3] # static list of images
self.function_list = [self.button_function_1,self.button_function_2,self.button_function_3 ]
self.rect_position = (posX, posY) # this is a tuple to identify the upper left corner of the rectangle of the image
self.button_type = 0 # initial value of the button, both for the function and the image
self.image = pygame.image.load(self.image_list[0]) #default image, index number 0 of image_list
self.rect = pygame.Rect(posX, posY, self.image.get_width(), self.image.get_height()) # create a rectangle object same size of the images
def check(self, pos):
if self.rect.collidepoint(pos) ==True:
self.change_button()
else:
pass
def change_button(self):
self.button_type = (self.button_type +1)%3
self.image = pygame.image.load(self.image_list[self.button_type ]) # load the image relative to button_type
self.function_list[self.button_type -1]() # execute the function relative to the new value of button_type
self.draw_button()
def draw_button(self):
screen.blit(self.image, self.rect_position) # blit the new button image
def button_function_1(self):
print ("function 1 in action")
def button_function_2(self):
print ("function 2 in action")
def button_function_3(self):
print ("function 3 in action")
multibutton = Buttons(100,100,"button1.png","button2.png","button3.png") # create an istance of the button in the x=100, y = 100, with the three image button
while True:
background.fill((0,0,0))
clock.tick(30)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos() # fetch the position of the mouse
multibutton.check(pos) # check if the mouse is on the button
multibutton.draw_button()
pygame.display.flip()