I want to start my main game loop with surface.fill(WHITE) to repaint my display white, but every surface from the last frame still appears.
To demonstrate, the following example blits a red surface on a green surface. The blit is created by an event trigger every 1 second. However, the red surface does not disappear when it is not being triggered by the event.
import pygame as pg
pg.init()
GREEN, WHITE, RED = (60, 120, 60), (255,255,255), (120, 60, 60)
screen = pg.display.set_mode((200, 200))
box = pg.Surface((150, 150))
box.fill(GREEN)
running = True
EVENT_SECOND_TIMER = pg.event.custom_type()
pg.time.set_timer(EVENT_SECOND_TIMER, 1000)
while running:
screen.fill(WHITE)
for event in pg.event.get():
# Draw circle every second
if event.type == EVENT_SECOND_TIMER:
surf = pg.Surface((50,50))
surf.fill(RED)
box.blit(surf, (50, 50))
screen.blit(box, (25, 25))
pg.display.update()
If you want to draw something permanently, you have to draw it in the application loop. If you want to see surf instead of box for an image, you have to set a state variable and draw the scene depending on the state. Reset the state variable after drawing the scene:
import pygame as pg
pg.init()
GREEN, WHITE, RED = (60, 120, 60), (255,255,255), (120, 60, 60)
screen = pg.display.set_mode((200, 200))
clock = pg.time.Clock()
box = pg.Surface((150, 150))
box.fill(GREEN)
surf = pg.Surface((50,50))
surf.fill(RED)
EVENT_SECOND_TIMER = pg.event.custom_type()
pg.time.set_timer(EVENT_SECOND_TIMER, 1000)
running = True
while running:
clock.tick(60)
draw_surf = False
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == EVENT_SECOND_TIMER:
draw_surf = True
screen.fill(WHITE)
screen.blit(box, (25, 25))
if draw_surf:
screen.blit(surf, (50, 50))
pg.display.update()
pg.quit()
Related
I'm working on this pygame game and i'm just getting started but got a bit confused because i want the image to move in the x-axis along with the mouse but when i run the program i want the image to show up at the center or the 'floor' but appears at the left side instead. This is my code and a screenshot of what's happening.
import pygame
import sys
pygame.init()
pygame.mixer.init()
WIDTH, HEIGHT = 400, 500
FPS = 60
TITLE = 'FOOD DROP'
SIZE = 190
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE_SKY = (152, 166, 255)
# Display
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)
# Surfaces
floor_surface = pygame.Surface((WIDTH, 100))
floor_surface.fill(BLUE_SKY)
floor_rect = floor_surface.get_rect(midbottom=(200, 500))
# Images
LOAD_DITTO = pygame.image.load('Graphics/ditto.png')
DITTO = pygame.transform.scale(LOAD_DITTO, (SIZE, SIZE))
# Time
CLOCK = pygame.time.Clock()
class Figure:
def draw_figure(self, mouse_x):
SCREEN.blit(DITTO, (mouse_x - 90, 330))
# Game loop
SCREEN_UPDATE = pygame.USEREVENT
# main_game = Main()
figure = Figure()
running = True
while running:
CLOCK.tick(FPS)
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
SCREEN.fill(WHITE)
SCREEN.blit(floor_surface, floor_rect)
figure.draw_figure(mx)
pygame.display.update()
When i run the program, this happens:
And i want the image to appear right at the center or the x-axis, not the border, i don't know why is this happening. Just to state, that screenshot was taken when the mouse hadn't been placed over the display.
If the mouse pointer is not in the window (out of focus), the initial position of the mouse pointer is (0, 0). Therefore pygame.mouse.get_pos returns (0, 0). It is also not possible to set the mouse position with pygame.mouse.set_pos if it is not in the window.
Initialize the variables mx and mx with the center of the window. Change the mouse position only when the mouse pointer is in the window (in focus). pygame.mouse.get_focused can be used to test whether the mouse is in the window.
mx, my = SCREEN.get_rect().center
running = True
while running:
CLOCK.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if pygame.mouse.get_focused():
mx, my = pygame.mouse.get_pos()
SCREEN.fill(WHITE)
SCREEN.blit(floor_surface, floor_rect)
figure.draw_figure(mx)
pygame.display.update()
pygame.quit()
sys.exit()
The screen wont fill and it says there is no fill cmd for screen, what did I do wrong?
import pygame
pygame.init()
screen = pygame.display
set_mode((0, 0))
white = (255, 255, 255)
Square =(0, 0, 0)
loop = True
while loop:
screen.fill(white)
pygame.draw.rect(screen, Square, pygame)Rect(100, 100, 200, 200)
pygame.display.update()
There are some syntax errors in your code:
screen = pygame.display
set_mode((0, 0))
screen = pygame.display.set_mode((0, 0))
pygame.draw.rect(screen, Square, pygame)Rect(100, 100, 200, 200)
pygame.draw.rect(screen, Square, pygame.Rect(100, 100, 200, 200))
More than that, the event loop is missing. This can cause your application to freeze. See pygame.event.get() respectively pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
Correct program:
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
white = (255, 255, 255)
Square = (0, 0, 0)
loop = True
while loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
loop = False
screen.fill(white)
pygame.draw.rect(screen, Square, pygame.Rect(100, 100, 200, 200))
pygame.display.update()
This question already has an answer here:
Why is my pygame application loop not working properly?
(1 answer)
Closed 2 years ago.
I made a picture of Pacman and I am trying to make it move to the right across the screen. This is my code so far. I have the Pacman drawing but it is many shapes combined together and I don't know how to move all of them at once.
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" %(20, 20)
import pygame
pygame.init()
BLACK = (0,0,0)
YELLOW = (255, 245, 59)
WHITE = (242, 242, 242)
SIZE = (500, 500)
screen = pygame.display.set_mode(SIZE)
# Fill background
pygame.draw.rect(screen, WHITE, (0,0, 500, 500))
pygame.draw.circle(screen, YELLOW, (250,250), 100,)
pygame.draw.circle(screen, BLACK, (250,250), 100, 3)
pygame.draw.circle(screen, BLACK, (260,200), 10,)
pygame.draw.polygon(screen, WHITE, ((250,250),(500,500),(500,100)))
pygame.draw.line(screen, BLACK, (250, 250), (334, 198), 3)
pygame.draw.line(screen, BLACK, (250, 250), (315, 318), 3)
pygame.display.flip()
pygame.time.wait(5000)
You have to add an application loop. The main application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (draw pacman)
update the display by either pygame.display.update() or pygame.display.flip()
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# update position
# [...]
# clear the display
screen.fill(WHITE)
# draw the scene
pacman(px, py, dir_x)
# update the display
pygame.display.flip()
Furthermore you have to draw pacman relative to a position (x, y) and a direction (dir_x). See the example:
import pygame
pygame.init()
BLACK = (0,0,0)
YELLOW = (255, 245, 59)
WHITE = (242, 242, 242)
SIZE = (500, 500)
screen = pygame.display.set_mode(SIZE)
def pacman(x, y, dir_x):
sign_x = -1 if dir_x < 0 else 1
pygame.draw.circle(screen, YELLOW, (x, y), 100,)
pygame.draw.circle(screen, BLACK, (x, y), 100, 3)
pygame.draw.circle(screen, BLACK, (x+10*sign_x, y-50), 10,)
pygame.draw.polygon(screen, WHITE, ((x, y),(x+250*sign_x, y+250),(x+250*sign_x, y-150)))
pygame.draw.line(screen, BLACK, (x, y), (x+84*sign_x, y-52), 3)
pygame.draw.line(screen, BLACK, (x, y), (x+65*sign_x, y+68), 3)
px, py, dir_x = 250, 250, 1
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
px += dir_x
if px > 300 or px < 200:
dir_x *= -1
# clear the display
screen.fill(WHITE)
# draw the scene
pacman(px, py, dir_x)
# update the display
pygame.display.flip()
I've been watching a bunch of tutorials and new to pygame and coding in general. None of the tutorials I watched helped me with this. What i'm aiming for is for the buttons to change the button to a different colour when the user hovers over the button. Below is my entire code for the main menu so far.
import pygame
import time
import random
pygame.init()
size = (800, 600)
screen = pygame.display.set_mode(size)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
pygame.display.set_caption("Basketball Shootout")
clock = pygame.time.Clock()
#GAME INTRO
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
#print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
#Font's and text
font = pygame.font.SysFont ("Times New Norman", 60)
text = font.render ("", True, WHITE)
#Background Image
background_images = pygame.image.load("greybackground.jpg").convert()
screen.blit(background_images, [0,0])
screen.blit(text, (150, 50))
#Background Music
pygame.mixer.music.load('game.ogg')
pygame.mixer.music.set_endevent(pygame.constants.USEREVENT)
pygame.mixer.music.play()
pygame.display.update()
clock.tick(15)
#BUTTONS
screen.blit(text, (150, 50))
pygame.draw.rect(screen,(0,0,0),(300,300,205,80));
pygame.draw.rect(screen,(0,0,0),(300,400,205,80));
pygame.draw.rect(screen,(0,0,0),(300,500,205,80));
font = pygame.font.SysFont ("Times New Norman, Arial", 30)
text = font.render ("START", True, WHITE)
screen.blit(text, (340, 320))
font = pygame.font.SysFont ("Times New Norman, Arial", 30)
text = font.render ("OPTIONS", True, WHITE)
screen.blit(text, (340, 420))
font = pygame.font.SysFont ("Times New Norman, Arial", 30)
text = font.render ("ABOUT", True, WHITE)
screen.blit(text, (340, 520))
pygame.display.flip();
#Quit Pygame
game_intro()
pygame.quit
I recommend storing the button data (text surface, rect, color) in a list of lists. When the mouse moves, pygame.MOUSEMOTION events get added to the event queue. In the event loop check if the mouse moved and then iterate over your buttons and set the color of colliding buttons to the hover color, reset the others to black. Blit the rects and text surfaces in a for loop as well.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
HOVER_COLOR = (50, 70, 90)
# Don't define new font objects in your while loop (that's inefficient).
FONT = pygame.font.SysFont ("Times New Norman", 60)
# If the text surfaces and button rects don't change,
# you can define them once outside of the while loop.
text1 = FONT.render("START", True, WHITE)
text2 = FONT.render("OPTIONS", True, WHITE)
text3 = FONT.render("ABOUT", True, WHITE)
rect1 = pygame.Rect(300,300,205,80)
rect2 = pygame.Rect(300,400,205,80)
rect3 = pygame.Rect(300,500,205,80)
# The buttons consist of a text surface, a rect and a color.
buttons = [
[text1, rect1, BLACK],
[text2, rect2, BLACK],
[text3, rect3, BLACK],
]
def game_intro():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.MOUSEMOTION:
for button in buttons:
# button[1] is the rect. Use its collidepoint method with
# the `event.pos` (mouse position) to detect collisions.
if button[1].collidepoint(event.pos):
# Set the button's color to the hover color.
button[2] = HOVER_COLOR
else:
# Otherwise reset the color to black.
button[2] = BLACK
screen.fill((20, 50, 70))
# Draw the buttons with their current colors at their rects.
# You can unpack the button lists directly in the head of the loop.
for text, rect, color in buttons:
pygame.draw.rect(screen, color, rect)
screen.blit(text, rect)
pygame.display.flip()
clock.tick(15)
game_intro()
pygame.quit()
I'd actually recommend defining a Button class, but I'm not sure if you're already familiar with classes/object-oriented programming.
The buttons also need callback functions to actually do something. Take a look at my answer here: https://stackoverflow.com/a/47664205/6220679
I would recommend creating a function for this:
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + w > mouse[0] > x and y + h > mouse[1] > y:
gameDisplay.blit(active, (x, y))
else:
gameDisplay.blit(inactive, (x, y))
and then in you game intro call you function like this:
def game_intro():
intro = True
while intro:
for event in pygame.event.get():
#print(event)
if event.type == pygame.QUIT:
pygame.quit()
quit()
# For example
button(100, 350, 195, 80, startBtn, startBtn_Hover)
This is what each parameter means:
x: x-coordinate of button
y: y-coordinate of button
w: width of button
h: height of button
active: picture when mouse is hovered over button
inactive: picture when button is idle
I am new to python and pygame and I'm trying to move a image over drawed rectangles with changing colors. Whenever I run this code the image moves but it creates a track of the image.
I know that I need to blit the background of the image in the game loop but how can I blit the background if the background is not an image?
Or do I need to draw my rectangles in a different way?
Full code:
import pygame
pygame.init()
screen = pygame.display.set_mode((600,200))
pygame.draw.rect(screen, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(screen, (255,192,0), [200, 0, 200, 200])
clock = pygame.time.Clock()
# load your own image here (preferably not wider than 30px)
truck = pygame.image.load('your_image.png').convert_alpha()
class Truck:
def __init__(self, image, x, y, speed):
self.speed = speed
self.image = image
self.pos = image.get_rect().move(x, y)
def move(self):
self.pos = self.pos.move(self.speed, 0)
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
game_loop()
You have to somehow redraw all the objects (rectangles) in your background at every update.
The simplest way to do this is just to call all the drawing code again, before updating your foreground object. Another way, if the background does not change after created, is to blit these background objects into a separate Surface object, and blit that one to the screen on every update.
More sophisticated ways would be to save the background under your foreground objects are prior to drawing them, and then, on the next redraw, first redraw the background and then save the background again and draw the foreground objects on the new position. It is easier to do one of the former methods.
Your code could be written like this:
import pygame
pygame.init()
SIZE = (600,200)
screen = pygame.display.set_mode(SIZE)
bg_image = None
def draw_background(screen):
global bg_image
if not bg_image:
bg_image = pygame.Surface((SIZE))
pygame.draw.rect(bg_image, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(bg_image, (255,192,0), [200, 0, 200, 200])
...
# Draw whatever you want inside this if body
screen.blit(bg_image, (0, 0))
...
class Truck:
...
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
draw_background()
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
game_loop()
You can just move the two lines that draw your rects into the main while loop:
def game_loop():
newTruck = Truck(truck, 0, 50, 1)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
newTruck.move()
# Draw the background items first, then the foreground images.
# If the rects don't cover the whole screen, you can use
# `screen.fill(some_color)` to clear it.
pygame.draw.rect(screen, (175,171,171), [0, 0, 600, 200])
pygame.draw.rect(screen, (255,192,0), [200, 0, 200, 200])
screen.blit(newTruck.image, newTruck.pos)
clock.tick(60)
pygame.display.update()
If the background should be static, you could also draw the rects onto a background surface once and then blit this surf onto the screen in the main loop as jsbueno suggests.