I'm trying to make sprite animation with 3 images and I need that animation to play on single mouse click.
I tried to use this code, that worked for standing and walking animations, but when I use it for shooting, animation can't happen, because mouse click is fast (It works if I hold mouse button though).
if self.standing:
if now - self.last_update > 50:
self.last_update = now
self.current_frame = (self.current_frame + 1) % len(self.game.player_idles)
self.image = pg.transform.scale(self.game.player_idles[self.current_frame], (64, 55))
self.image_copy = pg.transform.rotate(self.image, 270)
Use the event queue.
In update, replace:
if mouse[0]:
self.shooting = True
self.standing = False
self.moving = False
else:
self.shooting = False
with:
for event in pygame.event.get():
if e.type == MOUSEBUTTONDOWN:
shoot()
You can then get rid of the shooting bool altogether, and add the graphics changes made if self.shooting to your shoot function. Or, if you want to keep the shooting bool, you can do something like this:
for event in pygame.event.get():
if e.type == MOUSEBUTTONDOWN:
self.shooting = True
self.moving = False
self.standing = False
if e.type == MOUSEBUTTONUP:
self.shooting = False
This code will be more responsive, since it works whenever an event (a click) is inputted by the user, rather than whether the mouse button is being held down.
I am not sure how you are calling the animations for the shooting part.
Can you post your code for that? I could probably tell more then. Otherwise my code may not be meaningful/useful. I am sorry for that.
(I cannot comment yet)
Check for mouse click state in the pygame events. If you find a click, store it in a flag(s) like you are doing for the standing part.
We shall use a variable called click_animation_state here to see which frame we are currently in.
self.game.shooting_frames = [img1, img2, img3]
if self.mouse_clicked:
if now - self.last_update > time_threshold:
self.last_update = now
self.current_frame = (self.current_frame + 1) %
len(self.game.shooting_frames)
self.image = pg.transform.scale(self.game.click_frames[self.current_frame], (64, 55))
self.image_copy = pg.transform.rotate(self.image, 270)
Related
This is my python code, using pygame. When I pressed my mouse down, scene 1 does not switch to scene 2. I am coming from Code HS, so the scene switch is from the Tell A Story project. I realized that code is not the same as pygame's. So I used pygame docs and see what I can learn from that, but nothing still. Please can any one help me. Thank you.
import pygame as pg
pg.init()
win = pg.display.set_mode((500,500))
pg.display.set_caption('Scene Switcher')
center_x = 250 - 130
center_y = 250
black= (0,0,0)
red = (255,0,0)
blue = (0,0,255)
def ct(font, size, text, color):
mf = pg.font.Font(font, size)
t = mf.render(text, True, color)
return t
def draw_scene1():
print("This is Scene 1")
txt = ct("SB.ttf", 40, "Hello World!", black)
win.blit(txt, (center_x,center_y))
def draw_scene2():
print("This is scene 2")
txt2 = ct("SB.ttf", 40, "scene2 ", black)
win.blit(txt2, (center_x,center_y))
while True:
win.fill(red)
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
quit()
mouses = pg.mouse.get_pressed()
scene_counter = 0
# When this function is called the next scene is drawn.
def draw_next_screen():
global scene_counter
scene_counter += 1
if scene_counter == 1:
draw_scene1()
else:
draw_scene2()
if mouses:
draw_next_screen()
pg.display.update()
You have to initialize scene_counter before the application loop rather than in the application loop.
The 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. Therefor the scene_counter is continuously incremented in each frame as long a mouse button is hold down.
The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released. Increment the counter when the MOUSEBUTTONDOWN event occurs:
scene_counter = 0
run = True
while run:
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
elif event.type == pg.MOUSEBUTTONDOWN:
scene_counter += 1
win.fill(red)
if scene_counter == 0:
draw_scene1()
else:
draw_scene2()
pg.display.update()
pg.quit()
Using pygame and python 3.7, I have created a game in which an image at the center (250, 250) can be dragged around the screen and collide with a radius, in which case a break happens and the next image in the next loop spawns at the exact center, where the first image was located, as well. Despite the game working the way I intended, in principle, it behaves weirdly for fast mouse speed. In my minimal example code, the colored circles are supposed to reappear at the exact center, for every while-loop, however, they somehow don't update properly and therefore reappear not at the center of the screen, most of the time (they only do when I release the mouse-button really early / well-timed). I tested the game on windows and mac and noticed that on my mac, the "lag" seems to be even worse. If anybody knows how I could work around that, I'd be really thankful. Also, the game starts lagging and jumping to the next loop right away for really fast mouse movement, with which I have dealt by changing the speed of my external mouse. Is there an inherent fix for too fast mouse-movement in pygame? Thanks for all suggestions and maybe also other improvements to the idea of my code.
import pygame
import time
from time import sleep
import math
pygame.init()
gameDisplay = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
background_surface = pygame.Surface((500, 500))
background_surface.fill((255, 255, 255))
colors = [(0,0,0), (253, 45, 0), (249, 253, 0), (32, 201, 5), (0, 210, 235)]
for color in colors:
done = False
a, b = 250, 250
u, v = 250, 250
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEMOTION:
if event.buttons[0]:
a += event.rel[0]
b += event.rel[1]
rect = pygame.Rect(u, v, 0.001, 0.001)
radius = 200
corners = [rect.bottomleft, rect.bottomright, rect.topleft, rect.topright]
dist = [math.sqrt((p[0] - a) ** 2 + (p[1] - b) ** 2) for p in corners]
p_out = [i for i, d in enumerate(dist) if d > radius]
if any(p_out):
break
gameDisplay.blit(background_surface, (0, 0))
pygame.draw.circle(gameDisplay, color, (a,b), 50, 0)
pygame.draw.circle(gameDisplay, (0,0,0), (250,250), 200, 2)
pygame.display.flip()
sleep(0.7)
pygame.quit()
quit()
[...] they somehow don't update properly and therefore reappear not at the center of the screen, most of the time (they only do when I release the mouse-button really early / well-timed) [...]
Of course, investigate your code. The 1st thing what happens in your code is, that a and b are modified:
while not done:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEMOTION:
if event.buttons[0]:
a += event.rel[0]
b += event.rel[1]
If the pygame.MOUSEMOTION event is in the queue, then it is handled before the circle is drawn. So the position of the circle is modified and it does not appear in the center.
Note, an event does not reflect the current state of the mouse. It is a notification, which is stored in a queue when it happens, but then event handling may done later. What you actually do is to handle an event, which possibly occurred in the past.
Use a state dragging, which is False at the start of each loop. Set the state when the button is pressed (pygame.MOUSEBUTTONDOWN event) and reset it when the button is released (pygame.MOUSEBUTTONUP event):
for color in colors:
# [...]
dragging = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
dragging = True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
dragging = False
elif event.type == pygame.MOUSEMOTION:
if dragging:
a += event.rel[0]
b += event.rel[1]
# [...]
So I currently have this code for an airbrush in a drawing app. When the function is active, it should draw on a canvas, basically like an airbrush. But right now, I don't know how to make pygame detect a mouse down or mouse up and make a while loop out of it. Here is the code:
def airbrush():
airbrush = True
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
while click == True:
pygame.draw.circle(gameDisplay, colorChosen, (cur[0] + random.randrange(brushSize), cur[1] + random.randrange(brushSize)), random.randrange(1, 5))
pygame.display.update()
clock.tick(60)
Right now, I have "while click" which doesn't work. What should I replace "click" with to make this work so that while the mouse is held down, it paints, but when the mouse is "up", it stops?
The state which is returned by pygame.mouse.get_pressed() is evaluated once when pygame.event.get() is called. The return value of pygame.mouse.get_pressed() is tuple with th states of the buttons.
Don't implement a separate event handling in a function. Do the event handling in the main loop:
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
airbrush()
pygame.display.flip()
Evaluate the current state of a button (e.g. left button), of the current frame, in the function airbrush:
def airbrush():
airbrush = True
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if click[0] == True: # evaluate left button
pygame.draw.circle(gameDisplay, colorChosen, (cur[0] + random.randrange(brushSize), cur[1] + random.randrange(brushSize)), random.randrange(1, 5))
I am trying to create a menu screen for my game. At the moment im using sprites for buttons and everything works well, and I can create an infinite amount of buttons (currently I just have Start and Options), but only the first button I call appears on the screen. I think it has something to do with the while loop in the button class but I am not sure on how to fix it. I am probably making no sense here so if you need me to clarify anything I will. Thanks!
import pygame
import random
import time
pygame.init()
#colours
white = (255,255,255)
black = (0,0,0)
red = (255,0,0)
green = (0,155,0)
blue = (50,50,155)
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width,display_height))
pygame.display.set_caption('Numeracy Ninjas')
clock = pygame.time.Clock()
img_button_start = pygame.image.load('Sprites/Buttons/button_start.png')
img_button_options = pygame.image.load('Sprites/Buttons/button_options.png')
gameDisplay.fill(white)
class Button(pygame.sprite.Sprite):
def __init__(self, sprite, buttonX, buttonY):
super().__init__()
gameDisplay.blit(sprite, (buttonX, buttonY))
pygame.display.update()
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
print(x, y)
if x <= (150 + buttonX) and x >=(0 + buttonX) and y <= (75 + buttonY) and y >= (0 + buttonY):
print('clicked on button')
def gameIntro():
button_start = Button(img_button_start, 27, 0)
button_options = Button(img_button_options, 27, 500)
gameIntro()
In the constructor of your Button class, you have an infinite loop. This means you never get to the code part where you make your second Button.
def gameIntro():
button_start = Button(img_button_start, 27, 0) #stuck in infinite loop
print('This print statement is never reached')
button_options = Button(img_button_options, 27, 500)
Instead, what you want to do is initialize two Buttons, and then have a main game loop in your gameIntro() method that checks for events. If a mousebuttondown event occured, you want to pass the event (or even just the event position, if you don't care which mouse button was clicked) to a function of button that checks whether this instance of button was clicked and then handles the input (possibly by returning something which you handle in the main game loop).
Note that I haven't run the following code, I'm just trying to give you an idea how it should be structured:
class Button(pygame.sprite.Sprite):
def __init__(self, image, buttonX, buttonY):
super().__init__()
self.image = image
self.rect = image.getRect()
self.rect.x = buttonX
self.rect.y = buttonY
def wasClicked(event):
if self.rect.collidepoint(event.pos):
return True
def gameIntro():
#initialize buttons
buttons = pygame.sprite.Group() #make a group to make drawing easier
button_start = Button(img_button_start, 27, 0)
button_options = Button(img_button_options, 27, 500)
#draw buttons to display
buttons.draw(gameDisplay)
pygame.display.update()
#main game loop
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
#check for every button whether it was clicked
for btn in buttons:
if btn.wasClicked():
#do something appropriate
if event.type == pygame.QUIT:
pygame.quit()
I am trying to have an onclick button print a check and run my pencil function. At the moment if I hover over the Box sprite.. it will run the print and pencil function. It should be ONCLICK it runs those 2. Can anyone help me out? Thanks! (this should be all relevant code, if you need more please let me know :)
class Box(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((35, 30))
self.image = self.image.convert()
self.image.fill((255, 0, 0))
self.rect = self.image.get_rect()
self.rect.centerx = 25
self.rect.centery = 505
self.dx = 10
self.dy = 10
while keepGoing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepGoing = False
box = Box()
allSprites = pygame.sprite.Group(box)
allSprites.draw(screen)
if event.type == MOUSEMOTION:
x,y = event.pos
if box.rect.collidepoint(x,y) and pygame.MOUSEBUTTONUP:
print("collide works")
pencil(background,clock,keepGoing,screen)
pygame.display.flip()
Your code is not checking for mouse clicks, but rather mouse movement.
If you want to be testing for a click on your box, change condition to check for MOUSEBUTTONDOWN or MOUSEBUTTONUP events (depending on what part of the click you want to react to), rather than MOUSEMOTION events.
There are some other issues with your code though. For instance, you're creating your Box and Group after every event. Probably you want to just create them once, before going into the game loop (this will both make more sense and perform better).