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).
Related
Creating a space invaders style game using sprites as a pose to images - Trying to get the bullet to continually move up the screen until it detects that it's passed y = 0. I've got an function to fire that moves the bullet up, but only by 5 pixels per MOUSEBUTTONDOWN event detected. Ideally, it needs to continue moving to the top of the screen after only one event is fired.
Here is the relevant code concerning the bullet class and the event loop.
# bullet class
class Bullet(pygame.sprite.Sprite):
def __init__(self,path):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
def shoot(self,pixels):
self.rect.y -= pixels
def track(self):
bullet.rect.x = player.rect.x + 23
And the event loop:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.moveLeft(5)
if event.key == pygame.K_RIGHT:
player.moveRight(5)
if event.type == pygame.MOUSEBUTTONDOWN:
bullet.shoot(5)
bullet.track()
playerGroup.update()
gameDisplay.blit(backgroundImg,(0,0))
playerGroup.draw(gameDisplay)
bulletGroup.update()
bulletGroup.draw(gameDisplay)
pygame.display.update()
clock.tick(60)
Thanks to anyone who answers my question.
You can put an additional attribute shot in your bullet class. It
is initially set to 0 and updated when the shoot method is called.
Then the track method updates the y position of the bullet
accordingly.
class Bullet(pygame.sprite.Sprite):
def __init__(self,path):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(path)
self.rect = self.image.get_rect()
self.shot = 0 # bullet not shot initially
def shoot(self, pixels):
self.shot = pixels
def track(self):
self.rect.y -= self.shot
So, as soon as the button is clicked, the shot attribute is modified
and the bullet will keep moving.
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)
Edit: added a longer example code.
I'm having trouble with coding buttons in pygame. I'm a newbie to the pygame module, so be gentle.
Basically, the goal is to make a point-and-click telltale kind of game. The player gets presented with two choices each gameloop, ie "go left" or "go right". Accordingly, there are two buttons in each gameloop, all located at the same coordinates.
Here is the function:
import pygame
import os
import time
pygame.init()
display_width= 1280
display_height = 720
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
def button(msg,x, y, w, h, ic, ac, action=None): #message, x y location, width, height, inactive and active colour
if action ==None:
pygame.display.update()
clock.tick(15)
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
pygame.draw.rect(gameDisplay, ac,(x,y,w,h))
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
pygame.display.update()
clock.tick(15)
if action == "left1":
game_loop("loop1-1.png",0,0,"left2","left2","","")
else:
pygame.draw.rect(gameDisplay, ic,(x,y,w,h))
smallText = pygame.font.SysFont('timesnewroman',20)
textSurf, textRect = text_objects(msg, smallText, silver)
textRect.center = ( (x+(w/2)), (y+(h/2)) )
gameDisplay.blit(textSurf, textRect)
def game_loop(pic,width,heigth,act1,act2,left,right):
intro = True
while intro:
for event in pygame.event.get():
print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
gameDisplay.blit(get_image(pic), (0, 0)) #MAIN MENU PIC
button(left,440,450,width,heigth, dark_gray, gray, action=act1)#start nupp
button(right,740,450,width,heigth, dark_gray, gray, action=act2)#exit nupp
pygame.display.update()
clock.tick(15)
The problem occurs when I click the button carelessly, meaning if I don't purposefully click on the left mouse button as fast as I can. Once game_loop1 is called and if I click for a bit longer, the program will read the first click again in this game_loop1 and run the next game_loop and then the next etc. This means the player can accidentally skip through gameloops.
Is there a way to delay the program after the first click(however long it is)? Or maybe a way to include keyup in the function, so it won't count the click in the next gameloop?
Thanks!
I think your original code is a bit too convoluted to fix it and I'll rather show you some better ways to do what you want. You need a finite-state machine to transition between different states/scenes. You can find a simple example with functions as scenes here.
If the logic in your scenes is mostly the same, you could also try to just swap out the data for the scene, for example the background image. Each state/scene needs to know to which new states it can switch, so I put the data into a dictionary of dictionaries. The nested dicts contain the background image of the scene and the connected left and right scenes. When the user presses a button/rect I check if it was the left or right button and then switch to the corresponding scene (subdict) in the states dictionary.
import pygame
pygame.init()
display_width= 1280
display_height = 720
gameDisplay = pygame.display.set_mode((display_width, display_height))
clock = pygame.time.Clock()
# Use uppercase names for constants that should never be changed.
DARK_GRAY = pygame.Color('gray13')
BACKGROUND1 = pygame.Surface((display_width, display_height))
BACKGROUND1.fill((30, 150, 90))
BACKGROUND2 = pygame.Surface((display_width, display_height))
BACKGROUND2.fill((140, 50, 0))
BACKGROUND3 = pygame.Surface((display_width, display_height))
BACKGROUND3.fill((0, 80, 170))
states = {
'scene1': {'background': BACKGROUND1, 'left_scene': 'scene2', 'right_scene': 'scene3'},
'scene2': {'background': BACKGROUND2, 'left_scene': 'scene1', 'right_scene': 'scene3'},
'scene3': {'background': BACKGROUND3, 'left_scene': 'scene1', 'right_scene': 'scene2'},
}
def game_loop():
# The buttons are just pygame.Rects.
left_button = pygame.Rect(440, 450, 60, 40)
right_button = pygame.Rect(740, 450, 60, 40)
# The current_scene is a dictionary with the relevant data.
current_scene = states['scene1']
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEBUTTONDOWN:
# If the left button is clicked we switch to the 'left_scene'
# in the `current_scene` dictionary.
if left_button.collidepoint(event.pos):
current_scene = states[current_scene['left_scene']]
print(current_scene)
# If the right button is clicked we switch to the 'right_scene'.
elif right_button.collidepoint(event.pos):
current_scene = states[current_scene['right_scene']]
print(current_scene)
# Blit the current background.
gameDisplay.blit(current_scene['background'], (0, 0))
# Always draw the button rects.
pygame.draw.rect(gameDisplay, DARK_GRAY, left_button)
pygame.draw.rect(gameDisplay, DARK_GRAY, right_button)
pygame.display.update()
clock.tick(30) # 30 FPS feels more responsive.
game_loop()
pygame.quit()
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'm creating a physics simulation which simulates the Rutherford scattering experiment. I'm trying to create an alpha particles (sprite) each time the loop is run and I'm able to create it (shows up on screen) but it doesn't move forwards whereas if I create only one particle, it works fine.
I've attached the sprite's class and the loop as to where it's being created.
Loop:
while running:
clock.tick(20)
allparticles = pygame.sprite.Group(Particle(speed, bwidthmin, bwidthmax, background))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
allparticles.clear(screen, background)
nucgroup.draw(screen)
allparticles.update()
allparticles.draw(screen)
pygame.display.flip()
Sprite Class:
class Particle(pygame.sprite.Sprite):
def __init__(self, speed, bwidthmin, bwidthmax, background):
pygame.sprite.Sprite.__init__(self)
self.background = background
self.image = pygame.Surface((16,16))
self.rect = self.image.get_rect()
currenty = random.randint(bwidthmin,bwidthmax)
self.rect.centery = currenty
self.rect.centerx = 0
pygame.draw.circle(self.image, yellow, (8,8), 5)
self.dx=speed
self.dy = 0
def update(self):
c1 = (self.rect.centerx,self.rect.centery)
self.rect.centerx += self.dx
if self.rect.right >= 570:
pygame.sprite.Sprite.kill(self)
pygame.draw.line(self.background, white, c1, (self.rect.centerx,self.rect.centery), 1)
Where am I going wrong?
I also have the issue of my tkinter window in which this pygame is embedded hanging (buttons not pressing, tabs not changing, can't do anything until pygame stops). Is the loops running forever causing this to happen? I'd like to be able to update the variables to affect the simulation during runtime or is that not possible?
Thanks for the help.
One issue is that you are overwriting allparticles each time through the loop. Perhaps you mean to keep creating particles and appending to a list?
Try this:
allparticles = []
while running:
clock.tick(20)
allparticles.append(pygame.sprite.Group(Particle(speed, bwidthmin, bwidthmax, background)))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for particle in allparticles: # loop over all particles each time
particle.clear(screen, background)
nucgroup.draw(screen)
particle.update()
particle.draw(screen)
pygame.display.flip()