I'm new to pygame and now wanted to blit a background image with a text on top of it. Both together should appear on the screen for only half a second, then both should be gone again. The background image is meant to be full-screen.
What I'm stuck on now: every time, a black screen appears shortly before text and image are visible, they disappear quickly again.
pg.init()
info = pg.display.Info()
window = pg.display.set_mode((info.current_w, info.current_h), pg.FULLSCREEN)
font = pg.font.SysFont("Arial", 33, bold=True)
text = font.render(text, True, (0, 0, 0))
background = pg.image.load('temp.png')
window.blit(background, [0, 0])
window.blit(text, text.get_rect(center = window.get_rect().center))
pg.display.flip()
sleep(0.5)
pg.quit()
I feel like this has to be possibly quite easily, still I haven't found out how to do it just yet.
I'm assuming you are using the time.sleep function in your code. This function isn't good for GUIs since it delays the program for a given amount of time without checking for events and updating the display. This is why a black screen appears. So to fix your issue, I wrote a function called wait that waits a given amount of time without freezing the game.
The wait Function:
def wait(ms):
start = pg.time.get_ticks()
running = True
while running:
for event in pygame.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit(0)
now = pg.time.get_ticks()
if now - start == ms:
running = False
blit_background()
Explanation
It takes one parameter: ms, which is the amount of time that you want the program to wait in milliseconds (1 second = 1000 milliseconds). In the function, I set a variable called start to pygame.time.get_ticks(), which returns the current time. Then, inside a while loop, I created an event loop which checks for the pygame.QUIT event so that if the user tries to close the game while the function is still running, the game will respond and quit the program. After the event loop, I set a variable called now to pygame.time.get_ticks() to get the current time. Then I checked if now (the current time) subtracted by start (the start time) is equal to ms (the given amount of waiting time in milliseconds). This checks if the given amount of time has passed. If it has, the while loop ends. If not, the while loop keeps running until that condition is True.
I also wrote another function called blit_background, which displays the background and the text on the screen. I am calling this function inside the wait function so that the screen can display the background and the text while also waiting for the given amount of time to pass.
The blit_background Function:
def blit_background():
window.blit(background, [0, 0])
window.blit(text, text.get_rect(center=window.get_rect().center))
pg.display.flip()
Full Modified Code:
import pygame as pg
import pygame.time
import sys
pg.init()
info = pg.display.Info()
WIDTH = info.current_w
HEIGHT = info.current_h
window = pg.display.set_mode((WIDTH, HEIGHT), pg.FULLSCREEN)
font = pg.font.SysFont("Arial", 33, bold=True)
text = font.render("text", True, (0, 0, 0))
background = pg.image.load('temp.png')
background = pg.transform.scale(background, (WIDTH, HEIGHT))
def blit_background():
window.blit(background, [0, 0])
window.blit(text, text.get_rect(center=window.get_rect().center))
pg.display.flip()
def wait(ms):
start = pg.time.get_ticks()
running = True
while running:
for event in pygame.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit(0)
now = pg.time.get_ticks()
if now - start == ms:
running = False
blit_background()
wait(500)
pg.quit()
sys.exit(0)
Related
I am trying to write a little digits game using pygame. The idea of the game is to guess the four-digit number randomly chosen by computer. But I am stuck at the very beginning I started by creating all the essential elements: colours, fonts, surfaces, etc. I used blit to 'simulate' computer choice and to show the user's guess. And interestingly enough, not all the inputs are displayed. E.g. '1234' and '9999' is displayed. However, '5738' and '7365' are not. Looking forward to hearing opinions of the experienced users.
import random
import pygame
pygame.init()
width = 900
height = 500
black = (0,0,0)
pastel_blue = (200,205,230)
win = pygame.display.set_mode((width,height))
pygame.display.set_caption("Bulls and Cows")
digit_font = pygame.font.SysFont('comicsans', 30)
a = (random.randint(1000, 10000))
print(a)
def display():
win.fill((pastel_blue))
number = digit_font.render("_ _ _ _", 1, black)
win.blit(number, (width//2-number.get_width()//2, height//4))
pygame.display.update()
display()
def guess_number():
global c
c = input("Guess the number: ")
guess_number()
def guess_display():
text = digit_font.render(c, 1, black)
print(text.get_width()//2)
win.blit(text, [width//2-text.get_width()/2, 300]) #this seems to be the part that doesn't work correctly
pygame.display.update()
pygame.time.delay(2000)
guess_display()
You have to handle the events in the application loop. 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.
def guess_display():
text = digit_font.render(c, 1, black)
print(text.get_width()//2)
win.blit(text, [width//2-text.get_width()/2, 300]) #this seems to be the part that doesn't work correctly
pygame.display.update()
pygame.event.pump() # <---
pygame.time.delay(2000)
However, the usual way is to use an application loop. Also see Why is my PyGame application not running at all?:
def guess_display():
text = digit_font.render(c, 1, black)
print(text.get_width()//2)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.blit(text, [width//2-text.get_width()/2, 300])
pygame.display.update()
Also see:
Why is my display not responding while waiting for input?
Why does pygame.display.update() not work if an input is directly followed after it?
How to create a text input box with pygame?
When I run the code, it does not show any error. The rectangle just doesn't appear. How can that be fixed?
The function responsible is render() with the line pygame.draw.rect(win, (0, 0, 0), (100, 100, 50, 50)).
import pygame
w, h = 1000, 600
caption = "Game"
background = (255, 255, 255)
running = True
clock = pygame.time.Clock()
FPS = 60
win = pygame.display.set_mode((w, h))
pygame.display.set_caption(caption)
def start():
# This function runs only once at the start of the game
pass
def logic():
# This function runs every frame. This function contains the game's logic.
pass
def render():
# This function runs every frame. This function contains code for drawing
# everything to the screen.
pygame.draw.rect(win, (0, 0, 0), (100, 100, 50, 50))
def main():
global running
start()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
logic()
win.fill(background)
render()
pygame.display.update()
if __name__ == "__main__":
main()
pygame.quit()
quit()
Sorry for bothering everyone. I was using Atom to write and run this code but every time I used to run this code in Atom, using script package, it used to show a blank screen. Solution is to use build-python package (its a package that works, unlike script) instead. Sorry, I should’ve been more specific.
I am creating a game with pygame and I am using pygame.event.set_grab(True) to keep cursor locked in the game window (it keeps all keyboard input in the window also), but when the program crashes (either because of syntax error or some other exception) the set_grab stays turned on and it is impossible to turn off the program afterwards. (fortunately I am using Linux so i can access console that overrides everything so i can turn it off manually)
I am wondering if it is possible to make some error handling which will turn it off (or kills the program) or if there is a better way to keep just mouse inputs in the window. (so it is possible to alt+f4)
import pygame
pygame.init()
size = (600, 700)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True) #this is turned on with the initialization
#(doesnt have to be) of the game
When the game crashes it transforms into the usual black window with the exception you cant do anything.
Thank you.
Edit:
The full code:
#coding: utf-8
import pygame
import random
random.randint(0,2)
#TODO:
#Vymyslieť systém na čakanie framov
#Upraviť zmenu rýchlosti hráčovho náboja
##Pygame init
pygame.init()
size = (600, 700)
possible_sizes=[[600,700],[900,1050],[1200,1400],[1800,2100]] #ASI 1200,1400 obrázky a potom downscale?? (ak vôbec zmena rozlisenia..)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True)
#<VARIABLES>
##<COLORS>
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
##</COLORS>
##<IMAGES> #"../meteo_data/img/"
bg1=[pygame.image.load("../meteo_data/img/bg1.png"),700-5770,True]
#backgrounds.append([pygame.image.load("img/bg2.png"),[600,1924]])
#backgrounds.append([pygame.image.load("img/bg3.png"),[600,1924]])
img_crosshair=pygame.image.load("../meteo_data/img/crosshair.png").convert()
#Ships
img_player=pygame.image.load("../meteo_data/img/ships/player.png").convert()
img_enemy1=pygame.image.load("../meteo_data/img/ships/enemy1.png").convert()
#Bullets
img_b1=pygame.image.load("../meteo_data/img/bullets/bullet.png").convert()
img_player.set_colorkey(BLACK)
img_enemy1.set_colorkey(BLACK)
img_crosshair.set_colorkey(BLACK)
##</IMAGES>
##<SOUNDS>
##</SOUNDS>
menu_game=1 #Nula pre menu , jedna pre hru?? , medzi nula a jedna ostatné??
esc_menu=False
fire=False
level=-1
level_ended=True
## def=0 def=0
##<BULLET TYPES> #[IMAGE,DAMAGE,SPEED,PENETRATION,relX,relY] /relX a relY vziať z relX a relY lode.
B_default=[img_b1,3,4,False,0,0]
##</BULLET TYPES>
##<SHIP TYPES> #[IMAGE,HEALTH,SPEED,RELOAD,X,Y,relX,relY,BULLET_TYPE] /relX a relY je pre bullet
##</SHIP TYPES>
##<LEVELS>
level1=[bg1]
master_level=[level1]
##</LEVELS>
#</VARIABLES>
#<FUNCTIONS>
##<SPRITES>
class bullet(pygame.sprite.Sprite):
def __init__(self,bullet_type):
pygame.sprite.Sprite.__init__(self)
self.image=bullet_type[0]
self.dmg=bullet_type[1]
self.speed=bullet_type[2]
self.penetration=bullet_type[3] ##Prestrelí viac ENIMÁKOV ? True/False
self.rect = self.image.get_rect()
self.rect.x=bullet_type[4] ##Vypočítať pri vystrelení (ship pos -/+ ship.bullet_x(y)) (pre každý typ lode zvlášť)
self.rect.y=bullet_type[5]
self.image.set_colorkey(BLACK)
class ship(pygame.sprite.Sprite):
def __init__(self,ship_type):
pygame.sprite.Sprite.__init__(self)
self.image=ship_type[0]
self.hp=ship_type[1]
self.speed=ship_type[2] ##0 Pre hráča
self.reload=ship_type[3] ##Rýchlosť streľby (koľko framov čakať) 0 = každý frame bullet
self.rect=self.image.get_rect()
self.rect.x=ship_type[4]
self.rect.y=ship_type[5]
self.bullet_x=ship_type[6]
self.bullet_y=ship_type[7]
self.b_type=ship_type[8]
self.image.set_colorkey(BLACK)
class barrier(pygame.sprite.Sprite):
def __init__(self,coord):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.Surface([700,40])
self.image.fill(WHITE)
self.rect=self.image.get_rect()
self.rect.x=coord[0]
self.rect.y=coord[1]
bullets=pygame.sprite.Group()
ships=pygame.sprite.Group()
barriers=pygame.sprite.Group()
player_b_type=B_default
player_b_type[2]=player_b_type[2]*(-1)
player=ship([img_player,100,0,10,279,650,15,3,player_b_type]) ##PLAYER SHIP
wait=player.reload
barrier_top=barrier([-50,-400])
barrier_bottom=barrier([-50,900])
barriers.add(barrier_top)
barriers.add(barrier_bottom)
##</SPRITES>
#</FUNCTIONS>
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button==1:
fire=True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button==1:
fire=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
if not esc_menu:
esc_menu=True
pygame.event.set_grab(False)
else:
esc_menu=False
pygame.event.set_grab(True)
coord=pygame.mouse.get_pos()
if menu_game==0:
screen.fill(WHITE) #BG
elif menu_game==1:
#GAME LOGIC
if level_ended:
level=level+1
bg1_y=master_level[level][0][1]
level_ended=False
bg1_y=bg1_y+2
player.rect.x=coord[0]-20
pygame.sprite.groupcollide(barriers,bullets,False,True)
pygame.sprite.groupcollide(barriers,ships,False,True)
if fire:
if wait==0:
bullet_modified=player.b_type
bullet_modified[4]=player.rect.x+player.bullet_x
bullet_modified[5]=player.rect.y+player.bullet_y
b=bullet(bullet_modified)
bullets.add(b)
wait=player.reload
else:
wait=wait-1
#RENDERING
screen.fill(BLACK)
screen.blit(master_level[level][0][0],[0,bg1_y]) #BG
screen.blit(player.image,player.rect)
for naboj in bullets:
screen.blit(naboj.image,naboj.rect)
naboj.rect.y=naboj.rect.y+naboj.speed
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
pygame.quit()
#NOTES:
# Dlzka lvl sa urci vyskou bg (5760 px == 48 sec - 1. lvl)
#189 -
#
So, the problem there is that if some exception happens in the middle of that code, pygame.quit() is never called.
All you have to do is to set a try ... fynaly block around your Pygame code, and put the pygame.quit() call on the finally block.
For that I suggest some reformatting which will also improve the modularity of your code, which is to enclose all that code you put on the module level inside a function.
So:
...
def main():
done = False
while not done:
...
for naboj in bullets:
...
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
try:
main()
finally:
pygame.quit()
In this way, any unhandled exception within the code run in main (or in any other function called from it), as well as well behaved main-loop termination, will immediately run the code within the finally block: Pygame is shut down, along with its event handling, and you get the error traceback on the terminal enabling you to fix the code.
update the finally hint is also essential for anyone making a game with pygame that uses fullscreen, regardless of event_grab.
I'm making a small game using pygame. When the game starts I want to display "start" and vanish it after a few seconds. How to do that?
First you need a timer variable (there are other questions about timers, so I won't explain them here). I'm just counting the frames in the following example.
To remove the text abruptly you can just keep blitting it until the time is up.
if timer > 0:
screen.blit(txt_surf, (position))
The slowly disappearing text can be achieved by filling the text surface with white and the current alpha value (which is reduced each frame) and by passing the pg.BLEND_RGBA_MULT special flag. That will affect only the alpha channel of the surface.
txt_surf.fill((255, 255, 255, alpha), special_flags=pg.BLEND_RGBA_MULT)
Also, use a copy of the original text surface, otherwise it would subsequently reduce the alpha of the previously modified surface and the text would disappear too quickly.
import pygame as pg
def main():
pg.init()
clock = pg.time.Clock()
screen = pg.display.set_mode((640, 480))
font = pg.font.Font(None, 64)
orig_surf = font.render('Enter your text', True, pg.Color('royalblue'))
txt_surf = orig_surf.copy()
alpha = 255 # The current alpha value of the surface.
timer = 20 # To get a 20 frame delay.
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
if timer > 0:
timer -= 1
else:
if alpha > 0:
# Reduce alpha each frame, but make sure it doesn't get below 0.
alpha = max(0, alpha-4)
# Create a copy so that the original surface doesn't get modified.
txt_surf = orig_surf.copy()
txt_surf.fill((255, 255, 255, alpha), special_flags=pg.BLEND_RGBA_MULT)
screen.fill((30, 30, 30))
screen.blit(txt_surf, (30, 60))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
Please try adding below code:
for i in range(whatevernumberbutnotover1000):
mytext.set_alpha(i)
Okay, I'am trying to create a Tom and Jerry game with the pygame library.
The game focuses on catching mice by clicking on them as they appear in their holes. The problem
is that sometimes a cat appears instead of a mouse and should the player erroneously click on the
cat (s)he looses all earned points, but the game continues.
The mouse is an image of a mouse and the cat is an image of an cat.
If you click on the mouse, you get mouse, otherwise the cat gets the points.
The code is a mess, that's because I don't know what I'am doing and just set an another event loop because then it works, because it runs after I create the mouse. It works to click on the mouse but then you click somewhere else and after that it's like you did not clicked on the mouse.
The mouse is created in a loop and is supposed to wait for 5 seconds and if you click on the mouse within these seconds then an appropriate message prints out in the console ,,Jerry clicked!" else "1 click". If you don't click on the mouse within 5 seconds a image covers the mouse so she disappears.
Now, what I'am trying to do right now is to print the message 1 click when the player does not click on anything but print 1 click jerry clicked when the player clicks on the mouse. I have a image of the mousehole and then I put the mouse on the mousehole, that is, on an another image.
This code works with one image at least:
pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()
x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare , ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time
running = True
while (running):
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
# Set the x, y postions of the mouse click
x, y = event.pos
if redSquare.get_rect().collidepoint(x, y):
print('clicked on image')
#loop over, quite pygame
pygame.quit()
My problem is that, when I click on the mouse and then I don't click on the mouse I can't click on the mouse again at another position.
So what's wrong? What I'am doing wrong here?
Here is my code:
import pygame
from pygame import *
from random import *
init()
run = True
screen = (800,800)
screen = display.set_mode(screen)
xpos = 0
ypos = 0
mouseorcatxpos = 5
mouseorcatypos = 0
mousehole = image.load("mousehole.png").convert()
cat = image.load("tom.png")
jerry = image.load("jerry.png")
def makeholes():
global ypos
global xpos
for holey in range(1,9):
for holex in range(1,9):
screen.blit(mousehole,(xpos,ypos))
display.flip()
xpos += 100
ypos += 100
xpos = 0
def mouseorcat():
global xpos
mouseorcatxpos = 5
ypos = 0
for mousecaty in range(1,9):
pygame.event.pump()
for mousecatx in range(1,9):
randommouse = randint(1, 3)
randomcat = randint(1, 10)
if(randommouse == 2):
screen.blit(jerry, (mouseorcatxpos, ypos))
display.flip()
for event in pygame.event.get():
if (event.type == MOUSEBUTTONDOWN):
if jerry.get_rect().collidepoint(xpos, ypos) == False:
print("l clicked!")
x, y = event.pos
if jerry.get_rect().collidepoint(xpos, y):
print("JERRY CLICKED!!")
x, y = event.pos
print(x, y)
time.wait(5000)
#screen.blit(mousehole, (mouseorcatxpos - 5, ypos))
display.flip()
elif(randomcat == 2):
screen.blit(cat, (mouseorcatxpos, ypos))
display.flip()
time.wait(1500)
screen.blit(mousehole, (mouseorcatxpos-5, ypos))
display.flip()
mouseorcatxpos += 100
mouseorcatxpos = 0
ypos += 100
makeholes()
while run == True:
for event in pygame.event.get():
mouseorcat()
if event.type == QUIT:
run = False
I rewrote your game to show you how I would do it.
To keep track of the time and to limit the framerate I used a pygame.time.Clock and a timer variable. The clock returns the time in milliseconds since clock.tick was called the last time, which is used to increase the timer variable. The cat just replaces the mouse after two seconds and the mouse is set to a new position. I use pygame.Rects to store the positions, but you could also use lists or tuples.
import sys
import random
import pygame
pygame.init()
size = (800, 800)
screen = pygame.display.set_mode(size)
# Images replaced by pygame.Surface. Do that too
# in the future before you post your code.
mousehole = pygame.Surface((40, 40)).convert()
mousehole.fill(pygame.Color(30, 30, 30))
cat = pygame.Surface((40, 40)).convert()
cat.fill(pygame.Color(110, 110, 130))
jerry = pygame.Surface((40, 40)).convert()
jerry.fill(pygame.Color(190, 130, 0))
# Create the background image and blit the holes.
background = pygame.Surface(size).convert()
for holey in range(8):
for holex in range(8):
background.blit(mousehole, (holex*100, holey*100))
def new_position():
"""Return a random position between 0-700 in steps of 100."""
return (random.randrange(0, 701, 100), random.randrange(0, 701, 100))
def main():
fps = 30
clock = pygame.time.Clock()
jerry_rect = jerry.get_rect() # Stores jerry's position and size.
jerry_rect.topleft = new_position() # New random position.
# The cat is outside of the screen first.
cat_rect = cat.get_rect(topleft=(-100, -100))
points = 0
timer = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if jerry_rect.collidepoint(event.pos):
points += 1
print('Jerry caught! Points:', points)
timer = 0
jerry_rect.topleft = new_position()
else:
print('Missed. Points:', points)
# Run logic.
timer += clock.tick(fps) / 1000 # timer + seconds since last tick.
if timer > 2: # Cat catches mouse after 2 seconds.
cat_rect.topleft = jerry_rect.topleft
jerry_rect.topleft = new_position()
timer = 0
points = 0
print('Tom caught Jerry.')
# Draw.
# Clear the screen by blitting the bg.
screen.blit(background, (0, 0))
screen.blit(jerry, jerry_rect)
screen.blit(cat, cat_rect)
pygame.display.flip()
if __name__ == '__main__':
main()
pygame.quit()
sys.exit()
Side notes:
Don't use star imports (from module import *), because that can make code harder to read. If you want you can use from pygame.locals import *, if it's the only star import.
Don't use global variables, because they can make code harder to read, understand and maintain. Pass variables to functions as arguments and then return the result.
Update: Some notes about your program:
The first big problem is that your game has two event loops and the important one is deeply nested inside of two other for loops and a if. The event loop should be directly under the main while loop (one indentation level (when you have more experience you can put it into a function or class method)).
The two for loops seem to have the purpose to let the code run until randommouse or randomcat are 2. To run code until a condition is met is the purpose of a while loop. But in this case you should better just pick a random number and write the if/elif conditions so that they always apply. For example, you want a 2/3 chance for mouse and 1/3 for a cat,
random_number = random.randint(1, 3)
if random_number < 3:
print("2/3 probability. It's a mouse")
else:
print("1/3 probability. It's a cat")
Or use random.choice with a list:
>>> random.choice(['mouse', 'mouse', 'cat'])
'mouse'
time.wait(5000) shouldn't be used because the game just hangs in this time. You can't even close the window. Limit the framerate and get the time since the last tick with a pygame.time.Clock.
pygame.event.pump() is not needed.
If you call get_rect() without an argument, the rect is positioned at (0, 0).
if jerry.get_rect().collidepoint(xpos, y):
That's the reason why clicking on jerry only works in the top row, and because you use the global xpos here. Since xpos is 0, the whole top row counts as Jerry.
You can pass coordinates to get_rect like so (you can also use center or other args instead of topleft):
jerry_rect = jerry.get_rect(topleft=(50, 100))
I'm sorry but I don't think I can simply fix your code. I've tried it several times, but I always end up re-writing it completely.
I begin by extracting the event loop out of the two nested for loops, then remove these loops, create rects for the mouse and cat, fix the collision detection, add a timer and so on. Take a close look at my example and try to rewrite your game in a similar way, and keep asking questions if you don't understand something.