Set constant translucency for background - python

I am trying to put in a background image for my game and I need that at certain opacity. I have used the method set_alpha() of surface to control its translucency. However, this is creating a fade-in effect with the initial background at the set opacity turning to its full brightness eventually. Is there any way I can set the translucency of the background to a constant value?
My code is
self.bg = pygame.image.load("../game_images/background.png")
self.bg_size = self.bg.get_size()
self.bg_rect = self.bg.get_rect()
self.bg.set_alpha(5)
self.screen = pygame.display.set_mode(self.bg_size,pygame.FULLSCREEN)
self.screen.blit(self.bg, self.bg_rect)
pygame.display.update()
Any hints on how to do this?

It would help to see game loop.
Your image probably is getting bright because you not clearing the screen before it redrawn.
screen.fill((0,0,0))
screen.blit(self.bg, self.bg_rect)
pygame.display.update()
if you decide to do a self.bg.convert_alpha(). Then you need to use pygame.surfarray.pixels_alpha() and this require numpy to be install.
# value is a float .01 to .99
def set_alpha(image, value):
array = pygame.surfarray.pixels_alpha(image)
for x in xrange(array.shape[0]):
for y in xrange(array.shape[1]):
array[x][y] = int(array[x][y] * value)

Related

Pygame - blit is causing lag

I am making a simple platformer game, when I add a background using pygame.surface.blit() it causes a lot of lag, However when I use pygame.suface.fill() it runs at ~120 fps.
How do I fix this?
# map vars
mapFloor = []
ground = 700
mapController(True)
bg1Colour = bg1Colour = transform.scale(image.load('assets/PNG/Backgrounds/set1_background.png'), (1280, 1024))
bg1Colour = [bg1Colour, bg1Colour.get_rect()]
bg1Tiles = transform.scale(image.load('assets/PNG/Backgrounds/set1_tiles.png'), (1280, 1024))
bg1Tiles = [bg1Tiles, bg1Tiles.get_rect()]
bg1Hills = transform.scale(image.load('assets/PNG/Backgrounds/set1_hills.png'), (1280, 1024))
bg1Hills = [bg1Hills, bg1Hills.get_rect()]
while not finished:
for events in event.get():
exiCheck()
playerController(True)
surface.blit(bg1Colour[0], bg1Colour[1])
surface.blit(bg1Tiles[0], bg1Tiles[1])
surface.blit(bg1Hills[0], bg1Hills[1])
mapController(False)
player = playerController(False)
display.flip()
clock.tick(120)
Ensure that the background Surface has the same format as the display Surface. Use convert() (or convert_alpha()) to create a Surface that has the same pixel format. This improves performance when the background is, when the background is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation.
bg_surf = image.load('assets/PNG/Backgrounds/set1_background.png').convert()
bg1Colour = bg1Colour = transform.scale(bg_surf, (1280, 1024))
Just try to not using lists or tuple try by assingnig them so that the terminal should not check the lists and just use the signed values

How can i make all the images on a window scale down or up with the size of the monitor

So i am making a game in python and pygame and i have the indow setup like this
display = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
which makes the size of the window about 1334 X 800 so i based all the sprites and backgrounds on that size of screen but as you know not everyone has the same sized screen as me so my question is how can i make images scale with how big the monitor screen is
(P.S The game is in fullscreen mode)
First, how do you get the resolution and the scaling factor?
This is tricky, because someone's screen may not have the same aspect ratio as your 1334x800. You can letterbox (in various different ways) or stretch the sprites; you need to decide what you want, but I'll show one letterboxing possibility:
NOMINAL_WIDTH, NOMINAL_HEIGHT = 1334., 800.
surface = display.get_surface()
width, height = surface.get_width(), surface.get_height()
xscale = width / NOMINAL_WIDTH
yscale = height / NOMINAL_HEIGHT
if xscale < 1 and yscale < 1:
scale = max(xscale, yscale)
elif xscale > 1 and yscale > 1:
scale = min(xscale, yscale)
else:
scale = 1.0
Now, how do you scale each sprite and background?
Well, first, are you sure you want to? It may be simpler to just transform the whole surface. Whether this is slower or faster is hard to predict without testing (and probably not relevant anyway), but it will definitely look better (because any interpolation, dithering, antialiasing, etc. happens after compositing, instead of before—unless you're going for that 8-bit look, of course, in which case it will destroy the look…). You can do this by compositing everything to an off-screen surface of 1334x800 (or, better, scaling everything up by a constant factor), then transforming that surface for display. (Note that the transform methods include an optional DestSurface argument. You can use this to directly transform from the offscreen surface to the display's surface.)
But let's assume you want to do it the way you asked.
You can do this when loading the sprites. For example:
def rescale(surf, scale):
new_width, new_height = surf.get_width() * scale, surf.get_height() * scale
return pygame.transform.smoothscale(surf, (new_width, new_height))
class ScaledSprite(pygame.sprite.Sprite):
def __init__(self, path, scale):
pygame.sprite.Sprite.__init__(self)
self.image = rescale(pygame.image.load(path), scale)
self.rect = self.image.get_rect()
And the same for the backgrounds.
from this SO question, you can get the size of the monitor with
infoObject = pygame.display.Info()
which gets the height and width of the screen as infoObject.current_w and infoObject.current_h
You can then use these values to scale everything appropriately.

