FPS loose in Pygame - python-3.x

So there is my previous code that I corrected myself, but there is another problem. The 'game' is running at 60 FPS, but when I wanna blit a background instead of a black screen, the game slows down to 30 fps.. Is there a way to fix this ?
import math
import pygame
class Planete:
def __init__(self,rayon,periode,envergure,couleur):
self.rayon = rayon
self.periode = periode
self.couleur = couleur
self.envergure = envergure
self.omega = (2*math.pi)/self.periode
self.i = 0
def tourner(self) :
self.x = self.rayon*math.cos(self.omega*self.i)
self.x2 = int(self.x)+500
self.y = self.rayon*math.sin(self.omega*self.i)
self.y2 = int(self.y)+200
self.i = self.i + 1
def dessiner(self):
pygame.draw.circle(gameDisplay,(self.couleur),((self.x2, self.y2)), self.envergure)
pygame.init()
Terre = Planete(149, 65.25,9,(25,0,250))
Mars = Planete(227, 86.98,8,(250,25,0))
#Wuut = Planete(195,206,7,(15,30,70))
#Jupiter = Planete(80,800,12,(150,50,15))
gameDisplay = pygame.display.set_mode((1280,720))
background = pygame.image.load("Ecran titre\\principal\\background.jpg")
menu = pygame.image.load("Ecran titre\\principal\\menu.png")
clock = pygame.time.Clock()
gameExit = False
while not gameExit :
Terre.tourner()
Mars.tourner()
#Wuut.tourner()
#Jupiter.tourner()
for event in pygame.event.get():
if (event.type == pygame.QUIT):
gameExit = True
gameDisplay.fill(0)
#gameDisplay.blit(background,(0,0))
Terre.dessiner()
Mars.dessiner()
#Wuut.dessiner()
#Jupiter.dessiner()
pygame.display.update()
clock.tick(60)
pygame.display.set_caption("fps: " + str(clock.get_fps()))
pygame.quit()
quit()

Blitting an image that has transparency is really slow. If you call .convert() after loading it, it'll make it blit faster. This will convert it to a faster opaque format.
background = pygame.image.load("Ecran titre\\principal\\background.jpg").convert()

Related

