This question already has an answer here:
Set the width and height of a pygame surface
(1 answer)
Closed 2 years ago.
Recently I've been trying to build a game in pygame in a low res, pixel art style.
In order to make my game usable I have to scale up my window, so here's a basic example of the code I've developed to do that, where SCALE in the value by which the whole window is scaled up, and temp_surf is the surface I blit my graphics onto before the scale function scales them up.
import sys
import ctypes
import numpy as np
ctypes.windll.user32.SetProcessDPIAware()
FPS = 60
WIDTH = 150
HEIGHT = 50
SCALE = 2
pg.init()
screen = pg.display.set_mode((WIDTH*SCALE, HEIGHT*SCALE))
pg.display.set_caption("Example resizable window")
clock = pg.time.Clock()
pg.key.set_repeat(500, 100)
temp_surf = pg.Surface((WIDTH, HEIGHT))
def scale(temp_surf):
scaled_surf = pg.Surface((WIDTH*SCALE, HEIGHT*SCALE))
px = pg.surfarray.pixels2d(temp_surf)
scaled_array = []
for x in range(len(px)):
for i in range(SCALE):
tempar = []
for y in range(len(px[x])):
for i in range(SCALE):
tempar.append(px[x, y])
scaled_array.append(tempar)
scaled_array = np.array(scaled_array)
pg.surfarray.blit_array(scaled_surf, scaled_array)
return scaled_surf
while True:
clock.tick(FPS)
#events
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
pg.quit()
sys.exit()
#update
screen.fill((0,0,0))
temp_surf.fill ((255,255,255))
pg.draw.rect(temp_surf, (0,0,0), (0,0,10,20), 3)
pg.draw.rect(temp_surf, (255,0,0), (30,20,10,20), 4)
scaled_surf = scale(temp_surf)
#draw
pg.display.set_caption("{:.2f}".format(clock.get_fps()))
screen.blit(scaled_surf, (0,0))
pg.display.update()
pg.display.flip()
pg.quit()
For this example, there is very little lag. However when I try to implement this code in my game, the fps drops from 60 to more like 10.
Is there a more efficient way of scaling up a pygame window that I don't know about? Would there be a way for my code to run more efficiently? I'm open to any suggestions.
Do not recreate scaled_surf in every frame. Creating a pygame.Surface my be an time consuming operation. Create scaled_surf once and continuously use it.
Furthermore I recommend to use pygame.transform.scale() or pygame.transform.smoothscale(), which are designed for this task:
scaled_surf = pg.Surface((WIDTH*SCALE, HEIGHT*SCALE))
def scale(temp_surf):
pg.transform.scale(temp_surf, (WIDTH*SCALE, HEIGHT*SCALE), scaled_surf)
return scaled_surf
Related
This question already has answers here:
Why is nothing drawn in PyGame at all?
(2 answers)
Closed 1 year ago.
I'm fairly new to pymunk and I wanted to make physics engine for my future games, there's just a small problem, My code won't draw the circle that pygame is trying to visualize. Here's my code.
import pygame
import pymunk
import sys
def create_item(space):
body = pymunk.Body(1, 100, body_type = pymunk.Body.DYNAMIC)
body.position = (450, 50)
shape = pymunk.Circle(body, 80)
space.add(body, shape)
return shape
def draw_items(items):
for item in items:
pygame.draw.circle(screen, item_color, item.body.position, 80)
def quit():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def display_update():
screen.fill(bg_color)
clock.tick(FPS)
space.step(1/60)
pygame.display.flip()
# Constructor
pygame.init()
# Constants and Variables
WIDTH = 900
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
FPS = 60
clock = pygame.time.Clock()
# Colors
bg_color = 30, 30, 40
item_color = 200, 200, 200
# Pymunk Variables
space = pymunk.Space()
space.gravity = (0, 500)
items = []
items.append(create_item(space))
# Loops
def main():
running = True
while running:
quit()
display_update()
draw_items(items)
main()
I don't really know what the problem is here, it doesn't give me an error or something like that and I only get a clean blank canvas with my bg color.(also sorry for the bad comments)
You have to draw the objects before updating the display
def main():
running = True
while running:
quit()
screen.fill(bg_color)
draw_items(items)
pygame.display.flip()
clock.tick(FPS)
space.step(1/60)
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().
The typical PyGame 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 (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
limit frames per second to limit CPU usage
This question already has answers here:
How do I rotate an image around its center using Pygame?
(6 answers)
Closed 2 years ago.
Im trying to make a cookie clicker - like game, and I'm trying to get the center cookie to rotate, but nothing happens :( . Before it just rotated off the screen, but then I deleted that code because I gave up, but now I've done other stuff and I kinda need the rotation now. (I just started like 2 weeks ago so don't judge) Here's my code:
import pygame, sys, time, random
pygame.init()
height = 650
width = 800
cookies = 0
cps = 0
font = pygame.font.SysFont('Arial', 25)
clock = pygame.time.Clock()
cookie_surface = pygame.image.load('/Users/cameronbitter/Python/Game/cookie.png')
cookie_surface = pygame.transform.scale(cookie_surface, (275, 275))
cookie_rect = cookie_surface.get_rect(center = (width/2, height/2))
screen = pygame.display.set_mode((width, height))
auto_clicker = pygame.image.load('/Users/cameronbitter/Python/Game/mouse_cursor.png')
auto_clicker = pygame.transform.scale(auto_clicker, (480,360))
auto_clicker_rect = auto_clicker.get_rect(topleft = (450, -100))
rotation = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.QUIT
sys.exit()
if event.type == pygame.KEYDOWN:
cookies += 1
time.sleep(0)
screen.fill((0,0,0))
cookies_text_surface_p1 = font.render("Cookies :", True, (255,255,255))
cookies_text_surface_p2 = font.render(f" {cookies}", True, (255,255,255))
pygame.transform.rotate(cookie_surface, (rotation))
if rotation >= 360:
rotation = 0
rotation += 1
print(rotation)
screen.blit(cookies_text_surface_p1,(10, 10))
screen.blit(cookies_text_surface_p2,(10, 11))
screen.blit(cookie_surface, (cookie_rect))
screen.blit(auto_clicker, (auto_clicker_rect))
pygame.display.flip()
clock.tick(60)
pygame.transform.rotate returns a new and rotated pygame.Surface object. You have to blit the new surface:
while True:
# [...]
rotated_cookie_surface = pygame.transform.rotate(cookie_surface, rotation)
rotated_cookie_rect = rotated_cookie_surface.get_rect(center = cookie_rect.center)
# [...]
screen.blit(rotated_cookie_surface, rotated_cookie_rect)
# [...]
See How do I rotate an image around its center using Pygame?
I'm making a basic game where I have a surface and everytime I click on the surface it moves 5 pixels to the right. The program is working just fine without the checkCollide(event) function, but when I put the that condition it doesn't move. What is wrong?
My code until now is this
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300,300))
def checkCollide(event):
k = 0
a,b = event.pos
x = P1[0].get_rect()
if x.collidepoint(a,b):
return True
return False
CP1 = [(150, 150)
,(155, 150)
,(160, 150)
,(165, 150)
,(170, 150)
,(175, 150)
,(180, 150)
,(185, 150)
,(190, 150)]
statp1_1 = 0
WHITE = (255,255,255)
DISPLAYSURF.fill(WHITE)
while True: # the main game loop
P1 = [pygame.image.load('PAzul.png'),CP1[statp1_1],statp1_1]
DISPLAYSURF.blit(P1[0], P1[1])
e = pygame.event.get()
for event in e:
if event.type == MOUSEBUTTONUP:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
statp1_1 +=1
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Thank you
Check your logic in these lines of your function:
x = P1[0][0].get_rect()
if x.collidepoint(a,b):
return True
return False
Your code hinges on this bit:
a = checkCollide(event)
if a:
DISPLAYSURF.fill(WHITE)
So you're never evaluating this piece to be true.
I just realized what was wrong. When I do x = P1[0].get_rect() it creates a surface with topleft at (0,0).
What I needed to do was change the position of the rectangle using x.topleft = P1[1]
I've got some tips for you. First store the rect in the P1 list (it contains only the image and the rect in the following example, but maybe you could also add the statp1_1 index to it). Now we can just move this rect, if the user clicks on it (in the example I set the topleft attribute to the next point). Read the comments for some more tips. One thing you need to fix is to prevent the game from crashing when the statp1_1 index gets too big.
import sys
import pygame
pygame.init()
DISPLAYSURF = pygame.display.set_mode((300, 300))
WHITE = (255, 255, 255)
# Don't load images in your while loop, otherwise they have to
# be loaded again and again from your hard drive.
# Also, convert loaded images to improve the performance.
P1_IMAGE = pygame.image.load('PAzul.png').convert() # or .convert_alpha()
# Look up `list comprehension` if you don't know what this is.
CP1 = [(150+x, 150) for x in range(0, 41, 5)]
statp1_1 = 0
# Now P1 just contains the image and the rect which stores the position.
P1 = [P1_IMAGE, P1_IMAGE.get_rect(topleft=CP1[statp1_1])]
clock = pygame.time.Clock() # Use this clock to limit the frame rate.
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONUP:
if P1[1].collidepoint(event.pos):
print('clicked')
statp1_1 += 1
# Set the rect.topleft attribute to CP1[statp1_1].
P1[1].topleft = CP1[statp1_1]
DISPLAYSURF.fill(WHITE)
DISPLAYSURF.blit(P1[0], P1[1]) # Blit image at rect.topleft.
pygame.display.update()
clock.tick(30) # Limit frame rate to 30 fps.
I am trying to make a 'Runner' style game in PyGame (like Geometry Dash) where the background is constantly moving. So far everything works fine, but the rendering of the background images restricts the frame rate from exceeding 35 frames per second. Before I added the infinite/repeating background element, it could easily run at 60 fps. These two lines of code are responsible (when removed, game can run at 60+fps):
screen.blit(bg, (bg_x, 0)) |
screen.blit(bg, (bg_x2, 0))
Is there anything I could do to make the game run faster? Thanks in advance!
Simplified Source Code:
import pygame
pygame.init()
screen = pygame.display.set_mode((1000,650), 0, 32)
clock = pygame.time.Clock()
def text(text, x, y, color=(0,0,0), size=30, font='Calibri'): # blits text to the screen
text = str(text)
font = pygame.font.SysFont(font, size)
text = font.render(text, True, color)
screen.blit(text, (x, y))
def game():
bg = pygame.image.load('background.png')
bg_x = 0 # stored positions for the background images
bg_x2 = 1000
pygame.time.set_timer(pygame.USEREVENT, 1000)
frames = 0 # counts number of frames for every second
fps = 0
while True:
frames += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.USEREVENT: # updates fps every second
fps = frames
frames = 0 # reset frame count
bg_x -= 10 # move the background images
bg_x2 -= 10
if bg_x == -1000: # if the images go off the screen, move them to the other end to be 'reused'
bg_x = 1000
elif bg_x2 == -1000:
bg_x2 = 1000
screen.fill((0,0,0))
screen.blit(bg, (bg_x, 0))
screen.blit(bg, (bg_x2, 0))
text(fps, 0, 0)
pygame.display.update()
#clock.tick(60)
game()
Here is the background image:
Have you tried using convert()?
bg = pygame.image.load('background.png').convert()
From the documentation:
You will often want to call Surface.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.
I am new into Python and pyGame and i have a problem with scaling an image.
I want to zoom an image in pygame.
The pygame documentation claims that
pygame.transform.scale()
should scale to a new resolution.
But in my example below it does not work - it crops the image instead of resizing it!?
What am i doing wrong?
#!/usr/bin/env python3
# coding: utf-8
import pygame
from pygame.locals import *
# Define some colors
BLACK = (0, 0, 0)
pygame.init()
# Set the width and height of the screen [width, height]
screen = pygame.display.set_mode((1920, 1080))
pic = pygame.image.load('test.jpg').convert()
pic_position_and_size = pic.get_rect()
# Loop until the user clicks the close button.
done = False
# Clear event queue
pygame.event.clear()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == QUIT:
done = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
done = True
# background in black
screen.fill(BLACK)
# Copy image to screen:
screen.blit(pic, pic_position_and_size)
# Update the screen with what we've drawn.
pygame.display.flip()
pygame.display.update()
pygame.time.delay(10) # stop the program for 1/100 second
# decreases size by 1 pixel in x and y axis
pic_position_and_size = pic_position_and_size.inflate(-1, -1)
# scales the image
pic = pygame.transform.scale(pic, pic_position_and_size.size)
# Close the window and quit.
pygame.quit()
pygame.transform.scale() does not work very well for your case. If you shrink a Surface by such a small amount, the algorithm just crops the last column and row of pixels. If you now repeat this process over and over again with the same Surface, you get the strange behaviour you see.
A better approach would be to keep a copy of your original Surface around, and use that for creating the scaled image. Also, using smoothscale instead of scale may also lead to a better effect; it's up to you if you want to use it.
Here's a "fixed" version of your code:
#!/usr/bin/env python3
# coding: utf-8
import pygame
from pygame.locals import *
# Define some colors
BLACK = (0, 0, 0)
pygame.init()
# Set the width and height of the screen [width, height]
screen = pygame.display.set_mode((1920, 1080))
org_pic = pygame.image.load('test.jpg').convert()
pic_position_and_size = org_pic.get_rect()
pic = pygame.transform.scale(org_pic, pic_position_and_size.size)
# Loop until the user clicks the close button.
done = False
# Clear event queue
pygame.event.clear()
# -------- Main Program Loop -----------
while not done:
for event in pygame.event.get():
if event.type == QUIT:
done = True
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
done = True
# background in black
screen.fill(BLACK)
# Copy image to screen:
screen.blit(pic, (0,0))
# Update the screen with what we've drawn.
pygame.display.flip()
pygame.display.update()
pygame.time.delay(10) # stop the program for 1/100 second
# decreases size by 1 pixel in x and y axis
pic_position_and_size = pic_position_and_size.inflate(-1, -1)
# scales the image
pic = pygame.transform.smoothscale(org_pic, pic_position_and_size.size)
# Close the window and quit.
pygame.quit()