How do you make an image transparent over a period of time?

I am currently trying to display a death screen when the player dies in my game, but rather than it just popping up I want it to gradually becoming less and less transparent until the opacity is 256. I currently have this line of code below to display the image, but I was wondering how I would go about gradually increasing the opacity over a period of few seconds.
screen.blit(pygame.image.load("Images/dead.jpg"), (0, 0))
I've tried to add a for loop and tried to use convert_alpha, however I couldn't figure out a way of doing it, and any help will be greatly appreciated.
Here is what I would do. I have explained everything in the comments:
# Allow the image to have its alpha value changed
image = pygame.image.load("Images/dead.jpg").convert()
# Set the transparency to full
image_alpha = 0
# Decide how many frames you want the image to fade in over
fade_frame_number = 60
# And the frames per second
FPS = 30
FPS_Clock = pygame.time.Clock()
while True:
window.fill((255, 255, 255)) # Fill the window with a white bg
if image_alpha < 255:
image_alpha += 255 / fade_frame_number # Change the transparency variable
image.set_alpha(image_alpha) # Set the image's alpha value
window.blit(image, (0, 0)) # Display the image
pygame.display.update() # Update the screen
FPS_Clock.tick(FPS) # Wait for the next frame
In this, window is the display surface.

PyGame Platformer with Interactive Platforms "Drawn" In

I'm looking for the easiest way to implement this. I'm trying to implement platforms (with full collision detection) that you can draw in via mouse. Right now I have a line drawing function that actually draws small circles, but they're so close together that they more or less look like a line. Would the best solution be to create little pygame.Rect objects at each circle? That's going to be a lot of rect objects. It's not an image so pixel perfect doesn't seem like an option?
def drawGradientLine(screen, index, start, end, width, color_mode):
#color values change based on index
cvar1 = max(0, min(255, 9 * index-256))
cvar2 = max(0, min(255, 9 * index))
#green(0,255,0), blue(0,0,255), red(255,0,0), yellow(255,255,0)
if color_mode == 'green':
color = (cvar1, cvar2, cvar1)
elif color_mode == 'blue':
color = (cvar1, cvar1, cvar2)
elif color_mode == 'red':
color = (cvar2, cvar1, cvar1)
elif color_mode == 'yellow':
color = (cvar2, cvar2, cvar1)
dx = end[0] - start[0]
dy = end[1] - start[1]
dist = max(abs(dx), abs(dy))
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx)
y = int(start[1]+float(i)/dist*dy)
pygame.draw.circle(screen, color, (x, y), width)
That's my drawing function. And here's my loop that I have put in my main game event loop.
i = 0
while (i < len(pointList)-1):
drawGradientLine(screen, i, pointList[i], pointList[i + 1], r, mode)
i += 1
Thanks for any help, collision detection is giving me a huge headache right now (still can't get it right for my tiles either..).
Any reason you want to stick with circles?
Rectangles will make the line/rectangle a lot more smooth and will make collision detecting a lot easier unless you want to look into pixel perfect collision.
You also don't seem to save your drawn objects anywhere (like in a list or spritegroup), so how are you going to check for collision?
Here's a leveleditor I did for game awhile back, it's not perfect, but it works:
https://gist.github.com/marcusmoller/bae9ea310999db8d8d95
How it works:
The whole game level is divided up into 10x10px grid for easier drawing
The leveleditor check if the mouse is being clicked and then saves that mouse position
The player now moves the mouse to another position and releases the mouse button, the leveleditor now saves that new position.
You now have two different coordinates and can easily make a rectangle out of them.
Instead of creating a whole bunch of rect objects to test collision against, I'm going to recommend creating something called a mask of the drawn-in collideable object, and test for collision against that. Basically, a mask is a map of which pixels are being used and which are not in an image. You can almost think of it as a shadow or silhouette of a surface.
When you call pygame.draw.circle, you are already passing in a surface. Right now you are drawing directly to the screen, which might not be as useful for what I'm suggesting. I would recommend creating a rect which covers the entire area of the line being drawn, and then creating a surface of that size, and then draw the line to this surface. My code will assume you already know the bounds of the line's points.
line_rect = pygame.Rect(leftmost, topmost, rightmost - leftmost, bottommost - topmost)
line_surf = pygame.Surface((line_rect.width, line_rect.height))
In your drawGradientLine function, you'll have to translate the point coordinates to the object space of the line_surf.
while (i < len(pointList)-1):
drawGradientLine(line_surf, (line_rect.x, line_rect.y), i, pointList[i], pointList[i+1], r, mode)
i += 1
def drawGradientLine(surf, offset, index, start, end, width, color_mode):
# the code leading up to where you draw the circle...
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx) - offset[0]
y = int(start[1]+float(i)/dist*dy) - offset[1]
pygame.draw.circle(surf, color, (x, y), width)
Now you'll have a surface with the drawn object blitted to it. Note that you might have to add some padding to the surface when you create it if the width of the lines you are drawing is greater than 1.
Now that you have the surface, you will want to create the mask of it.
surf_mask = pygame.mask.from_surface(line_surf)
Hopefully this isn't getting too complicated for you! Now you can either check each "active" point in the mask for collision within a rect from your player (or whatever other objects you want to collide withe drawn-in platforms), or you can create a mask from the surface of such a player object and use the pygame.Mask.overlap_area function to check for pixel-perfect collision.
# player_surf is a surface object I am imagining exists
# player_rect is a rect object I am imagining exists
overlap_count = surf_mask.overlap_area(player_surf, (line_rect.x - player_rect.x, line_rect.y - player_rect.y))
overlap_count should be a count of the number of pixels that are overlapping between the masks. If this is greater than zero, then you know there has been a collision.
Here is the documentation for pygame.Mask.overlap_area: http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.overlap_area

