MOUSEBUTTON events in pygame - python

I have made a sort of cookie clicker like game in pygame, however when I click the mouse once loads of points are added to the score. I assume this is because of the game loop, however I would like to know how to make this stop, and make it add 1 to the score for every click, no matter how long the mouse button is held down.

Here's an example that only increments the score on mouse button down events:
import pygame
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([320,240])
sys_font = pygame.font.SysFont(pygame.font.get_default_font(), 18)
pygame.display.set_caption("Clicker")
clicks = 0 # initialise the score counter
done = False
while not done:
#Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
clicks += 1
#Graphics
screen.fill(pygame.color.Color("white"))
score_txt = sys_font.render(f"Clicks: {clicks}", True, pygame.color.Color("blue"))
screen.blit(score_txt, (20, 220))
#Frame Change
pygame.display.update()
clock.tick(60)
pygame.quit()

Use 2 variables like is_mouse_clicked and was_mouse_clicked_previously.
In the initialization of the game (even before the first loop), assign False to both of them.
At the beginning of the game loop assign value of is_mouse_clicked to was_mouse_clicked_previously
Then, load the information whether the mouse button is being pressed to is_mouse_clicked variable
Then, add the point if the values of is_mouse_clicked and was_mouse_clicked_previously differ.
Option 1: Adding points inside if is_mouse_clicked and not was_mouse_clicked_previously: will increase the score right away (the moment you start pressing the button)
Option 2: Adding points inside if not is_mouse_clicked and was_mouse_clicked_previously: will increase the score a bit later (the moment you release the button)

Related

How to edit a variable in a different file from where it was declared?

I'm currently working on the Alien Invasion project within the book Python Crash Course. The issue is that the picture of my ship is not moving whenever I press the right arrow key (I removed the code for the left arrow key temporarily).
I know that my function to check for key-press events works as it prints out the expected values whenever the key is pressed and when it's not.
When it's being held down, the flag movingRight is changed from its default state of false to true. What should happen then is the variable 'center' is changed by adding a predetermined movement factor to it, then using that to print the ship image in its new position.
However, when I print the state of movingRight in the ship.py file, it shows that movingRight is still False even during a KEY_DOWN event.
Here is the loop that handles the events and changing the ship state. This is within the main file called alien_invasion.py:
#Create a new Ship
ship = Ship(aiSettings, screen)
#start the main loop for the game
while True:
#Check for events that affect the ship
gf.checkEvents(ship)
#Update the status of the ship based upon checkEvents()
ship.update()
#Update the screen after pulling data from ship.update()
gf.updateScreen(aiSettings, screen, ship)
Below is the checkEvents() function in a separate file called game_functions.py:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
print("Moving Right")
ship.moveRight = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
print("Done moving right")
ship.moveRight = False
Lastly, below is the code that edits the position of the ship. This is within ship.py:
def update(self):
print("movingRight state is: " + self.movingRight) #movingRight is false during KEY_DOWN event
#Check for a right move
if self.movingRight:
#Update the ship's center to the right x amount of pixels
self.center += self.aiSettings.shipSpeedFactor
self.rect.centerx = self.center
I understand that this book is a few years old now, so could it be that the method I'm using is outdated?
From what I can see in your code, it looks like you're using different names for the moving right variables. In your second code snip, you set ship.moveRight to True, but in your third code snip, you're checking if self.movingRight is True. Try changing it so both use .movingRight or both use .moveRight

Pygame: how to display an image until a key is pressed

