I'm using pygame to show two images: I've maneged to resize the images according to the screen size of my user. But I'd like to standardized also the x and the y coordination position of my images. The images should always be in the middle according to the y axis and have the a little bit of pad to stay close to the border of the monitor like this no matter what resolution my user
has:
This are the values that I'd like to standardized
WIN.blit(FIRST_IMG, (100, 280))
WIN.blit(SECOND_IMAGE, (1350, 280))
This is my code right now:
import pygame
import os
WIN = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
WHITE = (255, 255, 255)
FPS = 60
SCREEN_INFO = pygame.display.Info()
IMAGE_WIDTH, IMG_HEIGHT = SCREEN_INFO.current_w // 5, SCREEN_INFO.current_h // 3
FIRST_IMG = pygame.image.load(os.path.join("Assets", "7.png"))
FIRST_IMG = pygame.transform.scale(FIRST_IMG, (IMAGE_WIDTH, IMG_HEIGHT))
SECOND_IMAGE = pygame.image.load(os.path.join("Assets", "8.png"))
SECOND_IMAGE = pygame.transform.scale(SECOND_IMAGE, (IMAGE_WIDTH, IMG_HEIGHT))
def draw_window():
WIN.fill(WHITE)
WIN.blit(FIRST_IMG, (100, 280))
WIN.blit(SECOND_IMAGE, (1350, 280))
pygame.display.update()
def main():
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
run = False
draw_window()
pygame.quit()
if __name__ == "__main__":
main()
Get the bounding rectangle of the image and set the centery and left or right of the rectangle depending on the size of WIN. Use the rectangles to blit the images:
def draw_window():
width, height = WIN.get_size()
WIN.fill(WHITE)
first_rect = FIRST_IMG.get_rect(midleft = (100, height // 2))
WIN.blit(FIRST_IMG, first_rect)
second_rect = FIRST_IMG.get_rect(midright = (width - 100, height // 2))
WIN.blit(SECOND_IMAGE, second_rect)
pygame.display.update()
Related
I have the problem with pygame. Specifically, I stuck on how to resize the text proportionally to the window (window is re-sizable and with picture).
Here is my code.
import pygame
from pygame.locals import *
import numpy as np
import matplotlib.pyplot as plt
import argparse
import threading, os, sys, time
pygame.init()
pygame.display.set_caption("AI Battlehip Game")
FPS = pygame.time.Clock()
red = (255,0,0)
screen = pygame.display.set_mode((1200,700), HWSURFACE|DOUBLEBUF|RESIZABLE)
add_screen = screen.copy()
back_end_image_set = pygame.image.load(r'/Users/User1/Desktop/Project work/images/backgroundimage1.jpg')
screen.blit(pygame.transform.scale(back_end_image_set, (1200,700)), (0,0))
pygame.display.flip()
myFont = pygame.font.SysFont("monospace", 300)
label = myFont.render("Check 1", 40, (red))
add_screen.blit(pygame.transform.scale(label, (700, 500)), (0,0))
FPS.tick(60)
try:
while True:
pygame.event.pump()
event = pygame.event.wait()
if event.type == QUIT:
pygame.display.quit()
elif event.type == VIDEORESIZE:
screen = pygame.display.set_mode(event.dict['size'], HWSURFACE|DOUBLEBUF|RESIZABLE)
screen.blit(pygame.transform.scale(back_end_image_set, event.dict['size']), (0,0))
pygame.display.flip()
except:
raise
Any help will be fully appreciated.
First store the original size of the surface:
original_size = (1200,700)
screen = pygame.display.set_mode(original_size, HWSURFACE|DOUBLEBUF|RESIZABLE)
Then you've 2 options.
Option 1:
Use pygame.font and render the text to a surface:
myFont = pygame.font.SysFont("monospace", 300)
label = myFont.render("Check 1", 40, (red))
Scale the text surface by the ratio of the new window size and original window size and blit it to the surface:
pygame.event.pump()
event = pygame.event.wait()
if event.type == QUIT:
pygame.display.quit()
elif event.type == VIDEORESIZE:
screen = pygame.display.set_mode(event.dict['size'], HWSURFACE|DOUBLEBUF|RESIZABLE)
new_size = event.dict['size']
screen.blit(pygame.transform.scale(back_end_image_set, new_size), (0,0))
label_w = label.get_width() * new_size[0] // original_size[0]
label_h = label.get_height() * new_size[1] // original_size[1]
screen.blit(pygame.transform.scale(label, (label_w, label_h)), (0,0))
pygame.display.flip()
Option 2:
Use pygame.freetype:
import pygame.freetype
myFont = pygame.freetype.SysFont('monospace', 30)
Calculate the scaled size of the text area and render it directly to the resized screen. Note the text is scaled by the ratio of the new window width and original window width.
This implementation keeps the ration of the width and height of the text and doesn't stretch or squeeze the text:
pygame.event.pump()
event = pygame.event.wait()
if event.type == QUIT:
pygame.display.quit()
elif event.type == VIDEORESIZE:
screen = pygame.display.set_mode(event.dict['size'], HWSURFACE|DOUBLEBUF|RESIZABLE)
new_size = event.dict['size']
screen.blit(pygame.transform.scale(back_end_image_set, new_size), (0,0))
myFont.render_to(screen, (0, 0), "Check 1", fgcolor=red, size = 300 * new_size[0] // original_size[0])
pygame.display.flip()
I have a program where it fills in the bits of a mask that are overlapped from another mask, but when I blit the mask of the overlapping bits onto the screen, the transparent bits are fully black for some reason? The program works as intended and I've tried converting the surface for the overlapping bits to per pixel alpha but the transparent bits are black
example gif
import pygame
import sprites
SCREEN_HEIGHT, SCREEN_WIDTH = 800, 800
running = True
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
player = sprites.Block((100, 100))
block2 = sprites.Block((100, 100))
blocks = pygame.sprite.Group(block2)
block2.rect.topleft = 150, 150
block2.image.fill((0, 255, 0))
while running:
events = pygame.event.get()
screen.fill((100, 100, 100))
for event in events:
if event.type == pygame.QUIT:
running = False
player.move(screen.get_rect())
screen.blit(player.image, player.rect)
blocks.draw(screen)
for block in blocks:
offset = (player.rect.x - block.rect.x, player.rect.y - block.rect.y)
colliding_bits = player.mask.overlap_mask(block.mask, offset)
colliding_bits_image = colliding_bits.to_surface(setcolor=(0, 255, 0))
screen.blit(colliding_bits_image, block.rect)
clock.tick(144)
pygame.display.flip()
code containing the sprite classes:
import pygame
class Block(pygame.sprite.Sprite):
def __init__(self, size):
self.image = pygame.image.load("flappy_bird.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
self.speed = 1
super().__init__()
def move(self, screen_rect):
pressed_keys = pygame.key.get_pressed()
if pressed_keys[pygame.K_w]:
self.rect.move_ip(0, -self.speed)
if pressed_keys[pygame.K_s]:
self.rect.move_ip(0, self.speed)
if pressed_keys[pygame.K_a]:
self.rect.move_ip(-self.speed, 0)
if pressed_keys[pygame.K_d]:
self.rect.move_ip(self.speed, 0)
self.rect.clamp_ip(screen_rect)
I added the unsetcolor attribute to the to_surface method and removed the line blocks.draw(screen) and it seems to produce the desired result :
from math import fabs
import pygame
import sprites
SCREEN_HEIGHT, SCREEN_WIDTH = 800, 800
running = True
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
player = sprites.Block((100, 100))
block2 = sprites.Block((100, 100))
blocks = pygame.sprite.Group(block2)
block2.rect.topleft = 150, 150
block2.image.fill((0, 255, 0))
while running:
events = pygame.event.get()
screen.fill((100, 100, 100))
for event in events:
if event.type == pygame.QUIT:
running = False
player.move(screen.get_rect())
screen.blit(player.image, player.rect)
for block in blocks:
offset = (player.rect.x - block.rect.x, player.rect.y - block.rect.y)
colliding_bits = player.mask.overlap_mask(block.mask, offset)
colliding_bits_image = colliding_bits.to_surface(setcolor=(0, 255, 0, 255), unsetcolor=(0, 0, 0, 0))
screen.blit(colliding_bits_image, block.rect)
clock.tick(144)
pygame.display.flip()
I am trying to create a pixel-art making program using pygame (yes, I know there are better packages to use). The way that I am drawing pixels is via surface.set_at(). I need to know how to completely delete a pixel from a pygame surface, so that I can have an eraser tool. I do not want to fill the pixel in with the background color of the window, because that would still render when using pygame.image.save() to get an image file from the drawing. Can someone please help? Here is my code:
import pygame
import sys
import math
pygame.init()
clock = pygame.time.Clock()
pygame.display.set_caption("Pixel Studio v1.1")
screen = pygame.display.set_mode((960, 720), pygame.RESIZABLE)
scroll_speed = 5
canvas_size = (32, 16)
color = (255, 255, 255)
canvas_surf = pygame.Surface(canvas_size, pygame.SRCALPHA)
zoom = 12
mouse_down = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.image.save(canvas_surf, "canvas.png")
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == pygame.BUTTON_LEFT:
mouse_down = True
if event.button == pygame.BUTTON_WHEELUP:
if zoom > 2:
zoom -= scroll_speed
if event.button == pygame.BUTTON_WHEELDOWN:
zoom += scroll_speed
if event.type == pygame.MOUSEBUTTONUP:
if event.button == pygame.BUTTON_LEFT:
mouse_down = False
if zoom < 2:
zoom = 2
screen.fill((50, 50, 50))
#canvas_surf.fill((200, 200, 200))
canvas_surf_scaled = pygame.transform.scale(canvas_surf,(canvas_surf.get_width() * zoom, canvas_surf.get_height() * zoom))
canvas_rect = canvas_surf_scaled.get_rect(center=(screen.get_width()/2, screen.get_height()/2))
mouse_pos = pygame.mouse.get_pos()
if mouse_down:
canvas_surf.set_at((math.floor((mouse_pos[0] - canvas_rect.x) / zoom), math.floor((mouse_pos[1] - canvas_rect.y) / zoom)), color)
pygame.draw.rect(screen, ( 75, 75, 75), canvas_rect, 1,)
screen.blit(canvas_surf_scaled, canvas_rect)
pygame.display.flip()
clock.tick(60)
As discussed in the comments, you can make a pixel transparent by setting it to a value whose alpha value is zero.
#r #g #b #a
canvas_surf.set_at((x, y), (0, 0, 0, 0))
There is the image I'm importing :
look_1 = pygame.image.load('data\\png\\look1.png').convert_alpha()
And what I tried to get its size reduce was this :
pygame.transform.scale()
But this seems not to be the right way to do it.
You can either use pygame.transform.scale or smoothscale and pass the new width and height of the surface or pygame.transform.rotozoom and pass a float that will be multiplied by the current resolution.
import sys
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
IMAGE = pg.Surface((100, 60))
IMAGE.fill(pg.Color('sienna2'))
pg.draw.circle(IMAGE, pg.Color('royalblue2'), (50, 30), 20)
# New width and height will be (50, 30).
IMAGE_SMALL = pg.transform.scale(IMAGE, (50, 30))
# Rotate by 0 degrees, multiply size by 2.
IMAGE_BIG = pg.transform.rotozoom(IMAGE, 0, 2)
def main():
clock = pg.time.Clock()
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
screen.fill(pg.Color('gray15'))
screen.blit(IMAGE, (50, 50))
screen.blit(IMAGE_SMALL, (50, 155))
screen.blit(IMAGE_BIG, (50, 230))
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
sys.exit()
You have to decide if you want to use Use pygame.transform.smoothscale or pygame.transform.scale. While pygame.transform.scale performs a fast scaling with the nearest pixel, pygame.transform.smoothscale scales a surface smoothly to any size with interpolation of the pixels.
Scaling up a Surface with pygame.transform.scale() will result in a jagged result. When downscaling you lose information (pixels). In comparison, pygame.transform.smoothscale blurs the Surface.
pygame.transform.scale() and pygame.transform.smoothscale are used in the same way. They do not scale the input Surface itself. It creates a new surface and does a scaled "blit" to the new surface. The new surface is returned by the return value. They:
Creates a new surface (newSurface) with size (width, height).
Scale and copy Surface to newSurface.
Return newSurface.
look_1 = pygame.image.load('data\\png\\look1.png').convert_alpha()
look_1 = pygame.transform.scale(look_1, (new_width, new_height))
or
look_1 = pygame.image.load('data\\png\\look1.png').convert_alpha()
look_1 = pygame.transform.smoothscale(look_1, (new_width, new_height))
See also Transform scale and zoom surface
Minimal example: replit.com/#Rabbid76/PyGame-ScaleCenter
import pygame
class ScaleSprite(pygame.sprite.Sprite):
def __init__(self, center, image):
super().__init__()
self.original_image = image
self.image = image
self.rect = self.image.get_rect(center = center)
self.mode = 1
self.grow = 0
def update(self):
if self.grow > 100:
self.mode = -1
if self.grow < 1:
self.mode = 1
self.grow += 1 * self.mode
orig_x, orig_y = self.original_image.get_size()
size_x = orig_x + round(self.grow)
size_y = orig_y + round(self.grow)
self.image = pygame.transform.scale(self.original_image, (size_x, size_y))
self.rect = self.image.get_rect(center = self.rect.center)
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
sprite = ScaleSprite(window.get_rect().center, pygame.image.load("Banana64.png"))
group = pygame.sprite.Group(sprite)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
group.update()
window.fill(0)
group.draw(window)
pygame.display.flip()
pygame.quit()
exit()
Import the Image first.
Then we can use pygame.transform.scale.
This is a fast-scale operation.
self.image = pygame.image.load('ship.bmp')
self.image = pygame.transform.scale(self.image,(35,35))
You can do:
import pygame,sys
from pygame.locals import *
size = int(input("What is the size?"))
look_1 = pygame.image.load('data\\png\\look1.png')
win = pygame.display.set_mode((500,500),0,32)
while True:
for event in pygame.event.get():
if event.type==QUIT:
pygame.quit()
sys.exit()
win.blit(pygame.transform.scale(look_1, (size, size)), (x, y))
win.update()
I hope it helps!
I'm trying to understand how get_rect() works. In this simple example, I have two images and want to obtain the location of the second and move the first image to the second image.
I have looked at a variety of examples online and cannot get this to work. What am I doing wrong?
import pygame, sys
from pygame.locals import *
import time
pygame.init()
FPS = 10 # frames per second setting
fpsClock = pygame.time.Clock()
# Set up the window
DISPLAYSURF = pygame.display.set_mode((600, 400), 0, 32)
pygame.display.set_caption('Test program for get_rect()')
WHITE = (255, 255, 255)
# Load two images
baseImg = pygame.image.load('image1.jpg')
spaceshipImg = pygame.image.load('image2.jpg')
DISPLAYSURF.fill(WHITE)
# Place one image at the bottom of the screen
DISPLAYSURF.blit(baseImg, (300, 300))
pygame.display.update()
# Place the second image at the top of the screen
DISPLAYSURF.blit(spaceshipImg, (300, 0))
pygame.display.update()
# Wait for one second
time.sleep(1)
# Obtain the rectangle for each image
baseRect = baseImg.get_rect()
spaceshipRect = spaceshipImg.get_rect()
# This is where I believe I'm going wrong
# I understand this to obtain the x,y of the spaceship image
# Set the xy coordinates for the top image to the xy of the bottom image
spaceshipRect.x = baseRect.x
spaceshipRect.y = baseRect.y
# Move the top image to new xy position
# However this doesn't work
DISPLAYSURF.blit(spaceshipImg, (spaceshipRect.x, spaceshipRect.y))
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
First, images/pygame.Surfaces don't have a position, so you have to store the blit position in the rect. When you call the get_rect method of a pygame.Surface, Pygame creates a new rect with the size of the image and the x, y coordinates (0, 0). To give the rect other coords during the instantiation you can pass an argument to get_rect, mostly center or topleft is used. To move the rect later, you can change any of these attributes of the rect:
x,y
top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
size, width, height
w,h
Here's an example (press a or d to change the position of the rect and thereby the blit pos of the image):
import sys
import pygame as pg
BG_COLOR = pg.Color(80, 60, 70)
PLAYER_COLOR = pg.Color(90, 140, 190)
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
player_img = pg.Surface((40, 60))
player_img.fill(PLAYER_COLOR)
# Create a rect with the size of the image/pygame.Surface
# and immediately set it's topleft coords to (100, 300).
player_rect = player_img.get_rect(topleft=(100, 300))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_d:
# Set the center to these new coords.
player_rect.center = (400, 200)
if event.key == pg.K_a:
# Set the x coord to 300.
player_rect.x = 300
screen.fill(BG_COLOR)
screen.blit(player_img, player_rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()