Image in pygame gets pixelated after scaling it several times [duplicate] - python

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!

Related

center images and add padding according to screen resolution

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()

transparent bits in pygame mask are black instead of being transparent

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()

Pygame - creating a rotating projectile arount the center [duplicate]

This code is mostly just the generic start up of a pygame window but I'm trying to make it so the object moves (the planet object I've made) in the orbit around the sun object I've made for the coordinates I've given it. I know the x and y values are updating but I don't understand why the object doesn't move.
#import the library
import pygame
import math
#classes
class button:
def _init_ (self,screen, colour, x, y, width,height, letter):
self.screen = screen
self.colour = colour
self.x = x
self.y = y
self.width = width
self.height = height
self.letter = letter
self.radius = radius
def draw(self):
pygame.draw.rect(self.screen, self.colour,(self.x,self.y, self.width, self.height))
if self.letter!= '+' and self.letter!= '-':
font = pygame.font.SysFont('agencyfb',15,True,False)
else:
font = pygame.font.SysFont('agencyfb',25,True,False)
text = font.render(self.letter, True, black)
text_rect = text.get_rect(center=(self.x+self.width/2,self.y+self.height/2))
screen.blit(text, text_rect)
class orbit:
def __init__(self,screen,colour,x,y,radius,width):
self.screen = screen
self.colour = colour
self.x = x
self.y = y
self.width = width
self.radius = radius
def draw_circle(self):
pygame.draw.circle(self.screen,self.colour,(self.x,self.y),self.radius,self.width)
#define colours
##Sun = pygame.draw.circle(screen,Sun,[1000,450],100,0)
Black = (0,0,0)
White = (255,255,255)
Green = (0,255,0)
Red = (255,0,0)
Blue = (0,0,255)
Sun = (255,69,0)
Sun = []
Planet = []
#initialise the engine
pygame.init()
#Opening a window
size = (1920,1080)
screen = pygame.display.set_mode(size)
#set window title
pygame.display.set_caption("Orbit Simulator")
#loop unti the user clicks the close button
done = False
#
x=1000
y=450
Sun.append(orbit(screen,Red,1000,450,100,0))
Planet.append(orbit(screen,White,x,y,50,0))
#
#used to manage how fast the screen updates
clock = pygame.time.Clock()
#------ Main program Loop ------
while not done:
#--- Main event loop
for event in pygame.event.get(): #user did something
if event.type == pygame.QUIT: #if user clicked close
done = True #flag that we are done and exit the loop
#------ Game logic should go here ------
#------ Drawing code should go here -------
#first, clear the screen to white. Don't put other drawing commands above this or they will be erased with this command.
screen.fill(Black)
for i in Sun:
i.draw_circle()
for i in Planet:
r=150
angle=0
count = 0
while angle <= 360:
angle_radians = math.radians(angle)
x = int(math.cos(angle_radians)*r)
y = int(math.sin(angle_radians)*r)
angle +=1
count +=1
print(count)
x+=1000
y+=450
pygame.draw.circle(screen,White,[x,y],10,0)
print("Position [",x,",",y,"]")
#update the screen
pygame.display.flip()
#------ Limit to 60 frames per second ------
clock.tick(60)
#------ When the loop ends, quit ------
pygame.quit()
You can make an object rotate around another by using trigonometry or vectors. With vectors you just have to rotate a vector which defines the offset from the rotation center each frame and add it to the position vector (self.pos which is the rotation center) to get the desired self.rect.center coordinates of the orbiting object.
import pygame as pg
from pygame.math import Vector2
class Planet(pg.sprite.Sprite):
def __init__(self, pos, *groups):
super().__init__(*groups)
self.image = pg.Surface((40, 40), pg.SRCALPHA)
pg.draw.circle(self.image, pg.Color('dodgerblue'), (20, 20), 20)
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos)
self.offset = Vector2(200, 0)
self.angle = 0
def update(self):
self.angle -= 2
# Add the rotated offset vector to the pos vector to get the rect.center.
self.rect.center = self.pos + self.offset.rotate(self.angle)
def main():
pg.init()
screen = pg.display.set_mode((640, 480))
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
planet = Planet(screen_rect.center, all_sprites)
yellow = pg.Color('yellow')
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
return
all_sprites.update()
screen.fill((30, 30, 30))
pg.draw.circle(screen, yellow, screen_rect.center, 60)
all_sprites.draw(screen)
pg.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
pg.quit()

Pygame: How to correctly use get_rect()

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()

How can I use tiling in a game with python and pygame?

I'm creating a 2D platformer type shooting game using python and pygame, and I'm trying to add in tiling mechanics so If I created a platform 100 pixels long and the tile image was only 70 pixels long, it would draw one tile and half of another, so I created a simple prototype, but I can't get it to draw the sprite. Here's my code for it:
import pygame
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
class Rec(pygame.sprite.Sprite):
def __init__(self, x, y, w, height):
super().__init__()
self.b = 0
self.image = pygame.image.load("grass.png").convert()
if w <= 70:
self.image = pygame.transform.scale(self.image, (w, height))
elif w > 70:
self.image = pygame.Surface([w, height])
while self.b < w:
self.image.blit(pygame.image.load("grass.png").convert(), (x + self.b, y))
self.b += 70
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("My Game")
all_sprites_list = pygame.sprite.Group()
rec = Rec(100, 200, 140, 70)
all_sprites_list.add(rec)
done = False
clock = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
screen.fill(WHITE)
all_sprites_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
The line
self.image.blit(pygame.image.load("grass.png").convert(), (x + self.b, y))
should be
self.image.blit(pygame.image.load("grass.png").convert(), (self.b, 0))
since the position you pass to the blit function are relative to the Surface you blit on; but x and y are screen coordinates.
So in your example you blit the grass image with a position of x + self.b = 100, y = 200 on a Surface which has a size of (140, 70), while you should blit the it at (0, 0).

Categories

Resources