So I read the documentation of pygame but I could not understand it clearly. I recently asked a question about bitmap fonts and I got some code as my answer; here is the code:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
font = pygame.font.Font("freesansbold.ttf", 32)
i = 0
text = "hello how are you?"
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
letter = text[i]
text_1 = font.render(letter, True, (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
# print(glyph_rect)
if glyph_rect:
gh = glyph_rect[0].height
print(f'letter {letter} bitmap height: {bh} glyph height: {gh}')
win.fill((0, 0, 0))
win.blit(text_1, (0, 0))
pygame.display.update()
i += 1
run = i < len(text)
pygame.quit()
So, my questions are on the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects().
What does the pygame.mask.from_surface() function do?
What does the line glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects() do?
What arguments does the variable glyph_rect return, and what is the meaning of those arguments?
pygame.mask.from_surface creates a pygame.mask.Mask object form a pygame.Surface.
A Surface is bitmap. A Mask is an 2 dimensional array with Boolean values. The Mask created is the size of the _Surface. A field is True if the corresponding pixel in the surface is not transparent, and False if it is transparent.
pygame.mask.Mask.get_bounding_rects creates a list of pygame.Rect objects. Each rectangle describes a bounding area of connected pixles.
If the Surface contains exactly 1 connected image, you will get exactly 1 rectangle surrounding the image.
See the example. The black rectangle is the Surface rectangle and the red rectangle is the bound rectangle of the connected component:
repl.it/#Rabbid76/ImageHitbox
import pygame
def getMaskRect(surf, top = 0, left = 0):
surf_mask = pygame.mask.from_surface(surf)
rect_list = surf_mask.get_bounding_rects()
surf_mask_rect = rect_list[0].unionall(rect_list)
surf_mask_rect.move_ip(top, left)
return surf_mask_rect
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
try:
my_image = pygame.image.load('Bomb-256.png')
except:
my_image = pygame.Surface((200, 200), pygame.SRCALPHA)
pygame.draw.circle(my_image, (0, 128, 0), (60, 60), 40)
pygame.draw.circle(my_image, (0, 0, 128), (100, 150), 40)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = window.get_rect().center
my_image_rect = my_image.get_rect(center = pos)
my_image_mask_rect = getMaskRect(my_image, *my_image_rect.topleft)
window.fill((255, 255, 255))
window.blit(my_image, my_image_rect)
pygame.draw.rect(window, (0, 0, 0), my_image_rect, 3)
pygame.draw.rect(window, (255, 0, 0), my_image_mask_rect, 3)
pygame.display.flip()
pygame.quit()
exit()
Related
Description
Hi, I am trying to make objects to take damage and I want to add a cool effect.
Problem
When I type this command: player.img.fill((255, 255, 255))
I get this:
and I want to get this:
Thanks!
First you have to get the mask from the image with pygame.mask.from_surface:
player_mask = pygame.mask.from_surface(player.img)
Then you can use to_surface to create an image from the mask. Make the background of the image transparent with set_colorkey():
mask_image = player_mask.to_surface(setcolor = (255, 255, 255))
mask_image.set_colorkey((0, 0, 0))
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((200, 100))
clock = pygame.time.Clock()
player_image = pygame.image.load('Bird.png')
player_mask = pygame.mask.from_surface(player_image)
mask_image = player_mask.to_surface(setcolor = (255, 255, 255))
mask_image.set_colorkey((0, 0, 0))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill((0, 32, 64))
window.blit(player_image, player_image.get_rect(center = (50, 50)))
window.blit(mask_image, mask_image.get_rect(center = (150, 50)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
How can i make this kind of countdown in Pygame? (i'm looking for how could i make the circle's perimeter decrease, that's the issue, because displaying the time isn't hard )
Keep in mind that how long the perimeter of the circle is and the displayed time should be in proportion with each other.
Just use pygame.draw.arc and specify the stop_angle argument depending on the counter:
percentage = counter/100
end_angle = 2 * math.pi * percentage
pygame.draw.arc(window, (255, 0, 0), arc_rect, 0, end_angle, 10)
Minimal example:
import pygame
import math
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArc(surf, color, center, radius, width, end_angle):
arc_rect = pygame.Rect(0, 0, radius*2, radius*2)
arc_rect.center = center
pygame.draw.arc(surf, color, arc_rect, 0, end_angle, width)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArc(window, (255, 0, 0), (100, 100), 90, 10, 2*math.pi*counter/100)
pygame.display.flip()
pygame.quit()
exit()
Sadly the quality of pygame.draw.arc with a width > 1 is poor. However this can be improved, using cv2 and cv2.ellipse:
import pygame
import cv2
import numpy
pygame.init()
window = pygame.display.set_mode((200, 200))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
counter = 100
text = font.render(str(counter), True, (0, 128, 0))
timer_event = pygame.USEREVENT+1
pygame.time.set_timer(timer_event, 1000)
def drawArcCv2(surf, color, center, radius, width, end_angle):
circle_image = numpy.zeros((radius*2+4, radius*2+4, 4), dtype = numpy.uint8)
circle_image = cv2.ellipse(circle_image, (radius+2, radius+2),
(radius-width//2, radius-width//2), 0, 0, end_angle, (*color, 255), width, lineType=cv2.LINE_AA)
circle_surface = pygame.image.frombuffer(circle_image.flatten(), (radius*2+4, radius*2+4), 'RGBA')
surf.blit(circle_surface, circle_surface.get_rect(center = center))
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == timer_event:
counter -= 1
text = font.render(str(counter), True, (0, 128, 0))
if counter == 0:
pygame.time.set_timer(timer_event, 0)
window.fill((255, 255, 255))
text_rect = text.get_rect(center = window.get_rect().center)
window.blit(text, text_rect)
drawArcCv2(window, (255, 0, 0), (100, 100), 90, 10, 360*counter/100)
pygame.display.flip()
pygame.quit()
exit()
I have a question regarding the glyph a font and the pygame width of a font yesterday i some code regarding this here is the code:
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
font = pygame.font.Font("freesansbold.ttf", 32)
i = 0
text = "hello how are you?"
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
letter = text[i]
text_1 = font.render(letter, True, (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
# print(glyph_rect)
if glyph_rect:
gh = glyph_rect[0].height
print(f"{glyph_rect[0]}")
print(f'letter {letter} bitmap height: {bh} glyph height: {gh} width = {glyph_rect[0].width} width_2 = {bw}')
win.fill((0, 0, 0))
win.blit(text_1, (0, 0))
pygame.display.update()
i += 1
run = i < len(text)
pygame.quit()
# win.blit(text_1, (0, 0))
I know why the glyph height is diffrent because i asked that in a question and i got a answer for that but why is the glyph width always a bit less than the actual width? Can someone explain me in detail
The width is different because there is a space between characters when they are connected to words.
glyph_rect[0].width is the width of the glyph. bw is the width of the bitmap. The glyph does not fill the entire bitmap and has some space on both sides.
You can investigate this with the following program (each letter is displayed for 1 second):
import pygame
pygame.init()
win = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
font = pygame.font.Font("freesansbold.ttf", 200)
i = 0
text = "hello how are you?"
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
letter = text[i]
text_1 = font.render(letter, True, (0, 0, 0))
text_draw = font.render(letter, True, (0, 0, 0), (255, 255, 255))
bw, bh = font.size(letter)
glyph_rect = pygame.mask.from_surface(text_1).get_bounding_rects()
if glyph_rect:
gh = glyph_rect[0].height
print(f"{glyph_rect[0]}")
print(f'letter {letter} bitmap height: {bh} glyph height: {gh} width = {glyph_rect[0].width} width_2 = {bw}')
win.fill((128, 128, 128))
win.blit(text_draw, (100, 100))
for r in glyph_rect:
draw_rect = r.move(100, 100)
pygame.draw.rect(win, (255, 0, 0), draw_rect, 3)
pygame.display.update()
i += 1
run = i < len(text)
clock.tick(1)
pygame.quit()
I tried to run this program, however this error comes up:
Traceback (most recent call last):
File "C:/Python/ComputingCW/ComputingCW.py", line 12, in <module>
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
TypeError: 'pygame.Surface' object is not callable
It used to be:
Surface = pygame.display.set_mode((W, H), 0, 32) and then using "Surface.fill(BLACK)" to make a black background.
I don't understand why won't it allow me to use a image as my background. Can someone please help me, and explain why is this happening.
import pygame, sys, time
from pygame.locals import *
pygame.init()
mainClock = pygame.time.Clock()
# all_fonts = pygame.font.get_fonts()
W = 1280
H = 900
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
pygame.display.set_caption('Physics in Motion')
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
game = True
mouse_pos = (0, 0)
mouse_click = (0, 0)
text1B = False
text2B = False
text3B = False
output = ''
while game == True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYUP:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
if event.type == MOUSEMOTION:
mouse_pos = event.pos
if event.type == MOUSEBUTTONUP:
mouse_click = event.pos
color = WHITE
Font = pygame.font.SysFont('arial', 72)
if text1B:
color = RED
text = Font.render('Start Simulation', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H / 5)
if text_rect.collidepoint(mouse_click):
output = 'Simulation()'
if text_rect.collidepoint(mouse_pos):
text1B = True
else:
text1B = False
Surface.blit(text, text_rect)
color = WHITE
if text2B:
color = RED
Font = pygame.font.SysFont('arial', 72)
text = Font.render('Graph Generator', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 2 / 5)
if text_rect.collidepoint(mouse_click):
output = 'GraphG()'
if text_rect.collidepoint(mouse_pos):
text2B = True
else:
text2B = False
Surface.blit(text, text_rect)
color = WHITE
if text3B:
color = RED
Font = pygame.font.SysFont('arial', 72)
text = Font.render('Exit', True, color)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 3 / 5)
if text_rect.collidepoint(mouse_click):
pygame.quit()
sys.exit()
if text_rect.collidepoint(mouse_pos):
text3B = True
else:
text3B = False
Surface.blit(text, text_rect)
Font = pygame.font.SysFont('arial', 72)
text = Font.render(output, True, BLUE)
text_rect = text.get_rect()
text_rect.center = (W / 2, H * 4 / 5)
Surface.blit(text, text_rect)
pygame.display.flip()
mainClock.tick(100000)
This line pygame.image.load('Pygame_Background.jpg') loads an image from your hard disk and turns it into a pygame.Surface object. Now you have this surface and try to call it like a function with parentheses behind it ((W, H), 0, 32) and that causes the TypeError, because a surface object isn't a function (it's not callable).
What you actually want to do is:
Create the display window (I call it screen here).
screen = pygame.display.set_mode((W, H), 0, 32)
Load the background image (better call it background because Surface is the name of the pygame.Surface class).
background = pygame.image.load('Pygame_Background.jpg').convert()
Also, call the pygame.Surface.convert or convert_alpha method to improve the performance.
Blit the background and everything else onto the screen in the main while loop.
screen.blit(background, (0, 0))
screen.blit(text, text_rect)
This looks like a name conflict. pygame.Surface is a module (or a sub module), but this line seems to overwrite this Surface object:
Surface = pygame.image.load('Pygame_Background.jpg')((W, H), 0, 32)
I would try to use different variable in the left hand side, for example surf_bg.
Try using the absolute path of the file of 'Pygame_Background.jpg'
Trace your directory starting from your Drive's letter
I want to draw a polygon on screen, like so:
coords = (653,333),(680,444),(680,444),(653,445)
pygame.draw.polygon(screen, (0, 0, 255), coords, 0)
however this does not draw to the screen. Any help appreciated.
ANSWER: coords should be a list of tuples, so for my example the correct answer would be:
coords = [(653,333), (680,444), (680,444), (653,445)]
pygame.draw.polygon(screen, (0, 0, 255), coords, 0)
The code that you gave seems to be correct. I've tested it and it displays a triangle. As suggested by skrx in the comment your screen size is too small and you are not able to see it.
import pygame
pygame.init()
BLUE = (0, 0, 255)
size = [1920, 1080]
screen = pygame.display.set_mode(size)
done = False
clock = pygame.time.Clock()
while not done:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
temp = (653, 333), (680, 444), (680, 444), (653, 445)
pygame.draw.polygon(screen, BLUE, temp, 0)
pygame.display.flip()
pygame.quit()