Pygame, A Bit Racey tutorial: Calling the same function to draw the same object multiple times [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()

Pygame, how do you fix a part of the screen being black when i make a game?

A part (specifically the top middle) of the screen is black, once i have added a background, but i can see the rest of the background.
I have tried running it a few times and i have tried searching what might have caused it, i don't have a game loop yet, because nothing is supposed to happen, that might be the problem... sorry the code is long...
import pygame
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
SCREEN_TITLE = "road cross game thing"
WHITE_COLOR = (255,255,255)
clock = pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont("comicsans", 75)
title = SCREEN_TITLE
width = SCREEN_WIDTH
height = SCREEN_HEIGHT
class Game:
TICK_RATE = 60
title = SCREEN_TITLE
width = SCREEN_WIDTH
height = SCREEN_HEIGHT
image = pygame.image.load("photo.jpg")
game_screen = pygame.display.set_mode((width, height))
game_screen.fill(WHITE_COLOR)
game_screen.fill(WHITE_COLOR)
game_screen.blit(image, (0, 0))
pygame.display.set_caption(title)
is_game_over = False
did_win = False
direction = 0
while not is_game_over:
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_game_over = True
elif event.type == pygame.MOUSEBUTTONUP:
pos= pygame.mouse.get_pos()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
direction = 0
print(event)
game_screen.fill(WHITE_COLOR)
pygame.display.update()
clock.tick(TICK_RATE)
object_image1 = pygame.image.load("photo.jpg")
image1 = pygame.transform.scale(object_image1, (50, 50))
x_pos = 50
y_pos = 50
game_screen.blit(image1,(x_pos, y_pos))
pygame.init()
Game()
pygame.quit()
quit()
i expected the code to make the whole background my picture.
Your code is mostly there, it's just a bit un-organised.
The reason the screen updating is not what you expect is because the code is drawing and then updating in out-of-order. In a nutshell, it should make all the drawing operations first, then update() them all to the display. Obviously if you draw and image, then call fill(), the first draw operation is not going to be visible, since it's been over-painted.
The image1 does not fill the entire display because it's only stretched to ( 50, 50 ) whereas the screen is 1500 x 800.
While I was debugging the code, I moved the initialisation functions into a member function Game.__init__(), and the main game loop into a Game.run() function.
The OP's indentation is a little messed-up, I expect this is caused by the paste into SO (otherwise the program would not produce the described result). But it looks like you're loading images inside the main game loop. It's best to load resources like images, sounds, etc. only once before the user-event loop begins.
import pygame
SCREEN_WIDTH = 1500
SCREEN_HEIGHT = 800
TICK_RATE = 60
SCREEN_TITLE = "road cross game thing"
WHITE_COLOR = (255,255,255)
# Initialise PyGame
pygame.init()
pygame.display.set_caption( SCREEN_TITLE )
game_screen = pygame.display.set_mode( ( SCREEN_WIDTH, SCREEN_HEIGHT ) )
clock = pygame.time.Clock()
pygame.font.init()
font = pygame.font.SysFont("comicsans", 75)
class Game:
def __init__( self, game_screen ):
self.game_screen = game_screen
self.image = pygame.image.load( "photo.jpg" )
self.object_image1 = pygame.image.load( "photo.jpg" )
#self.image1 = pygame.transform.scale( self.object_image1, (50, 50) )
# Scale to fill window
self.image1 = pygame.transform.scale( self.object_image1, ( SCREEN_WIDTH, SCREEN_HEIGHT) )
def run( self ):
is_game_over = False
did_win = False
direction = 0
while not is_game_over:
# Handle user input events
for event in pygame.event.get():
if event.type == pygame.QUIT:
is_game_over = True
elif event.type == pygame.MOUSEBUTTONUP:
pos= pygame.mouse.get_pos()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
direction = 0
print(event)
# Update the screen
#game_screen.fill( WHITE_COLOR )
#game_screen.blit( self.image, (0, 0) )
#x_pos = 50
#y_pos = 50
#game_screen.blit( self.image1, (x_pos, y_pos) )
# Fill the window with image1
game_screen.blit( self.image1, ( 0, 0 ) )
pygame.display.update()
clock.tick( TICK_RATE )
# Run the Game
game = Game( game_screen )
game.run()
pygame.quit()
quit()

Pygame is not displaying a grid or fps counter

I am trying to make a rpg but i always get the issue when pygame dose not display I am using a youtube video to help my make this rpg but Its just not working the creator made a color module to help but it just dose not work I know there are better ways of making a fps counter feel free to improve it
import pygame, sys, time
from Scripts.UltraColor import *
pygame.init()
cSec = 0
cFrame = 0
FPS = 0
tile_size = 32
fps_font = pygame.font.Font("C:\\Windows\\Fonts\\Verdana.ttf", 20)
def show_fps():
fps_overlay = fps_font.render(str(FPS), True, Color.Goldenrod)
window.blit(fps_overlay, (0,0))
def create_window():
global window, window_height, window_width, window_title
window_width, window_hight = 800, 600
window_title = "RPG"
pygame.display.set_caption(window_title)
window = pygame.display.set_mode((window_width, window_hight), pygame.HWSURFACE|pygame.DOUBLEBUF)
def count_fps():
global cSec, cFrame, FPS
if cSec == time.strftime("%S"):
cFrame += 1
else:
FPS = cFrame
cFrame = 0
cSec = time.strftime("%S")
create_window()
isRunning = True
while isRunning:
for event in pygame.event.get():
if event.type == pygame.QUIT:
isRunning = False
# LOGIC
count_fps()
# Render Graphics
window.fill(Color.Black)
# - Render Sinple Terrain Grid
for x in range(0, 640, tile_size):
for y in range(0, 480, tile_size):
pygame.draw.rect(window, Color.White, (x, y, tile_size + 1, tile_size + 1), 1)
show_fps()
pygame.display.update
pygame.quit()
sys.exit()
You need to add parentheses behind pygame.display.update to call this function and update the display: pygame.display.update()
I also recommend to use a pygame.time.Clock to limit the frame rate and get the fps. Calling clock.tick(FPS) makes sure that the game doesn't run faster than this frame rate. In the show_fps function you can just call clock.get_fps() to get the current frame rate.
import pygame
def show_fps(window, clock):
fps_overlay = FPS_FONT.render(str(clock.get_fps()), True, GOLDENROD)
window.blit(fps_overlay, (0, 0))
pygame.init()
FPS_FONT = pygame.font.SysFont("Verdana", 20)
GOLDENROD = pygame.Color("goldenrod")
tile_size = 32
window = pygame.display.set_mode((800, 600), pygame.HWSURFACE|pygame.DOUBLEBUF)
clock = pygame.time.Clock()
FPS = 60
isRunning = True
while isRunning:
for event in pygame.event.get():
if event.type == pygame.QUIT:
isRunning = False
# Render Graphics
window.fill((50, 50, 50))
# Render Simple Terrain Grid
for x in range(0, 640, tile_size):
for y in range(0, 480, tile_size):
pygame.draw.rect(
window, (255, 255, 255),
(x, y, tile_size+1, tile_size+1), 1)
show_fps(window, clock)
clock.tick(FPS)
pygame.display.update()
pygame.quit()

How do I create a button that changes sprites when clicked in pygame?

I am currently having trouble with buttons in pygame. At the moment I am testing myself in pygame and I am trying to create a flappy bird type of game. What I am trying to achieve is that when I click on the play button on the main menu it will change the button sprite and run the main game.
I have managed to define the button function and get it to switch to the main game when I click. The only problem is that it doesn't show a different sprite when clicked and I can click anywhere in the application to switch to the main game instead of having to click on the button.
Any help would be much appreciated.
Thanks in Advance
import time
import random
import pygame
from pygame.locals import *
pygame.init()
#Predefined Colors
white = (255,255,255)
black = (0,0,0)
red = (200,0,0)
light_red = (255,0,0)
yellow = (200,200,0)
light_yellow = (255,255,0)
green = (34,177,76)
light_green = (0,255,0)
blue = (0,0,255)
light_blue = (0, 0, 200)
player_list = (
# Red Bird
('assets/sprites/redbird-upflap.png', 'assets/sprites/redbird-midflap.png',
'assets/sprites/redbird-downflap.png'),
# Blue Bird
('assets/sprites/bluebird-upflap.png', 'assets/sprites/bluebird-midflap.png',
'assets/sprites/bluebird-downflap.png'),
# Yellow Bird
('assets/sprites/yellowbird-upflap.png', 'assets/sprites/yellowbird-midflap.png',
'assets/sprites/yellowbird-downflap.png')
)
background_list = (
('assets/sprites/background-day.png', 'assets/sprites/background-night.png')
)
pipe_list = (
('assets/sprites/pipe-green.png', 'assets/sprites/pipe-red.png')
)
FPS = 30
images, sounds = {}, {}
def main():
global base_x, base_y, clock, gameDisplay, display_height, display_width
display_width = 288
display_height = 512
base_x = 0
base_y = display_height * 0.79
clock = pygame.time.Clock()
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Flappy Bird")
#Loading icon sprite
images['Icon'] = pygame.image.load('assets/sprites/yellowbird-midflap.png')
pygame.display.set_icon(images['Icon'])
#Loading all the Numbers sprites
images['Numbers'] = (
pygame.image.load('assets/sprites/0.png').convert_alpha(),
pygame.image.load('assets/sprites/1.png').convert_alpha(),
pygame.image.load('assets/sprites/2.png').convert_alpha(),
pygame.image.load('assets/sprites/3.png').convert_alpha(),
pygame.image.load('assets/sprites/4.png').convert_alpha(),
pygame.image.load('assets/sprites/5.png').convert_alpha(),
pygame.image.load('assets/sprites/6.png').convert_alpha(),
pygame.image.load('assets/sprites/7.png').convert_alpha(),
pygame.image.load('assets/sprites/8.png').convert_alpha(),
pygame.image.load('assets/sprites/9.png').convert_alpha()
)
#Game Over Sprite
images['Game Over'] = pygame.image.load('assets/sprites/gameover.png').convert_alpha()
#Starting Game sprite
images['Starting Game'] = pygame.image.load('assets/sprites/startgame-screen.png').convert_alpha()
#Flappy Bird Logo sprite
images['Flappy Bird Logo'] = pygame.image.load('assets/sprites/flappybird-logo.png').convert_alpha()
#Base Ground sprite
images['Base Ground'] = pygame.image.load('assets/sprites/base.png').convert_alpha()
#Play Button Up sprite
images['Play Button Up'] = pygame.image.load('assets/sprites/playbutton-up.png').convert_alpha()
#Play Button Down sprite
images['Play Button Down'] = pygame.image.load('assets/sprites/playbutton-down.png').convert_alpha()
#Quit Button Up sprite
#images['Quit Button Up'] = pygame.image.load('assets/sprites/quitbutton-up.png').convert_alpha()
#Quit Button Down sprite
#images['Quit Button Down'] = pygame.image.load('assets/sprites/quitbutton-down.png').convert_alpha()
#Sounds
# sounds['Die'] = pygame.mixer.Sound('assets/audio/die.wav')
# sounds['Hit'] = pygame.mixer.Sound('assets/audio/hit.wav')
# sounds['Point'] = pygame.mixer.Sound('assets/audio/point.wav')
# sounds['swoosh'] = pygame.mixer.Sound('assets/audio/swoosh.wav')
# sounds['wing'] = pygame.mixer.Sound('assets/audio/wing.wav')
while True:
#Select random Background sprites
random_background = random.randint(0, len(background_list) - 1)
images['Background'] = pygame.image.load(background_list[random_background]).convert()
#Select random Player sprites
random_player = random.randint(0, len(player_list) - 1)
images['Player'] = (
pygame.image.load(player_list[random_player][0]).convert_alpha(),
pygame.image.load(player_list[random_player][1]).convert_alpha(),
pygame.image.load(player_list[random_player][2]).convert_alpha()
)
#Select random Pipe sprite
random_pipe = random.randint(0, len(pipe_list) - 1)
images['Pipe'] = pygame.image.load(pipe_list[random_pipe])
main_menu()
pygame.display.update()
clock.tick(FPS)
def button(action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if action == 'Play':
button = images['Play Button Up'].get_rect()
for event in pygame.event.get():
if click[0] == 1:
if button.collidepoint(cur):
print ('Mouse Over')
images['Play Button Down']
main_game()
else:
gameDisplay.blit(images['Play Button Up'], (0, -10))
def main_menu():
global player_index, player_x, player_y
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'] [0].get_height()) / 2)
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Flappy Bird Logo'], (50, -30))
gameDisplay.blit(images['Player'][player_index], (125, 140))
gameDisplay.blit(images['Play Button Up'], (10, 10))
button(action = 'Play')
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_x = 0
player_y = 0
while not gameExit:
if gameOver == True:
gameDisplay.blit(images['Game Over'], 50, 50)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Starting Game'], (0, 0))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
gameDisplay.blit(images['Player'][player_index], (player_x, player_y))
pygame.display.update()
clock.tick(FPS)
main()
Here's a minimal example to demonstrate how you can switch between different button images.
When the user presses a mouse button (a pygame.MOUSEBUTTONDOWN event is added to the queue), check if the event.pos collides with the button rect and if it collides, set the image to the "down" version.
When the user releases the button (pygame.MOUSEBUTTONUP event), just set the image back to the original version.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
GRAY = pg.Color('gray15')
BLUE = pg.Color('dodgerblue1')
LIGHTBLUE = pg.Color('lightskyblue1')
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(LIGHTBLUE)
def main():
clock = pg.time.Clock()
font = pg.font.Font(None, 30)
# Currently selected button image.
button_image = BUTTON_UP_IMG
button_rect = button_image.get_rect(topleft=(200, 200))
x = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if button_rect.collidepoint(event.pos):
button_image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
button_image = BUTTON_UP_IMG
if button_rect.collidepoint(event.pos):
print('Button pressed.')
x += 1
screen.fill(GRAY)
screen.blit(button_image, button_rect)
txt = font.render(str(x), True, BLUE)
screen.blit(txt, (260, 206))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
I would actually recommend to use classes, pygame sprites and sprite groups instead of just rects and images. Then you can easily create as many instances of the button class as you want.
import pygame as pg
pg.init()
GRAY= pg.Color('gray12')
BLUE = pg.Color('dodgerblue1')
FONT = pg.font.Font(None, 30)
BUTTON_UP_IMG = pg.Surface((50, 30))
BUTTON_UP_IMG.fill(BLUE)
BUTTON_DOWN_IMG = pg.Surface((50, 30))
BUTTON_DOWN_IMG.fill(pg.Color('lightskyblue1'))
# The Button is a pygame sprite, that means we can add the
# instances to a sprite group and then update and render them
# by calling `sprite_group.update()` and `sprite_group.draw(screen)`.
class Button(pg.sprite.Sprite):
def __init__(self, pos, callback):
pg.sprite.Sprite.__init__(self)
self.image = BUTTON_UP_IMG
self.rect = self.image.get_rect(topleft=pos)
self.callback = callback
def handle_event(self, event):
"""Handle events that get passed from the event loop."""
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1:
if self.rect.collidepoint(event.pos):
self.image = BUTTON_DOWN_IMG
elif event.type == pg.MOUSEBUTTONUP:
if event.button == 1:
self.image = BUTTON_UP_IMG
if self.rect.collidepoint(event.pos):
print('Button pressed.')
# Call the function that we passed during the
# instantiation. (In this case just `increase_x`.)
self.callback()
class Game:
def __init__(self):
self.screen = pg.display.set_mode((800, 600))
self.clock = pg.time.Clock()
self.x = 0
self.buttons = pg.sprite.Group(
Button((200, 200), callback=self.increase_x),
Button((500, 200), callback=self.decrease_x))
self.done = False
# A callback function that we pass to the button instance.
# It gets called if a collision in the handle_event method
# is detected.
def increase_x(self):
"""Increase self.x if button is pressed."""
self.x += 1
def decrease_x(self):
"""Decrease self.x if button is pressed."""
self.x -= 1
def run(self):
while not self.done:
self.handle_events()
self.run_logic()
self.draw()
self.clock.tick(30)
def handle_events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
for button in self.buttons:
button.handle_event(event)
def run_logic(self):
self.buttons.update()
def draw(self):
self.screen.fill(GRAY)
self.buttons.draw(self.screen)
txt = FONT.render(str(self.x), True, BLUE)
self.screen.blit(txt, (360, 206))
pg.display.flip()
if __name__ == "__main__":
Game().run()
pg.quit()
Thanks for the Answer to my previous question your answer solved my previous issue, but now I have ran into another issue.
When I press my button on the main menu it changes sprites correctly and loads the main game correctly, but as soon as I move my mouse it switches back to the main menu. My best guess is that it is because it is looping the whole button sequence, but I am not entirely sure.
import pygame
pygame.init()
display_width = 288
display_height = 512
def main_menu():
done = False
play_button_image = images['Play Button Up']
play_button_rect = play_button_image.get_rect(topleft=(30,15))
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if play_button_rect.collidepoint(event.pos):
play_button_image = images['Play Button Down']
elif event.type == MOUSEBUTTONUP:
if event.button == 1:
play_button_image = images['Play Button Up']
if play_button_rect.collidepoint(event.pos):
main_game()
gameDisplay.blit(play_button_image, play_button_rect)
pygame.display.update()
clock.tick(FPS)
def main_game():
gameExit = False
gameOver = False
player_index = 0
player_x = int(display_width * 0.2)
player_y = int((display_height - images['Player'][0].get_height()) / 2)
player_index_gen = cycle([0, 1, 2, 1])
loop_iter = 0
starting_game_x = int((display_width - images['Starting Game'].get_width()) / 2)
starting_game_y = int(display_height * 0.12)
base_x = 0
base_shift = images['Base Ground'].get_width() - images['Background'].get_width()
player_move_vals = {'val': 0, 'dir': 1}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.MOUSEBUTTONDOWN or pygame.K_SPACE or pygame.K_UP:
sounds['Wing']
return {
'player_y': player_y + player_move_vals['val'],
'base_x': base_x,
'player_index_gen': player_index_gen
}
if (loop_iter + 1) % 5 == 0:
player_index = next(player_index_gen)
loop_iter = (loop_iter + 1) % 30
base_x = -((-base_x + 4) % base_shift)
player_move(player_move_vals)
# draw sprites
gameDisplay.blit(images['Background'], (0, 0))
gameDisplay.blit(images['Player'][player_index],
(player_x, player_y + player_move_vals['val']))
gameDisplay.blit(images['Starting Game'], (starting_game_x, starting_game_y))
gameDisplay.blit(images['Base Ground'], (base_x, base_y))
pygame.display.update()
clock.tick(FPS)
if __name__ == '__main__':
main()

How to detect collision/mouse-over between the mouse and a sprite?

Goal: to know when a sprite is clicked / active with the mouse
Using: Python 3.2 64bit, Pygame 1.92 64bit, windows 7 64bit
I spend 6 hours to no avail...I've tried :
s.rect.collidepoint(pygame.mouse.get_pos())
s.collidepoint(pygame.mouse.get_pos())
s.sprite.spritecollide(pygame.mouse.get_pos())
s.spritecollide(pygame.mouse.get_pos())
s.sprite.collide_rect(pygame.mouse.get_pos())
s.collide_rect(pygame.mouse.get_pos())
I've also tried turning the mouse location, which I really don't want to do, like someone else mentioned here on another post, into a sprite and collide like that with the same results ;(
I'm able to successfully mouse-collide with an image, but as soon as I turn the image into a sprite class, it becomes a nightmare...what's wrong with the sprite class? Or am I wasting time trying to use sprites for the nice collision features and just use images with rect collision instead?
Keep on getting the AttributeError: 'Sheldon' object has no attribute 'Rect' (s.Rect.collidepoint)
or AttributeError: 'tuple' object has no attribute 'collidepoint' (s.collidepoint)
or AttributeError: 'Rake' object has no attribute 'sprite' (s.sprite.collidepoint)
Since I'm new to python/pygame, should I be putting this detection in an Update/Render method in the sprite class itself, or am I using the wrong event polling???
I haven't bothered trying to recode the mousedown/up/dragging since I can't even get the mouse-over to work
Hopefully this time the post gets a working response...the others didn't ;(
Thanks for your help.
Code:
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10)
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250)
all_sprites.add(rake)
x,y = pygame.mouse.get_pos()
mouse_sprite = Sprite_Mouse_Location(x,y)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
You don't need Sprite_Mouse_Location.
BTW: to set position you need
rake.rect.topleft = (400, 250)
# or
rake.rect.x = 400
rake.rect.y = 250
not
rake.rect = (400, 250)
because it replace pygame.Rect() with tuple
Example code:
I use Surface instead of image.load() so everyone can run it without images.
import pygame
# --- constants --- (UPPER_CASE names)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# --- classes --- (CamelCase names)
class Sheldon(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(RED)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit RED")
class Rake(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((230, 310))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def check_click(self, mouse):
if self.rect.collidepoint(mouse):
print("hit GREEN")
# --- main --- (lower_case names)
# - init -
pygame.init()
window = pygame.display.set_mode((800,600))
# - objects -
sheldon = Sheldon(10, 10)
#sheldon.rect.topleft = (10, 10)
rake = Rake(400, 250)
#rake.rect.topleft = (400, 250)
all_sprites = pygame.sprite.Group()
all_sprites.add(sheldon, rake)
# - mainloop -
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
for s in all_sprites:
s.check_click(event.pos)
window.fill(BLACK)
all_sprites.update()
all_sprites.draw(window)
pygame.display.update()
# - end -
pygame.quit()
There are a lot of things that are going on with your code. Coding can be hard at first, but keep sticking to! You'll better as long as you keep trying and don't give up! Your error is occurring because above the loop, you are setting your various sprites' rect variables to tuples instead of rects. Rects are not tuples, they have a bunch of variables that tuples don't have and collide_rect needs some of those variables. I put a couple comments in the code below about your other questions.
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Rake(Sprite):
def __init__(self):
Sprite.__init__(self)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
class Sprite_Mouse_Location(Sprite):
def __init__(self,x,y):
Sprite.__init__(self)
self.rect = pygame.Rect(x,y,1,1)
print(self.rect)
pygame.init()
window = display.set_mode( (800,600) )
sheldon = Sheldon()
sheldon.rect = (10,10) # (10,10) is not a rect!
# pygame.Rect(0,0,10,10) is a rect!
all_sprites = Group(sheldon)
rake = Rake()
rake.rect = (400,250) # Same as above comment.
all_sprites.add(rake)
x,y = pygame.mouse.get_pos() # If you are going to make the mouse
mouse_sprite = Sprite_Mouse_Location(x,y) # a sprite, then update your mouse position in the loop.
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEMOTION :
# You could have this instead.
# mouse_sprite.rect.x, mouse_sprite.rect.y = pygame.mouse.get_pos()
# Then move the for loop below to be out of the event loop.
for s in all_sprites :
if pygame.sprite.collide_rect(s,mouse_sprite):
print("hit")
window.fill( (0,0,0) )
all_sprites.update()
all_sprites.draw(window)
display.update()
I'd also recommend going through a pygame tutorial as well as looking at other people's pygame code and trying to understand it line by line. Both those tips helped me out. Good luck.
thanks to alanxoc3 and a new day, code working
posting here because I'm sure another newbie is going to want to do this type of mouse click
# to create a group of sprites and use the mouse over them individually
import pygame
from pygame import *
from pygame.locals import *
from pygame.sprite import *
class Sheldon(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups) #have to have groups here, not documented well
self.image = transform.scale(image.load('sheldon.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect(smouse, self): #could not use sritecollison because it would flag whole group
print('hit sheldon') # this if the check mouse that its working
#------------------------------------------
class Rake(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self,self.groups)
self.image = transform.scale(image.load('rake.jpg').convert(),(230,310))
self.rect = self.image.get_rect()
def clickCheck(self,smouse):
if pygame.sprite.collide_rect( smouse, self ):
print('hit rake')
#-------------------------------------------
class Sprite_Mouse_Location(Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.rect = pygame.Rect( 0 , 0 , 1 , 1 ) # no updating needed here
#--------------------------------------------
pygame.init()
window = display.set_mode( (800,600) )
mouse_sprite = Sprite_Mouse_Location() # had to create a mouse as sprite to use the sprite collision feature
all_sprites = pygame.sprite.Group() # have to declare first, no well documented
Sheldon.groups = all_sprites # better than all_sprites = Group(sheldon), and can be assigned before instantiation of sheldon!!!
sheldon = Sheldon()
sheldon.rect = (10,10,230,310)
Rake.groups = all_sprites
rake = Rake()
rake.rect = (400,250,230,310)
running = True
while running == True:
for event in pygame.event.get():
if event.type == QUIT or event.type == KEYUP and event.key == K_ESCAPE :
pygame.quit()
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
mouse_sprite.rect.x , mouse_sprite.rect.y = pygame.mouse.get_pos() # have to have this to update mouse here or get wrong location
for s in all_sprites: #can't have outside the event or it will continuously check
s.clickCheck(mouse_sprite)
window.fill( (0,0,0) )
all_sprites.update() # have to have this for group sprites
all_sprites.draw(window) # have to have this for sprites
display.flip()

Resources