I'm trying to build a game in Pygame where if a player moves onto a red square, the player loses. When this happens, I want to display a picture of an explosion where the player lost until the user presses any key on the keyboard. When the user presses a key, I'll call the function new_game() to start a new game. The issue is that my code seems to skip over the line where I blit the explosion, and instead just starts a new game right away.
I've tried using something like this, but I'm not sure what to put in the while loop (I want it to wait until there is a keypress):
while event != KEYDOWN:
# Not sure what to put here
If I put time.sleep() in the while loop, the whole program seems to freeze and no image is blitted.
Here's me loading the image into Pygame:
explosionpic = pygame.image.load('C:/Users/rohan/Desktop/explosion.png')
And here's where I call it/determine if a player has lost (program seems to skip over the screen.blit line because I don't even see the image at all):
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
screen.blit(explosionpic, (p1.x, p1.y))
# Bunch of other code goes here, like changing the score, etc.
new_game()
It's supposed to display the image, then when the user presses a key, call the new_game() function.
I'd appreciate any help.
The simplest solution which comes to my mind is to write a small independent function which delay the execution of the code. Something like:
def wait_for_key_press():
wait = True
while wait:
for event in pygame.event.get():
if event.type == KEYDOWN:
wait = False
break
This function will halt the execution until a KEYDOWN signal is catch by the event system.
So your code would be:
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
screen.blit(explosionpic, (p1.x, p1.y))
pygame.display.update() #needed to show the effect of the blit
# Bunch of other code goes here, like changing the score, etc.
wait_for_key_press()
new_game()
Add a state to the game, which indicates if the game is running, the explsoion happens or a ne game has to be started. Define the states RUN, EXPLODE and NEWGAME. Initialize the state game_state:
RUN = 1
EXPLODE = 2
NEWGAME = 3
game_state = RUN
If the explosion happens, the set the state EXPLODE
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
game_state = EXPLODE
When a key is pressed then switch to the state NEWGAME:
if game_state == EXPLODE and event.type == pygame.KEYDOWN:
game_state = NEWGAME
When newgame() was executed, then set game_state = RUN:
newgame()
game_state = RUN
Implement a separated case in the main loop for each state of the game. With this solution not any "sleep" is needed:
e.g.
ENDGAME = 0
RUN = 1
EXPLODE = 2
NEWGAME = 3
game_state = RUN
while game_state != ENDGAME:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_state = ENDGAME
if game_state == EXPLODE and event.type == pygame.KEYDOWN:
game_state = NEWGAME
if game_state == RUN:
# [...]
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
game_state = EXPLODE
# [...]
elif game_state == EXPLODE:
screen.blit(explosionpic, (p1.x, p1.y))
elif game_state == NEWGAME:
newgame()
game_state = RUN
pygame.display.flip()

Python, pygame mouse position and which button is pressed

