I would like to know how to draw images using pygame. I know how to load them.
I have made a blank window.
When I use screen.blit(blank, (10,10)), it does not draw the image and instead leaves the screen blank.
This is a typical layout:
myimage = pygame.image.load("myimage.bmp")
imagerect = myimage.get_rect()
while 1:
your_code_here
screen.fill(black)
screen.blit(myimage, imagerect)
pygame.display.flip()
import pygame, sys, os
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((100, 100))
player = pygame.image.load(os.path.join("player.png"))
player.convert()
while True:
screen.blit(player, (10, 10))
pygame.display.flip()
pygame.quit()
Loads the file player.png.
Run this and it works perfectly. So hopefully you learn something.
Images are represented by "pygame.Surface" objects. A Surface can be created from an image with pygame.image.load:
my_image_surface = pygame.load.image('my_image.jpg')
However, the pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
Use the appropriate conversion method for best performance:
image_surface = pygame.load.image('my_image.jpg').convert()
alpha_image_surface = pygame.load.image('my_icon.png').convert_alpha()
A Surface can be drawn on or blended with another Surface using the blit method. The first argument to blit is the Surface that should be drawn. The second argument is either a tuple (x, y) representing the upper left corner or a rectangle. With a rectangle, only the upper left corner of the rectangle is taken into account. It should be mentioned that the window respectively display is also represented by a Surface. Therefore, drawing a Surface in the window is the same as drawing a Surface on a Surface:
window_surface.blit(image_surface, (x, y))
window_surface.blit(image_surface,
image_surface.get_rect(center = window_surface.get_rect().center))
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
pygameSurface = pygame.image.load('apple.png').convert_alpha()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((127, 127, 127))
window.blit(pygameSurface, pygameSurface.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
pygame.image.load is bale to load most images. According to the documentation the following formats are supported: JPG, PNG, GIF (non-animated), BMP, PCX, TGA (uncompressed), TIF, LBM (and PBM), PBM (and PGM, PPM), XPM.
If you want to use images in PyGame that are loaded with other libraries, see:
PIL and pygame.image
How do I convert an OpenCV (cv2) image (BGR and BGRA) to a pygame.Surface object
For information on loading Scalable Vector Graphics (SVG) files, see:
SVG rendering in a PyGame application
Loading animated GIF files is presented at:
How can I load an animated GIF and get all of the individual frames in PyGame?
How do I make a sprite as a gif in pygame?
Or see how to load NumPy frames:
Pygame and Numpy Animations
After using blit or any other update on your drawing surface, you have to call pygame.display.flip() to actually update what is displayed.
Related
I'm using pygame (version 2.0.0) to visualize big matrices interactively. I use pygame's display.set_mode function to which I pass the SCALED flag in order to automatically scale-down the displayed image to fit the screen:
image = # a numpy 2D-array
surface = pygame.display.set_mode(image.shape, flags=pygame.SCALED)
Then, inside the event loop, I create a surfarray that resembles the numpy array, and render it using the blit function:
surfarray = pygame.surfarray.make_surface(image)
surface.blit(surfarray, (0, 0))
However, the SCALED parameter doesn't seem to work, and pygame's window has the same size as the original image (which, in particular, is much bigger than the desktop size). Am I using the SCALED parameter wrong?
According to this example, SCALED scales up the window for you. They also use the RESIZABLE flag in conjunction. Maybe it only scales things up? Or you could try combining that flag with RESIZABLE and see if that works.
I am trying to make a pygame window that can be resized but after resizing it just deletes everything on it. So I thought of a solution on my own.
pygame.init()
screen=pygame.display.set_mode((640,360),pygame.RESIZABLE)
clock=pygame.time.Clock()
screen.blit(somesurface,(0,0))
pygame.display.flip()
while True:
clock.tick(100)
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
sys.exit()
if event.type==pygame.VIDEORESIZE:
old=screen
screen=pygame.display.set_mode(event.size,pygame.RESIZABLE)
screen.blit(old,(0,0))
del old
pygame.display.flip()
But this doesnt work. The blitted surface just disappears after resizing.
I'm using python 3.8.5 and pygame 1.9.6
The main issue is that pygame.display.set_mode does not create a new surface object. It just resets the existing one. When you're 'saving' the old surface, you're just creating another reference to the same object. If you want to save the current screen surface, use surface.copy().
I updated your code to copy the screen then redraw the saved screen surface centered on the new screen. I also print the screen memory address before and after set_mode. You can see the screen address doesn't change.
import pygame
pygame.init()
screen=pygame.display.set_mode((640,360),pygame.RESIZABLE)
clock=pygame.time.Clock()
somesurface = pygame.Surface((640,360)) # new surface
somesurface.fill((255,255,255)) # fill white
pygame.draw.circle(somesurface,(100,100,255),(320,190),50) # draw blue circle
screen.blit(somesurface,(0,0)) # draw surface onto screen
pygame.display.flip()
while True:
clock.tick(100)
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
exit()
if event.type==pygame.VIDEORESIZE:
print(id(screen)) # memory address of screen object
old=screen.copy() # copy current screen to temp surface
screen=pygame.display.set_mode(event.size,pygame.RESIZABLE) # reset screen
print(id(screen)) # memory address of 'new' screen object, same address :(
screen.blit(old,((event.w-old.get_width())//2,(event.h-old.get_height())//2)) # draw back temp surface centered
del old # delete temp surface
pygame.display.flip()
I wanted a background picture for my game that would always be behind the player character and enemies. I just typed in:
background = pygame.image.load("mypicture")
at the top of my code, before my running loop. I then blit the image by typing:
screen.blit(background, (0, 0))
if I blit the image BEFORE the running loop, the game is not laggy at all, but everywhere the player character moves, it leaves an outline that never goes way, as shown
However, if I blit the image DURING the running loop, the previously mentioned tracer effect doesn't happen, but instead, the gamer runs insanely slow.
It's worth mentioning some of the code for my player character:
def Player(x, y):
screen.blit(PlayerImg, (x, y))
I call the function at the end of the running loop, right before
pygame.display.update
. The player is placed at coordinates x, y, which are decided based on input from the keyboard.
Try adding convert method: background = pygame.image.load("mypicture").convert(). This will improve performance. More info here [https://www.pygame.org/docs/ref/surface.html#pygame.Surface.convert]
And if you want your background to refresh you must call it inside the loop.
i have started a new project in python using pygame and for the background i want the bottom half filled with gray and the top black. i have used rect drawing in projects before but for some reason it seems to be broken? i don't know what i am doing wrong. the weirdest thing is that the result is different every time i run the program. sometimes there is only a black screen and sometimes a gray rectangle covers part of the screen, but never half of the screen.
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAY=pygame.display.set_mode((800,800))
pygame.display.set_caption("thing")
pygame.draw.rect(DISPLAY, (200,200,200), pygame.Rect(0,400,800,400))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
You need to update the display.
You are actually drawing on a Surface object. If you draw on the Surface associated to the PyGame display, this is not immediately visible in the display. The changes become visibel, when the display is updated with either pygame.display.update() or pygame.display.flip().
See pygame.display.flip():
This will update the contents of the entire display.
While pygame.display.flip() will update the contents of the entire display, pygame.display.update() allows updating only a portion of the screen to updated, instead of the entire area. pygame.display.update() is an optimized version of pygame.display.flip() for software displays, but doesn't work for hardware accelerated displays.
The typical PyGame application loop has to:
handle the events by calling 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 all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
limit frames per second to limit CPU usage with pygame.time.Clock.tick
import pygame
from pygame.locals import *
pygame.init()
DISPLAY = pygame.display.set_mode((800,800))
pygame.display.set_caption("thing")
clock = pygame.time.Clock()
run = True
while run:
# handle events
for event in pygame.event.get():
if event.type == QUIT:
run = False
# clear display
DISPLAY.fill(0)
# draw scene
pygame.draw.rect(DISPLAY, (200,200,200), pygame.Rect(0,400,800,400))
# update display
pygame.display.flip()
# limit frames per second
clock.tick(60)
pygame.quit()
exit()
repl.it/#Rabbid76/PyGame-MinimalApplicationLoop See also Event and application loop
simply change your code to:
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAY=pygame.display.set_mode((800,800))
pygame.display.set_caption("thing")
pygame.draw.rect(DISPLAY, (200,200,200), pygame.Rect(0,400,800,400))
pygame.display.flip() #Refreshing screen
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
it should help
I'm trying to capture a video of my game by saving each frame as an image than stitching them together using ffmpeg. I followed the example of these scripts:
http://www.pygame.org/wiki/DummyVideoDriver
http://www.pygame.org/wiki/HeadlessNoWindowsNeeded
Here's the basic logic of my code:
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.init()
pygame.display.set_mode((1,1))
screen = pygame.Surface((400, 400)).convert()
# screen becomes attribute of game object: game.screen
game = MyGame(screen)
while game.running:
game.update() # <--- updates game.screen
pygame.display.flip()
pygame.image.save(game.screen, 'frame-%03d.png' % (game.frame_num))
create_video_using_ffmpeg()
If I comment out the first line setting video driver to 'dummy', the images turn out as expected. But it opens a (small) window, so the script fails when run as a cronjob. With the line uncommented, I just get a series of blank black images.
Any idea what's going wrong?
I was able to work around this by drawing a rectangle to the base image surface serving as my screen:
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.init()
pygame.display.set_mode((1,1))
# surface alone wouldn't work so I needed to add a rectangle
screen = pygame.Surface((400, 400), pygame.SRCALPHA, 32)
pygame.draw.rect(screen, (0,0,0), (0, 0, 400, 400), 0)
# screen becomes attribute of game object: game.screen
game = MyGame(screen)
while game.running:
game.update() # <--- updates game.screen
pygame.display.flip()
pygame.image.save(game.screen, 'frame-%03d.png' % (game.frame_num))
create_video_using_ffmpeg()
Perhaps someone else can explain why this makes a difference. (Was I using the wrong surface parameters originally?)
This worked successfully as a cronjob.