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()
Related
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)
I have a display area and a surface that is blitted on the display. On the surface is an image, in this case a rect. In the future it may be multiple rects or lines drawn on the surface keep that in mind.
I am trying to enlarge (by pressing x) the Rect named Sprite that is on SpriteSurface and SpriteSurface as well as the whole display window. The SpriteSurface image should be centered despite the resize. Currently the window will enlarge and the image stays centered, but if you uncomment the spritesizeX and Y lines the image gets larger but too big too fast and the window doesn't seem to enlarge big enough. Lowering the values shows that the offset of centering gets thrown off after the first resize. I feel like the solution should be relatively easy but im stumped. Any help would be appreciated.
Settings.py
spriteSizeX = 30
spriteSizeY = 30
SpHalfX = int(round(spriteSizeX / 2))
SpHalfY = int(round(spriteSizeY / 2))
multiplyer = 3
windowSizeX = int(round(spriteSizeX * multiplyer))
windowSizeY = int(round(spriteSizeY * multiplyer))
HalfWinX = int(round((windowSizeX / 2) - SpHalfX))
HalfWinY = int(round((windowSizeY / 2) - SpHalfY))
Orange = (238,154,0)
Gold = (255,215,0)
Black = (0,0,0)
Blue = (0,0,255)
Gray = (128,128,128)
DarkGray = (100,100,100)
Green = (0,128,0)
Lime = (0,255,0)
Purple = (128,0,128)
Red = (255,0,0)
Teal = (0,200, 128)
Yellow = (255,255,0)
White = (255,255,255)
run = True
SpriteCapture.py
#!/usr/local/bin/python3.6
import sys, pygame
from pygame.locals import *
from settings import *
pygame.init()
pygame.display.set_caption("Sprite Capture")
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
while run == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if pygame.key.get_pressed()[pygame.K_s]:
pygame.image.save(SpriteSurface, 'img1.png')
run = False
if pygame.key.get_pressed()[pygame.K_q]:
run = False
if pygame.key.get_pressed()[pygame.K_z]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += -10
windowSizeY += -10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
if pygame.key.get_pressed()[pygame.K_x]:
#spriteSizeX += 10
#spriteSizeY += 10
windowSizeX += 10
windowSizeY += 10
HalfWinX = int(round(windowSizeX / 2 - SpHalfX))
HalfWinY = int(round(windowSizeY / 2 - SpHalfY))
Screen = pygame.display.set_mode((windowSizeX, windowSizeY),RESIZABLE)
SpriteSurface = pygame.Surface((spriteSizeX,spriteSizeY))
Sprite = Sprite = Rect(0,0,spriteSizeX,spriteSizeY)
Screen.fill(Black)
pygame.draw.rect(SpriteSurface,Orange,Sprite)
Screen.blit(SpriteSurface, (HalfWinX,HalfWinY))
pygame.display.flip()
If you want to scale your surfaces or rects according to the screen size, you can define a zoom_factor variable which you can just increase when a key gets pressed and then use it to scale the window and the surfaces. Multiply it by the original screen width and height to scale the window, and also scale your surfaces with pygame.transform.rotozoom and pass the zoom_factor as the scale argument.
import sys
import pygame
from pygame.locals import *
width = 30
height = 30
multiplyer = 3
window_width = round(width * multiplyer)
window_height = round(height * multiplyer)
zoom_factor = 1
ORANGE = (238,154,0)
BLACK = (0,0,0)
pygame.init()
screen = pygame.display.set_mode((window_width, window_height), RESIZABLE)
screen_rect = screen.get_rect() # A rect with the size of the screen.
clock = pygame.time.Clock()
# Keep a reference to the original image to preserve the quality.
ORIG_SURFACE = pygame.Surface((width, height))
ORIG_SURFACE.fill(ORANGE)
surface = ORIG_SURFACE
# Center the rect on the screen's center.
rect = surface.get_rect(center=screen_rect.center)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
run = False
elif event.key == pygame.K_z:
zoom_factor = round(zoom_factor-.1, 1)
# Scale the screen.
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect() # Get a new rect.
# Scale the ORIG_SURFACE (the original won't be modified).
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center) # Get a new rect.
elif event.key == pygame.K_x:
zoom_factor = round(zoom_factor+.1, 1)
w, h = int(window_width*zoom_factor), int(window_height*zoom_factor)
screen = pygame.display.set_mode((w, h), RESIZABLE)
screen_rect = screen.get_rect()
surface = pygame.transform.rotozoom(ORIG_SURFACE, 0, zoom_factor)
rect = surface.get_rect(center=screen_rect.center)
# Note that the rect.w/screen_rect.w ratio is not perfectly constant.
print(zoom_factor, screen_rect.w, rect.w, rect.w/screen_rect.w)
screen.fill(BLACK)
screen.blit(surface, rect) # Blit the surface at the rect.topleft coords.
pygame.display.flip()
clock.tick(60)
Alternatively, you could just blit all of your surfaces onto a background surface, then scale this background with pygame.transform.rotozoom each frame and blit it onto the screen. However, scaling a big background surface each frame will be bad for the performance.
I've recently written a program with a list, but now I need to change the list into a matrix. I programmed a grid by drawing rectangles. Now I want to change the color of the rectangle when I click on it. In my program with the list everything worked just fine, but now I have to use a matrix, because I need a matrix for the rest of my program. I've already got a matrix with all zeros, but now I want to change the 0 in to a 1 when I click on a rectangle.
x = 5
y = 5
height = 30
width = 50
size = 20
color = (255,255,255)
new_color = (0,255,0)
screen.fill((0,0,0))
def draw_grid():
for y in range(height):
for x in range(width):
rect = pygame.Rect(x * (size + 1),y * (size + 1),size,size)
pygame.draw.rect(screen,color,rect)
x += 20
y += 20
rects = [[0 for i in range(width)] for j in range(height)]
draw_grid()
while 1:
clock.tick(30)
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if menu == 'start':
if pygame.mouse.get_pressed()[0]:
mouse_pos = pygame.mouse.get_pos()
for i,(rect,color) in enumerate(rects):
if rect.collidepoint(mouse_pos):
rects[i] = (rect,new_color)
for rect,color in rects:
pygame.draw.rect(screen,color,rect)
pygame.display.flip()
This is the code I used with the list, but I've already replaced the list with a matrix. When I run this code it gives an error:
ValueError: too many values to unpack
What is the best way to solve this problem?
To draw the rects you can iterate over the matrix and depending on the value (0 or 1) draw a white rect or a green rect. (You could also store the colors directly in the matrix, but I don't know if you want to do something else with it.)
To change the color of the clicked cells, you can easily calculate the indexes of the cells by floor dividing the mouse coords by (size+1), e.g. x = mouse_x // (size+1). Then just set matrix[y][x] = 1.
import sys
import pygame
WHITE = pygame.Color('white')
GREEN = pygame.Color('green')
def draw_grid(screen, matrix, size):
"""Draw rectangles onto the screen to create a grid."""
# Iterate over the matrix. First rows then columns.
for y, row in enumerate(matrix):
for x, color in enumerate(row):
rect = pygame.Rect(x*(size+1), y*(size+1), size, size)
# If the color is white ...
if color == 0:
pygame.draw.rect(screen, WHITE, rect)
# If the color is green ...
elif color == 1:
pygame.draw.rect(screen, GREEN, rect)
def main():
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
height = 30
width = 50
size = 20 # Cell size.
matrix = [[0 for i in range(width)] for j in range(height)]
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if pygame.mouse.get_pressed()[0]:
# To change the color, calculate the indexes
# of the clicked cell like so:
mouse_x, mouse_y = pygame.mouse.get_pos()
x = mouse_x // (size+1)
y = mouse_y // (size+1)
matrix[y][x] = 1
screen.fill((30, 30, 30))
# Now draw the grid. Pass all needed values to the function.
draw_grid(screen, matrix, size)
pygame.display.flip()
clock.tick(30)
if __name__ == '__main__':
pygame.init()
main()
pygame.quit()
sys.exit()
I've already programmed a grid, but now I want to change the color of a single rectangle in the grid.
x = 5
y = 5
height = 30
width = 50
size = 20
color = (255,255,255)
new_color = (255,255,0)
screen.fill((0,0,0))
def draw_grid():
for y in range(height):
for x in range(width):
rect = pygame.Rect(x * (size + 1),y * (size + 1),size,size)
pygame.draw.rect(screen,color,rect)
x += 20
rects.append((rect,color))
y += 20
rects = []
colored_rects = []
while 1:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
draw_grid()
if pygame.mouse.get_pressed()[0]:
mouse_pos = pygame.mouse.get_pos()
for i,(rect,color) in enumerate(rects):
if rect.collidepoint(mouse_pos):
rects[i] = (rect,new_color)
colored_rects.append((rect,new_color))
for rect,color in rects:
pygame.draw.rect(screen,color,rect)
for rect,new_color in colored_rects:
pygame.draw.rect(screen,new_color,rect)
pygame.display.flip()
clock.tick()
Now I only want to change one rectangle when I click on it, but later they must change automatically (for example when there are three rectangles touching in the same color, they all must become white). I've updated a little bit, but there are still some problems. For example: You have to click on the rectangle till it changes color, and it takes to much time te change color.
One solution would be to store the rects together with their color in tuples. If the mouse button is pressed, you iterate over the rectangles list, if a rectangle collides with the mouse, you create a tuple with the rect and the new color and replace the tuple at the current index.
import sys
import pygame as pg
def main():
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
height = 30
width = 50
size = 20
color = (255, 255, 255)
new_color = (255, 255, 0)
rectangles = []
for y in range(height):
for x in range(width):
rect = pg.Rect(x * (size+1), y * (size+1), size, size)
# The grid will be a list of (rect, color) tuples.
rectangles.append((rect, color))
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
if pg.mouse.get_pressed()[0]:
mouse_pos = pg.mouse.get_pos()
# Enumerate creates tuples of a number (the index)
# and the rect-color tuple, so it looks like:
# (0, (<rect(0, 0, 20, 20)>, (255, 255, 255)))
# You can unpack them directly in the head of the loop.
for index, (rect, color) in enumerate(rectangles):
if rect.collidepoint(mouse_pos):
# Create a tuple with the new color and assign it.
rectangles[index] = (rect, new_color)
screen.fill((30, 30, 30))
# Now draw the rects. You can unpack the tuples
# again directly in the head of the for loop.
for rect, color in rectangles:
pg.draw.rect(screen, color, rect)
pg.display.flip()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
I want to add gradient to the ball in this program & also possibly the waves drawn to fade into the colour of the background (as if glowing) instead of one colour fills.
I've looked at tons of tutorials however none of them are making much sense to my syntax, the general idea to me is confusing as I have moving objects that draw the space I want to add gradient to quite slowly. Can anyone give an insight into how I can do this?
code:
import sys, pygame, math
from pygame.locals import *
# set up of constants
WHITE = (255, 255, 255)
DARKRED = (128, 0, 0)
RED = (255, 0, 0)
BLACK = ( 0, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
BGCOLOR = WHITE
screen = pygame.display.set_mode()
WINDOWWIDTH = 800 # width of the program's window, in pixels
WINDOWHEIGHT = 800 # height in pixels
WIN_CENTERX = int(WINDOWWIDTH / 2) # the midpoint for the width of the window
WIN_CENTERY = int(WINDOWHEIGHT / 2) # the midpoint for the height of the window
screen = pygame.display.get_surface()
FPS = 160 # frames per second to run at
AMPLITUDE = 80 # how many pixels tall the waves with rise/fall.
# standard pygame setup code
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.RESIZABLE)
pygame.display.set_caption('Window title')
fontObj = pygame.font.Font('freesansbold.ttf', 16)
# variables that track visibility modes
showSine = True
showSquare = True
pause = False
xPos = 0
step = 0 # the current input f
posRecord = {'sin': [], 'square': []} # keeps track of the ball positions for drawing the waves
yPosSquare = AMPLITUDE # starting position
# main application loop
while True:
# event handling loop for quit events
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
# fill the screen to draw from a blank state
DISPLAYSURF.fill(BGCOLOR)
# sine wave
yPos = -1 * math.sin(step) * AMPLITUDE
posRecord['sin'].append((int(xPos), int(yPos) + WIN_CENTERY))
if showSine:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, RED, (int(xPos), int(yPos) + WIN_CENTERY), 10)
sinLabelRect.center = (int(xPos), int(yPos) + WIN_CENTERY + 20)
DISPLAYSURF.blit(sinLabelSurf, sinLabelRect)
# draw the waves from the previously recorded ball positions
if showSine:
for x, y in posRecord['sin']:
pygame.draw.circle(DISPLAYSURF, DARKRED, (x,y), 4)
#drawing horizontal lines
# square
posRecord['square'].append((int(xPos), int(yPosSquare) + WIN_CENTERY))
if showSquare:
# draw the sine ball and label
pygame.draw.circle(DISPLAYSURF, GREEN, (int(xPos), int(yPosSquare) + WIN_CENTERY), 10)
squareLabelRect.center = (int(xPos), int(yPosSquare) + WIN_CENTERY + 20)
DISPLAYSURF.blit(squareLabelSurf, squareLabelRect)
# draw the waves from the previously recorded ball positions
if showSquare:
for x, y in posRecord['square']:
pygame.draw.circle(DISPLAYSURF, BLUE, (x, y), 4)
# draw the border
pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1)
pygame.display.update()
FPSCLOCK.tick(FPS)
if not pause:
xPos += 1
#wave movement
if xPos > WINDOWWIDTH:
#sine
xPos = 0
posRecord['sin'] = []
step = 0
# square
yPosSquare = AMPLITUDE
posRecord['square'] = []
else:
#sine
step += 0.008
#step %= 2 * math.pi
# square
# jump top and bottom every 100 pixels
if xPos % 100 == 0:
yPosSquare *= -1
# add vertical line
for x in range(-AMPLITUDE, AMPLITUDE):
posRecord['square'].append((int(xPos), int(x) + WIN_CENTERY))
Use SPACE to change background color.
First line use only transparency - and has no problem with different background color.
Second line changes only circles color - and depends on background color.
Third and fourth line (it is the same line with different starting color) change circles color and transparency - and depends on background color.
Second and last line look good on one color background and need more work to find good-looking fading.
import pygame
pygame.init()
screen = pygame.display.set_mode((600,200))
#--------------------------------------
# circles positions and transparency (x,y, alpha)
circles = []
for x in range(100):
circles.append( [100+x*3, 200, x*2] )
#--------------------------------------
white = True # background color
#--------------------------------------
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_SPACE:
white = not white
#--------------------------------------
if white:
screen.fill((255,255,255))
else:
screen.fill((0,0,0))
#--------------------------------------
# first
circle_img = pygame.Surface((20,20))
pygame.draw.circle(circle_img, (255,0,0), (10,10), 10)
circle_img.set_colorkey(0)
for x in circles:
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],40))
#--------------------------------------
# second
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
screen.blit(circle_img, (x[0],90))
#--------------------------------------
# last
circle_img = pygame.Surface((20,20))
for x in circles:
pygame.draw.circle(circle_img, (255,255-x[2],255-x[2]), (10,10), 10)
circle_img.set_colorkey(0)
circle_img.set_alpha(x[2])
screen.blit(circle_img, (x[0],140))
#--------------------------------------
pygame.display.flip()
pygame.quit()