I have been trying to get my code collecting which mouse button is pressed and its position yet whenever I run the below code the pygame window freezes and the shell/code keeps outputting the starting position of the mouse. Does anybody know why this happens and more importantly how to fix it?
(For the code below I used this website https://www.pygame.org/docs/ref/mouse.html and other stack overflow answers yet they were not specific enough for my problem.)
clock = pygame.time.Clock()
# Set the height and width of the screen
screen = pygame.display.set_mode([700,400])
pygame.display.set_caption("Operation Crustacean")
while True:
clock.tick(1)
screen.fill(background_colour)
click=pygame.mouse.get_pressed()
mousex,mousey=pygame.mouse.get_pos()
print(click)
print(mousex,mousey)
pygame.display.flip()
You have to call one of the pygame.event functions regularly (for example pygame.event.pump or for event in pygame.event.get():), otherwise pygame.mouse.get_pressed (and some joystick functions) won't work correctly and the pygame window will become unresponsive after a while.
Here's a runnable example:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
BG_COLOR = pygame.Color('gray12')
done = False
while not done:
# This event loop empties the event queue each frame.
for event in pygame.event.get():
# Quit by pressing the X button of the window.
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
# MOUSEBUTTONDOWN events have a pos and a button attribute
# which you can use as well. This will be printed once per
# event / mouse click.
print('In the event loop:', event.pos, event.button)
# Instead of the event loop above you could also call pygame.event.pump
# each frame to prevent the window from freezing. Comment it out to check it.
# pygame.event.pump()
click = pygame.mouse.get_pressed()
mousex, mousey = pygame.mouse.get_pos()
print(click, mousex, mousey)
screen.fill(BG_COLOR)
pygame.display.flip()
clock.tick(60) # Limit the frame rate to 60 FPS.

pygame have multiple of the same images

Hello I am trying to create towers for my Tower Defense game but every time i select a new tower the old one gets removed. I do not want this to be removed and I am sure there is a simple way to do this but I cannot find it. Here is my code. Thank you for any help.
def displayTower():
global bx, by
click = pygame.mouse.get_pressed()
Background.blit(redTower, (mx-bx,my-by))
Background.blit(redTower, (530,650))
while intro == 1:
mousePos = pygame.mouse.get_pos()
mousePressed = pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if 530 < mousePos[0] < 590 and 650 < mousePos[1] < 710:
if mousePressed[0] == 1:
clicked = True
if clicked == True:
mx, my = pygame.mouse.get_pos()
bx = 30
by = 30
if mousePressed[0] == 0:
Background.blit(redTower, (mx-bx,my-by))
tx = mx - bx
ty = my - by
clicked = False
displayTower()
For one thing, you are calling displayTower() outside of a while loop, so it never gets executed. So you are blitting only one tower at a time, not two.
You each time have to blit all the screen or blit a portion of it and update only the rects affected by the change.
Yes, what you blitted should stay on, but you cannot count on it without proper updating when you wish.
To be secure, you should use internal surface, then blit it over the screen surface when you are done blitting and drawing. Anyway, what should the background variable contain? A screen or your surface?
So the second thing is that you never update the screen. You have to use either pygame.display.flip() or pygame.display.update().
And, do use events to get mouse position, it's more clever. Also add a sleep or pygame.time.clock() to regulate fps, this is almost a busy loop what you wrote.

How can I detect if the user has double-clicked in pygame?

I know I can check if there was a left click
event.type == pygame.MOUSEBUTTONDOWN and event.button == LEFT
but how can I check if they double clicked? Also is there any way to check if the user moved the scroll wheel forward or backwards?
I'd just use the delta time value that clock.tick returns to increase a timer. In this example you have 0.5 seconds to double click otherwise the timer is reset.
import sys
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BLACK = pg.Color('black')
FONT = pg.font.Font(None, 32)
def game():
clock = pg.time.Clock()
timer = 0
dt = 0
running = True
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if timer == 0: # First mouse click.
timer = 0.001 # Start the timer.
# Click again before 0.5 seconds to double click.
elif timer < 0.5:
print('double click')
timer = 0
# Increase timer after mouse was pressed the first time.
if timer != 0:
timer += dt
# Reset after 0.5 seconds.
if timer >= 0.5:
print('too late')
timer = 0
screen.fill(BLACK)
txt = FONT.render(str(round(timer, 2)), True, (180, 190, 40))
screen.blit(txt, (40, 40))
pg.display.flip()
# dt == time in seconds since last tick.
# / 1000 to convert milliseconds to seconds.
dt = clock.tick(30) / 1000
if __name__ == '__main__':
game()
pg.quit()
sys.exit()
I've never used pygame - but:
Detecting double clicks: at a guess, instead of processing each click immediately, apply a 50ms delay and see if you get another click event in that time. The user probably won't notice the 50ms delay.
Distinguishing between scrollwheel up/down: see the comments on this documentation page. Apparently there are five buttons defined - left, middle, right, scrollwheel-up and scrollwheel-down. That is, you can capture scrollwheel events the same way you're capturing left clicks - you just need to look for SCROLL_UP or similar instead of LEFT.
Look up the documentation to find out exactly what SCROLL_UP is called.
A very simple solution is to define a pygame.time.Clock() to keep track of the time between two consecutive MOUSEBUTTONDOWN event.
Before the main loop define:
dbclock = pygame.time.Clock()
and in the event loop controller:
if event.type == pygame.MOUSEBUTTONDOWN:
if dbclock.tick() < DOUBLECLICKTIME:
print("double click detected!")
where DOUBLECLICKTIME is the maximum time allowed (in milliseconds) between two clicks for them being considered a double click. Define it before the mainloop. For example, to allow a maximum delay of half a second between the two clicks: DOUBLECLICKTIME = 500.
In pygame is possible to create as many pygame.time.Clock() objects are needed. dbclock must be used only for this purpose (I mean, no other calls to dbclock.tick() anywhere in the main loop) or it will mess with the tracking of the time between the two clicks.
For the sake of completeness, let me add also the answer about the scroll wheel, even if other answers already covered it.
The scroll wheel emits MOUSEBUTTONDOWN and MOUSEBUTTONUP events (it's considered a button). I can be identified by the event.button parameter, which is 4 when the wheel is rolled up, and 5 when the wheel is rolled down.
Set a timer when the mouse is pressed the first time to place a userevent on the pygame event queue, and set a variable to 1 to indicate a click. When the second click occurs, check the variable and set the timer event object to off. Check if the userevent comes up on the queue as this means the timer has timed out. see this beautiful answer for more information: Move an object every few seconds in Pygame
Here is the code, replace the double_click() call with your own function call:
def run():
global clock, double_click_event, timer
double_click_event = pygame.USEREVENT + 1
timer = 0
while True:
clock.tick(60)
check_events()
frame.update()
screen.blit(frame, (0,0))
pygame.display.flip()
def check_events():
global dispatcher, double_click_event, timer
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if timer == 0:
pygame.time.set_timer(double_click_event, 500)
timerset = True
else:
if timer == 1:
pygame.time.set_timer(double_click_event, 0)
double_click()
timerset =False
if timerset:
timer = 1
return
else:
timer = 0
return
elif event.type == double_click_event:
# timer timed out
pygame.time.set_timer(double_click_event, 0)
timer = 0
print "evt = dble click"
There doesn't appear to be a native double-click event. I'll guess you'd need to check the time between consecutive MOUSEBUTTONDOWN events.
The mouse wheel will generate pygame.MOUSEBUTTONDOWN events when rolled. The button will be set to 4 when the wheel is rolled up, and to button 5 when the wheel is rolled down

Categories