So I'm making a 2D pixel art game in pygame and as you could assume, all my sprite textures appear very small. I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually or messing up the coordinates. Every sprite will move on a grid: one unit is 16x16 pixels and when my player sprite moves, for example, it will just move over in a direction 16 pixels.
Here's my main script:
import sys
from pygame.locals import *
import pygame
from game.sprites import Ghost
pygame.init()
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 640
DES_WIDTH = 64
DES_HEIGHT = 64
COL_BG = (46, 48, 55)
COL_FG = (235, 229, 206)
X = 1000
Y = 1000
win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Through The Doors")
running = True
paused = False
# INITIALIZE SPRITES
player = Ghost()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
clock = pygame.time.Clock()
while running:
clock.tick(30)
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.go_right()
elif keys[pygame.K_LEFT]:
player.go_left()
elif keys[pygame.K_UP]:
player.go_up()
elif keys[pygame.K_DOWN]:
player.go_down()
pygame.display.flip()
pygame.quit()
I do have more sprites I am going to load in, but I would like to resolve the scaling issue first.
I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually [...]"
No there is no way. You have to scale each coordinate, each size and each surface individually. PyGame is made for images (Surfaces) and shapes in pixel units. Anyway up scaling an image will result in either a fuzzy, blurred or jagged (Minecraft) appearance.
Is there a way I could make a separate surface and just put that on top of the base window surface, and just scale that?
Yes of course.
Create a Surface to draw on (win). Use pygame.transform.scale() or pygame.transform.smoothscale() to scale it to the size of the window and blit it to the actual display Surface (display_win):
display_win = pygame.display.set_mode((WINDOW_WIDTH*2, WINDOW_HEIGHT*2))
win = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
while running:
# [...]
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
# [...]
scaled_win = pygame.transform.smoothscale(win, display_win.get_size())
# or scaled_win = pygame.transform.scale(win, display_win.get_size())
display_win.blit(scaled_win, (0, 0))
pygame.display.flip()
Minimal example: repl.it/#Rabbid76/PyGame-UpScaleDisplay
Related
So I'm making a 2D pixel art game in pygame and as you could assume, all my sprite textures appear very small. I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually or messing up the coordinates. Every sprite will move on a grid: one unit is 16x16 pixels and when my player sprite moves, for example, it will just move over in a direction 16 pixels.
Here's my main script:
import sys
from pygame.locals import *
import pygame
from game.sprites import Ghost
pygame.init()
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 640
DES_WIDTH = 64
DES_HEIGHT = 64
COL_BG = (46, 48, 55)
COL_FG = (235, 229, 206)
X = 1000
Y = 1000
win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Through The Doors")
running = True
paused = False
# INITIALIZE SPRITES
player = Ghost()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
clock = pygame.time.Clock()
while running:
clock.tick(30)
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.go_right()
elif keys[pygame.K_LEFT]:
player.go_left()
elif keys[pygame.K_UP]:
player.go_up()
elif keys[pygame.K_DOWN]:
player.go_down()
pygame.display.flip()
pygame.quit()
I do have more sprites I am going to load in, but I would like to resolve the scaling issue first.
I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually [...]"
No there is no way. You have to scale each coordinate, each size and each surface individually. PyGame is made for images (Surfaces) and shapes in pixel units. Anyway up scaling an image will result in either a fuzzy, blurred or jagged (Minecraft) appearance.
Is there a way I could make a separate surface and just put that on top of the base window surface, and just scale that?
Yes of course.
Create a Surface to draw on (win). Use pygame.transform.scale() or pygame.transform.smoothscale() to scale it to the size of the window and blit it to the actual display Surface (display_win):
display_win = pygame.display.set_mode((WINDOW_WIDTH*2, WINDOW_HEIGHT*2))
win = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
while running:
# [...]
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
# [...]
scaled_win = pygame.transform.smoothscale(win, display_win.get_size())
# or scaled_win = pygame.transform.scale(win, display_win.get_size())
display_win.blit(scaled_win, (0, 0))
pygame.display.flip()
Minimal example: repl.it/#Rabbid76/PyGame-UpScaleDisplay
So I'm making a 2D pixel art game in pygame and as you could assume, all my sprite textures appear very small. I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually or messing up the coordinates. Every sprite will move on a grid: one unit is 16x16 pixels and when my player sprite moves, for example, it will just move over in a direction 16 pixels.
Here's my main script:
import sys
from pygame.locals import *
import pygame
from game.sprites import Ghost
pygame.init()
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 640
DES_WIDTH = 64
DES_HEIGHT = 64
COL_BG = (46, 48, 55)
COL_FG = (235, 229, 206)
X = 1000
Y = 1000
win = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("Through The Doors")
running = True
paused = False
# INITIALIZE SPRITES
player = Ghost()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
clock = pygame.time.Clock()
while running:
clock.tick(30)
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
player.go_right()
elif keys[pygame.K_LEFT]:
player.go_left()
elif keys[pygame.K_UP]:
player.go_up()
elif keys[pygame.K_DOWN]:
player.go_down()
pygame.display.flip()
pygame.quit()
I do have more sprites I am going to load in, but I would like to resolve the scaling issue first.
I'm wondering if there's a way I can globally scale everything up in my game without either having to scale each sprite up individually [...]"
No there is no way. You have to scale each coordinate, each size and each surface individually. PyGame is made for images (Surfaces) and shapes in pixel units. Anyway up scaling an image will result in either a fuzzy, blurred or jagged (Minecraft) appearance.
Is there a way I could make a separate surface and just put that on top of the base window surface, and just scale that?
Yes of course.
Create a Surface to draw on (win). Use pygame.transform.scale() or pygame.transform.smoothscale() to scale it to the size of the window and blit it to the actual display Surface (display_win):
display_win = pygame.display.set_mode((WINDOW_WIDTH*2, WINDOW_HEIGHT*2))
win = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
while running:
# [...]
if not paused:
win.fill(COL_BG)
all_sprites.update()
all_sprites.draw(win)
# [...]
scaled_win = pygame.transform.smoothscale(win, display_win.get_size())
# or scaled_win = pygame.transform.scale(win, display_win.get_size())
display_win.blit(scaled_win, (0, 0))
pygame.display.flip()
Minimal example: repl.it/#Rabbid76/PyGame-UpScaleDisplay
I need to rotate a 3D scene or a character using the mouse position: for example, if I move the mouse to the right, I want to turn to the right / the character should turn right.
I've seen lots of games place the mouse in the center of the screen, then get its position relatively to the previous frame, update the game accordingly and move the mouse back in the center.
I tried to replicate this but I ran into the issue that pygame does not rotate reliably: with higher framerates, I tend to get a slower rotation. This is not a problem when rotating a character in a 2D game, but this is concerning when a 3D scene rotates differently if there are more objects to draw.
I used this simple code to make sure the problem actually came from this:
import pygame
from pygame.locals import *
pygame.init()
WIDTH, HEIGHT = (640, 480)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
font = pygame.font.SysFont('consolas', 20)
clock = pygame.time.Clock()
angle = 0
time_passed = 0
pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
# I did not use the MOUSEMOTION event because it used to glitch a bit
x, y = pygame.mouse.get_pos()
angle += (WIDTH//2 - x) * 360 / WIDTH # 360° every WIDTH pixels
pygame.mouse.set_pos((WIDTH//2, HEIGHT//2))
screen.fill(0)
text = font.render('Angle: %d°' %angle, True, (255, 255, 255))
w, h = text.get_size()
screen.blit(text, (WIDTH//2 - w//2, HEIGHT//2 - h//2))
pygame.display.flip()
time_passed = clock.tick(60)/1000
I think either the mouse or the OS tweak the travelled distance according to the mouse speed, so I did the following tests at similar and relatively constant speed:
Going say 5 centimeters with the framerate limited to 60 FPS did not result in the same final angle as with 120 FPS for example, even when moving the mouse at the same speed, with the same mouse with high DPI.
Do I need to implement a tick system for example, where I only update the position every few frames? I think the problem wouldn't be solved with slower framerates then, and maybe the movement would not be as responsive as I want.
Or perhaps there is an alternative to pygame.mouse.get_pos() / event.pos, getting the raw, more precise position from the mouse?
I would appreciate any help.
You get the position of the mouse with pygame.mouse.get_pos() and you reset the position with pygame.mouse.set_pos(). What if there was an event between pygame.mouse.get_pos() and pygame.mouse.set_pos()? Also pygame.mouse.get_pos() only tells you the current position of the mouse curosr, but it does not tell you how the mouse got there.
I suggest to use the MOUSEMOTION event. The event queue gives you all movements of the mouse. The relative movement of the mouse pointer since the last movement can be queried with the rel attribute:
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
angle += event.rel[0] * 360 / screen.get_width()
Minimal example
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
font = pygame.font.SysFont('consolas', 20)
clock = pygame.time.Clock()
angle = 0
text = font.render(f'Angle: {angle}°', True, (255, 255, 255))
pygame.mouse.set_pos(screen.get_rect().center)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
run = False
if event.type == pygame.MOUSEMOTION:
angle += event.rel[0] * 360 / screen.get_width()
text = font.render(f'Angle: {angle}°', True, (255, 255, 255))
pygame.mouse.set_pos(screen.get_rect().center)
screen.fill(0)
screen.blit(text, text.get_rect(center = screen.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()
I was making a pygame project and i made a square move and It would summon a square every frame and there would just be a line of squares.
I tried to update the screen every frame (Because i hadn't yet), but that did not work. Here is my code:
#Import Pygame
import pygame
#screen object(width, height)
screen_x = 750
screen_y = 600
screen = pygame.display.set_mode((screen_x, screen_y))
#Set the caption of the screen
pygame.display.set_caption('Game')
#Define a velocity, x, and y variable
velocity = 2.5x = 0.0y = 0.0
#Variable to keep our game loop running
running = True
#Game loop
while running:
# Initialing Color
color = (255,0,0)
if pygame.key.get_pressed()[pygame.K_w]:
y += 2.5
if pygame.key.get_pressed()[pygame.K_s]:
y -= 2.5
if pygame.key.get_pressed()[pygame.K_a]:
x -= 2.5
if pygame.key.get_pressed()[pygame.K_d]:
x += 2.5
# Drawing Rectangle
pygame.draw.rect(screen, color, pygame.Rect(x, y, 50, 50))
pygame.display.flip()
pygame.display.update()
# for loop through the event queue
for event in pygame.event.get():
# Check for QUIT event
if event.type == pygame.QUIT:
running = False
The entire scene is redrawn in every frame, therefore you have to clear the display in every frame:
import pygame
#screen object(width, height)
screen_x = 750
screen_y = 600
screen = pygame.display.set_mode((screen_x, screen_y))
#Set the caption of the screen
pygame.display.set_caption('Game')
#Define a velocity, x, and y variable
velocity = 2.5
x, y = 0, 0
color = (255,0,0)
clock = pygame.time.Clock()
running = True
while running:
clock.tick(100)
# for loop through the event queue
for event in pygame.event.get():
# Check for QUIT event
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
x += (keys[pygame.K_d] - keys[pygame.K_a]) * velocity
y += (keys[pygame.K_s] - keys[pygame.K_w]) * velocity
# clear display
screen.fill((0, 0, 0))
# Drawing Rectangle
pygame.draw.rect(screen, color, pygame.Rect(x, y, 50, 50))
# update display
pygame.display.flip()
pygame.quit()
exit()
The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
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 (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
The solution is very simple. You forgot to fill the screen with a color. Because your code just draw's a square to the screen surface every frame, and that's why it's drawing a line of squares. You have to fill the screen before drawing the things, because otherwise you will see an empty screen with that color, that you filled the surface with. Hope it helped.
I am struggling with moving a drawn rectangle on the screen in pygame, I am trying to create a Snake game. I am very new to Python and object oriented programming in general so it is probably a stupid mistake. Code below.
#X coordinate of snake
lead_x = 300
#sets window position on screen
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (320,240)
#imports pygame module
import sys, pygame
#initialises pygame module
pygame.init()
#changes background colour to green
background_colour = 155, 188, 15
blue =(0,0,255)
red = (100,40,20)
#sets screen size
screen = pygame.display.set_mode((640, 480))
#changes the background colour
screen.fill(background_colour)
#creates rectangle on the screen
pygame.draw.rect(screen, blue, [lead_x,lead_x,10,10])
#updates display to show new background colour
pygame.display.update()
#Sets the window title to 'Python'
pygame.display.set_caption('Python')
#closes the window when user presses X
running = True
#if cross is clicked set running = False
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#Controls
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
lead_x -= 10
if event.key == pygame.K_RIGHT:
lead_x += 10
#if running = False close pygame window
if running == False:
pygame.quit()
You need to put pygame.draw.rect(screen, blue, [lead_x, lead_y, 10, 10]) in your game loop. As of right now you're drawing the rect on the screen only once, and that's in the beginning of the program. You want to continuously draw the rect at different lead_x and lead_y positions in order for a rect to move on the screen.
You should also put a screen.fill(background_colour) (to clear the previous drawing) and pygame.display.update() (to update the changes) in your loop as well.
EDIT: I noticed something: you probably want to create a variable lead_y and use pygame.draw.rect(screen, blue, [lead_x, lead_y, 10, 10]) so you don't move diagonally every time.