Is there any way to "clear" a surface?

Is there any way to clear a surface from anything that has been blitted to it?
If what you want is to make a pygame Surface object "clean", that is erase all images blited to it, to blit new images to it every game loop and keep the peer pixel alpha without creating a new surface, you can fill it, but instead of a solid color, use a transparent color
from pygame import Color, Surface
empty = Color(0,0,0,0) #The last 0 indicates 0 alpha, a transparent color
field = Surface((width, height), flags=SRCALPHA)
#Inside the game loop
field.fill(empty)
*Sorry is my english is bad, still learning
When I dont care about performance, I use:
mysurface.fill((0,0,0))
Which will draw the specified color (black in this case) across the entire surface. Is that what you meant by "clear"?
Oh, and you need, of course, to "flip" the surface after doing this for it to be visible on the screen.
I don't know what API you're using, so here's a vague answer:
In virtually all cases "clearing" a surface simply blits a coloured quad of the same size as the surface onto it. The colour used is whatever you want your clear colour to be.
If you know how do blit, just blit a white quad of the same size onto the surface.
You can't undo one graphic written over the top of another graphic any more than you can undo one chalk illustration drawn over the top of another chalk illustration on the same board.
What is typically done in graphics is what you'd do with the chalkboard - clear the whole lot, and next time only redraw what you want to keep.
I had this problem too
To create the surface:
mask=pygame.Surface((180,100),pygame.SRCALPHA)
To clear the surface:
mask.fill
mask.set_alpha(255)
Draw your lines/images etc
Then blit this surface onto your main surface using the Alpha Blend flag
screen.blit(mask,(0,0),special_flags=(pygame.BLEND_RGBA_ADD))
.fill((255,255,255,0)) appears to work for me anyway.
I used the following in a game I made:
self.image = 0 #to empty it
self.image = pygame.image.load("image")
For pygame you can use Surface.fill
If you want to clear a surface before blitting, you can first fill the surface with a unlikely random color, set the alpha (colorkey) with that color, and then blit the original sprite with the transparent background back onto the surface.
idle_image = pg.image.load("sprite.png") #sprite with transparent background
temp_color = pg.Color(132, 23, 213, 255) #random unlikely replacement color
def clear_surface(surface):
surface.fill(st.temp_color)
surface.set_colorkey(st.temp_color)
surface.blit(idle_image, (0,0))
You can clear a surface by making it transparent.
Below is how I used the empty color RGB to make text blink by setting the surface to switch to a transparent version.
from __future__ import absolute_import, division, print_function
from itertools import cycle
import pygame
VISITOR_TTF_FILENAME = 'pixelFont.ttf'
BLINK_EVENT = pygame.USEREVENT + 0
empty = (255,255,255,0)
def main():
pygame.init()
try:
screen = pygame.display.set_mode((800, 600))
screen.fill((255,255,255))
screen_rect = screen.get_rect()
clock = pygame.time.Clock()
font = pygame.font.Font(VISITOR_TTF_FILENAME, 50)
on_text_surface = font.render(
'Press Any Key To Start', True, pygame.Color('green3')
)
blink_rect = on_text_surface.get_rect()
blink_rect.center = screen_rect.center
off_text_surface = pygame.Surface(blink_rect.size)
off_text_surface.fill(empty)
blink_surfaces = cycle([on_text_surface, off_text_surface])
blink_surface = next(blink_surfaces)
pygame.time.set_timer(BLINK_EVENT, 1000)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == BLINK_EVENT:
blink_surface = next(blink_surfaces)
screen.blit(blink_surface, blink_rect)
pygame.display.update()
clock.tick(60)
finally:
pygame.quit()
if __name__ == '__main__':
main()
When I want to get rid of something I use
screen.fill("Gray")
every time I update! So it hides it behind the gray screen.
Gray is not the only color available, so here is a list of all of the available colors you can use for pygame
Hope it helped!
Fill the surface with fill, then make it transparent with set_alpha
surface.fill
surface.set_alpha(255)

Categories