This question already has an answer here:
Pygame set_alpha not working with attempted background fading
(1 answer)
Closed 1 year ago.
So I have been trying to fade an image in and out. I found this tutorial and I copied the code exactly and changed the images to my images, but it comes up with errors;
File "C:\Users\Me\Documents\Fan game\Start-Up.py", line 114, in <module>
main(screen)
File "C:\Users\Me\Documents\Fan game\Start-Up.py", line 91, in main
fade = CrossFade(screen)
File "C:\Users\Me\AppData\Local\Programs\Python\Python39\lib\site-packages\pygame\sprite.py", line 115, in __init__
self.add(*groups)
File "C:\Users\Me\AppData\Local\Programs\Python\Python39\lib\site-packages\pygame\sprite.py", line 133, in add
self.add(*group)
TypeError: pygame.sprite.Sprite.add() argument after * must be an iterable, not pygame.Surface
I have no idea what these errors mean.
Here is my code;
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
class CrossFade(pygame.sprite.Sprite):
"""Synthesizes fade by incrementing the transparency
of a black surface blitted on top of screen"""
def _init_(self, screen):
pygame.sprite.Sprite._init_(self)
#Make a surface to be used as our fader
#The size is dynamically based on size of the screen
self.image = pygame.surface(screen.get_size())
self.image = self.image.convert()
self.image.fill((0, 0, 0))
#Get the Rect dimensions
self.rect = self.image.get_rect()
#fade_dir determines whether to fade in or fade out
self.fade_dir = 1
#trans_value is the degree of transparency
#225 is opaque/0 is fully transparent
self.trans_value = 255
#fade_speed is the difference in transparency after each delay
self.fade_speed = 6
#Delay helps to dynamically adjust the number of frames between fades
self.delay = 1
#Increment increases each frame (each call to update)
#until it is equal to our delay (see update() below)
self.increment = 0
#Initialize our transparency (at opaque)
self.image.set_alpha(self.trans_value)
#Set position of black surface
self.rect.centerx = 320
self.rect.centery = 240
def update(self):
self.image.set_alpha(self.trans_value)
#Increase increment
self.increment += 1
if self.increment >= self.delay:
self.increment = 0
#Fade in
if self.fade_dir > 0:
#Make sure the transparent value doesn't go negative
if self.trans_value - self.fade_speed < 0:
self.trans_value = 0
#Increase transparency of the black surface by decreasing its alpha
else:
self.trans_value -= self.fade_speed
#Fade out
elif self.fade_dir < 0:
#Make sure transparency value doesn't go above 225
if self.trans_value + self.delay > 225:
self.trans_value = 225
#Increase opacity of black surface
else:
self.trans_value += self.fade_speed
def main(screen):
pygame.display.set_caption("Start-Up")
clock = pygame.time.Clock()
keepPlaying = True
#Image you'd like to fade over
logo = pygame.image.load("office.png")
screen.blit(logo, (0, 0))
#CrossFade must be in a sprite group so we can use the .clear() method
#of sprite updating
fade = CrossFade(screen)
all_Sprites = pygame.sprite.Group(fade)
while keepPlaying:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
keepPlaying = False
#Reset the fade after a time-controlled delay
if fade.trans_value == 0:
pygame.time.delay(1500)
fade.fade_dir *= -1
#C.U.D. Sprite Group Dirty Blitting
all_Sprite.clear(screen, logo)
all_Sprite.update()
all_Sprites.draw(screen)
#Refresh the screen
pygame.display.flip()
#MAIN
main(screen)
pygame.quit()
I have no idea what is wrong and if you have a better/alternative way to do this please tell me
I figured out a better way to do this :D
import pygame
import time
pygame.init()
display_size = pygame.display.Info().current_w , pygame.display.Info().current_h - 50
screen = pygame.display.set_mode(display_size)
image = pygame.image.load(r'textures\logo.png')
image_size = image.get_rect().size
centered_image = [(display_size[0] - image_size[0])/2, (display_size[1] - image_size[1])/2]
time.sleep(1)
for i in range (255):
screen.fill((0,0,0))
image.set_alpha(i)
screen.blit(image, centered_image)
pygame.display.update()
time.sleep(0.001)
time.sleep(1.5)
for i in range (255, 0, -1):
screen.fill((0,0,0))
image.set_alpha(i)
screen.blit(image, centered_image)
pygame.display.update()
time.sleep(0.001)
Related
I am trying to create a game using pygame and I am attempting to add a background to it (I have used some code from a YouTube video but this is not working). I also to not understand what the code is on about. I mean the background and does move but it automatically adds a new version of the background in the middle of the screen when the older background has not gone off screen yet:
class Background:
def __init__(self, x, y, picture):
self.xpos = x
self.ypos = y
self.picture = picture
self.rect = self.picture.get_rect()
self.picture = pygame.transform.scale(self.picture, (1280, 720))
def paste(self, xpos, ypos):
screen.blit(self.picture, (xpos, ypos))
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
while True:
background=pygame.image.load("C:/images/mars.jpg").convert_alpha()
cliff = Background(0, 0, background)
rel_x = x % cliff.rect.width
cliff.paste(rel_x - cliff.rect.width, 0)
if rel_x < WIDTH:
cliff.paste(rel_x, 0)
x -= 1
This is what currently happens with my background
[![what my problem looks like][1]][1]
[![What I want the background to move like ][2]][2]
This is what I want my background to look like (please ignore the sign it was the only one I could find)
I have now discovered what the real problem is
If you want to have a continuously repeating background, then you've to draw the background twice:
You've to know the size of the screen. The size of the height background image should match the height of the screen. The width of the background can be different, but should be at least the with of the window (else the background has to be drawn more than 2 times).
bg_w, gb_h = size
bg = pygame.transform.smoothscale(pygame.image.load('background.image'), (bg_w, bg_h))
The background can be imagined as a endless row of tiles.
If you want to draw the background at an certain position pos_x, then you have to calculate the position of the tile relative to the screen by the modulo (%) operator. The position of the 2nd tile is shifted by the width of the background (bg_w):
x_rel = pos_x % bg_w
x_part2 = x_rel - bg_w if x_rel > 0 else x_rel + bg_w
Finally the background has to be blit twice, to fill the entire screen:
screen.blit(bg, (x_rel, 0))
screen.blit(bg, (x_part2, 0))
You can test the process by the following example program. The background can be moved by <- respectively ->
import pygame
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
bg_w, bg_h = size
bg = pygame.transform.smoothscale(pygame.image.load('background.image'), (bg_w, bg_h))
pos_x = 0
speed = 10
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
allKeys = pygame.key.get_pressed()
pos_x += speed if allKeys[pygame.K_LEFT] else -speed if allKeys[pygame.K_RIGHT] else 0
x_rel = pos_x % bg_w
x_part2 = x_rel - bg_w if x_rel > 0 else x_rel + bg_w
screen.blit(bg, (x_rel, 0))
screen.blit(bg, (x_part2, 0))
pygame.display.flip()
Also see How to make parallax scrolling work properly with a camera that stops at edges pygame
This SO answer should have what you need
This seems to provide maybe a smarter and more functional background class than what you're using. I'd say give a try.
I am trying to create a game using pygame and I am attempting to add a background to it (I have used some code from a YouTube video but this is not working). I also to not understand what the code is on about. I mean the background and does move but it automatically adds a new version of the background in the middle of the screen when the older background has not gone off screen yet:
class Background:
def __init__(self, x, y, picture):
self.xpos = x
self.ypos = y
self.picture = picture
self.rect = self.picture.get_rect()
self.picture = pygame.transform.scale(self.picture, (1280, 720))
def paste(self, xpos, ypos):
screen.blit(self.picture, (xpos, ypos))
def draw(self):
screen.blit(self.picture, (self.xpos, self.ypos))
while True:
background=pygame.image.load("C:/images/mars.jpg").convert_alpha()
cliff = Background(0, 0, background)
rel_x = x % cliff.rect.width
cliff.paste(rel_x - cliff.rect.width, 0)
if rel_x < WIDTH:
cliff.paste(rel_x, 0)
x -= 1
This is what currently happens with my background
[![what my problem looks like][1]][1]
[![What I want the background to move like ][2]][2]
This is what I want my background to look like (please ignore the sign it was the only one I could find)
I have now discovered what the real problem is
If you want to have a continuously repeating background, then you've to draw the background twice:
You've to know the size of the screen. The size of the height background image should match the height of the screen. The width of the background can be different, but should be at least the with of the window (else the background has to be drawn more than 2 times).
bg_w, gb_h = size
bg = pygame.transform.smoothscale(pygame.image.load('background.image'), (bg_w, bg_h))
The background can be imagined as a endless row of tiles.
If you want to draw the background at an certain position pos_x, then you have to calculate the position of the tile relative to the screen by the modulo (%) operator. The position of the 2nd tile is shifted by the width of the background (bg_w):
x_rel = pos_x % bg_w
x_part2 = x_rel - bg_w if x_rel > 0 else x_rel + bg_w
Finally the background has to be blit twice, to fill the entire screen:
screen.blit(bg, (x_rel, 0))
screen.blit(bg, (x_part2, 0))
You can test the process by the following example program. The background can be moved by <- respectively ->
import pygame
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
bg_w, bg_h = size
bg = pygame.transform.smoothscale(pygame.image.load('background.image'), (bg_w, bg_h))
pos_x = 0
speed = 10
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
allKeys = pygame.key.get_pressed()
pos_x += speed if allKeys[pygame.K_LEFT] else -speed if allKeys[pygame.K_RIGHT] else 0
x_rel = pos_x % bg_w
x_part2 = x_rel - bg_w if x_rel > 0 else x_rel + bg_w
screen.blit(bg, (x_rel, 0))
screen.blit(bg, (x_part2, 0))
pygame.display.flip()
Also see How to make parallax scrolling work properly with a camera that stops at edges pygame
This SO answer should have what you need
This seems to provide maybe a smarter and more functional background class than what you're using. I'd say give a try.
I have a program with a player (who is an image) and a rectangle and I want that when the player has a collision with the rectangle, the size of the image increase.
For now, I have this code :
import pygame
from random import randint
WIDTH, HEIGHT = 800, 800
FPS = 60
pygame.init()
win = pygame.display.set_mode((WIDTH, HEIGHT))
fenetre_rect = pygame.Rect(0, 0, WIDTH, HEIGHT)
pygame.display.set_caption("Hagar.io")
clock = pygame.time.Clock()
bg = pygame.image.load("bg.png").convert()
bg_surface = bg.get_rect(center=(WIDTH / 2, HEIGHT / 2))
bg_x = bg_surface.x
bg_y = bg_surface.y
x_max = WIDTH / 2
y_max = HEIGHT / 2
# player
player = pygame.transform.scale(pygame.image.load("player.png").convert_alpha(), (i, i))
player_rect = player.get_rect(center=(x_max, y_max))
# cell
rect_surface = pygame.Rect(300, 500, 20, 20)
# Game loop
running = True
while running:
dt = clock.tick(FPS) / 1000
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if player_rect.colliderect(rect_surface):
print("collide")
bg_surface.x = bg_x
bg_surface.y = bg_y
# draw on screen
win.blit(bg, bg_surface)
pygame.draw.rect(win, (255, 0, 0), rect_surface)
win.blit(player, player_rect)
pygame.display.flip()
pygame.quit()
I have try to add in the "colliderect" condition but it does not work :
player_rect.width += 1
player_rect.height += 1
Thanks for your help !
This line
player = pygame.transform.scale(pygame.image.load("player.png").convert_alpha(), (i, i))
is using the variable i but it is not defined in your code. I'm not sure where it is defined, but it is key to what you want. I will try to answer without this information anyway:
Thing is, enlarging the rect won't do anything, because a rect is just coordinates. You have to scale the actual image, and pygame.transform.scale does exactly that.
You can keep the image in a separate variable player_img:
player_img = pygame.image.load("player.png").convert_alpha()
player = pygame.transform.scale(player_img, (i, i))
Then when you want to scale it differently, just call .scale() again:
double_size_player = pygame.transform.scale(player_img, (i*2, i*2))
That still leaves us to the mistery of your undefined i variable, but I think you get the gist of it. Remeber that you have to extract a new rect from the scaled image because it will be bigger.
I'm trying to make a heat bar which grows every time I press 'x' and I can't figure out how. What can I do?
heatBar = [45, 30]
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
for pos in heatBar:
pygame.draw.rect(DISPLAYSURF, GREEN,(pos[0],pos[1],10,50))
You can pass an area argument to Surface.blit. The area has to be a rect or rect-style tuple (x_coord, y_coord, width, height) and allows you to control the visible area of the surface. So if you have a surface that is 150 pixels wide and pass a rect with a width of 100, then the surface will only be rendered up to the 100 pixel border.
Now just use the heat value (which is the percentage of the image width) to calculate the current width, heat_rect.w/100*heat, and pass it to the blit method.
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
BG_COLOR = pg.Color(30, 30, 50)
HEAT_BAR_IMAGE = pg.Surface((150, 20))
color = pg.Color(0, 255, 0)
# Fill the image with a simple gradient.
for x in range(HEAT_BAR_IMAGE.get_width()):
for y in range(HEAT_BAR_IMAGE.get_height()):
HEAT_BAR_IMAGE.set_at((x, y), color)
if color.r < 254:
color.r += 2
if color.g > 1:
color.g -= 2
def main():
clock = pg.time.Clock()
heat_rect = HEAT_BAR_IMAGE.get_rect(topleft=(200, 100))
# `heat` is the percentage of the surface's width and
# is used to calculate the visible area of the image.
heat = 5 # 5% of the image are already visible.
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
if keys[pg.K_x]:
heat += 4 # Now 4% more are visible.
heat -= 1 # Reduce the heat every frame.
heat = max(1, min(heat, 100)) # Clamp the value between 1 and 100.
screen.fill(BG_COLOR)
screen.blit(
HEAT_BAR_IMAGE,
heat_rect,
# Pass a rect or tuple as the `area` argument.
# Use the `heat` percentage to calculate the current width.
(0, 0, heat_rect.w/100*heat, heat_rect.h)
)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
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.