having problem with drawing lines thickness in pygame - python

Here is the board I drew using pygame: https://i.stack.imgur.com/Hne6A.png
I'm facing a bug with the thickness of the last two lines as I marked them on the image. I believe there is something wrong with the if statement in my code but I can't quite figure it out, it just won't take effect.
here is the code that has drawn the board above:
import pygame, sys
pygame.init()
width, height = 750, 750
rows, cols = 9, 9
BLACK = (0,0,0)
screen = pygame.display.set_mode((width, height))
screen.fill((255, 255, 255, 255))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
sys.exit()
surf = pygame.Surface((600, 600))
surf.fill((255,255,255))
padding = surf.get_width()/9
for i in range(rows+1):
if i % 3 == 0:
thick = 4
else:
thick = 1
pygame.draw.line(surf, BLACK, (0, i*padding), (width, i*padding), width=thick)
pygame.draw.line(surf, BLACK, (i*padding, 0), (i*padding, height), width=thick)
surf_center = (
(width-surf.get_width())/2,
(height-surf.get_height())/2
)
screen.blit(surf, surf_center)
pygame.display.update()
pygame.display.flip()

To draw a thick line, half the thickness is applied to both sides of the line. The simplest solution is to make the target surface a little larger and add a small offset to the coordinates of the lines.
For performance reasons, I also recommend creating the surface before the application loop and continuously blit it in the application loop:
import pygame, sys
pygame.init()
width, height = 750, 750
rows, cols = 9, 9
BLACK = (0,0,0)
screen = pygame.display.set_mode((width, height))
surf_size = 600
surf = pygame.Surface((surf_size+4, surf_size+4))
surf.fill((255,255,255))
padding = surf_size/9
for i in range(rows+1):
thick = 4 if i % 3 == 0 else 1
offset = i * padding + 1
pygame.draw.line(surf, BLACK, (0, offset), (width, offset), width=thick)
pygame.draw.line(surf, BLACK, (offset, 0), (offset, height), width=thick)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255, 255))
screen.blit(surf, surf.get_rect(center = screen.get_rect().center))
pygame.display.update()
pygame.display.flip()
pygame.quit()
sys.exit()

The code below should do the trick. The thing is that the width in pygame.draw.line is drawn in the middle of the line. So, the border lines are actually cropped in half because the half on one side and another half on the other side are not taken into account. But the catch is that if the thickness is even there is an offset of 1 on the borders as the width is larger on one of the sides.
Even if the thickness (bold_think) is raised or lowered the algorithm below will work flawlessly without any gaps outside the grid.
Thickness of 4
Thickness of 8
Thinkness of 15
import pygame
import sys
pygame.init()
width, height = 750, 750
rows, cols = 9, 9
BLACK = (0, 0, 0)
screen = pygame.display.set_mode((width, height))
screen.fill((255, 255, 255, 255))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
sys.exit()
surf = pygame.Surface((600, 600))
surf.fill((255, 255, 255))
bold_thick = 4
# Calculates the offset of 1 for the even widths
thick_offset = (bold_thick - 1) % 2
offset = (bold_thick / 2) - thick_offset
# The padding should take into account the borders
padding = (surf.get_width() - bold_thick) / 9
for i in range(rows+1):
if i % 3 == 0:
thick = bold_thick
else:
thick = 1
pygame.draw.line(surf, BLACK, (0, i*padding + offset),
(width, i*padding + offset), width=thick)
pygame.draw.line(surf, BLACK, (i*padding + offset, 0),
(i*padding + offset, height), width=thick)
surf_center = (
(width-surf.get_width())/2,
(height-surf.get_height())/2
)
screen.blit(surf, surf_center)
pygame.display.update()
pygame.display.flip()

Related

Im trying to make a fade in and out

I am trying to create a function that will fade in and out for a game that I'm making. the problem is that the first part works fine but the second pard doesn't work.
WIDTH = screen width, HEIGHT = screen height, WINDOW = the name of the window, and I imported pygame as pg
def fade():
fade = pg.Surface((WIDTH, HEIGHT))
fade.fill((0,0,0))
opacity = 0
for r in range(0, 100):
opacity += 1
fade.set_alpha(opacity)
WINDOW.blit(fade, (0,0))
pg.display.update()
pg.time.delay(10)
for r in range(0, 100):
opacity -= 1
fade.set_alpha(opacity)
WINDOW.blit(fade, (0,0))
pg.display.update()
pg.time.delay(10)
From looking at the code everything should work fine but it doesn't. I didn't paste the entire code because its 300 lines.
What they actually do is draw a transparent image over the background over and over again until the background is completely covered. You can't make the background visible again by blending the image with less transparency over it. You have to completely redraw the whole scene in every frame, blending an ever more agile transparent image over it.
I suggest to write a blitFadeIn and blitFadeOut function and call it in the application loop.
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *background.get_size(), (128, 128, 128), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
[pygame.draw.rect(background, color, rect) for rect, color in tiles]
font = pygame.font.SysFont(None, 100)
text = font.render("image", True, (255, 255, 0))
image = pygame.Surface(window.get_size(), pygame.SRCALPHA)
pygame.draw.ellipse(image, "red", window.get_rect().inflate(-20, -20))
image.blit(text, text.get_rect(center = window.get_rect().center))
image.set_alpha(0)
def blitFadeIn(target, image, pos, step=2):
alpha = image.get_alpha()
alpha = min(255, alpha + step)
image.set_alpha(alpha)
target.blit(image, pos)
return alpha == 255
def blitFadeOut(target, image, pos, step=2):
alpha = image.get_alpha()
alpha = max(0, alpha - step)
image.set_alpha(alpha)
target.blit(image, pos)
return alpha == 0
fade_in = False
fade_out = False
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN:
if not fade_in and not fade_out:
fade_in = True
window.blit(background, (0, 0))
if fade_in:
done = blitFadeIn(window, image, (0, 0))
if done:
fade_in, fade_out = False, True
if fade_out:
done = blitFadeOut(window, image, (0, 0))
if done:
fade_out = False
pygame.display.flip()
pygame.quit()
exit()
Also see:
How to fade from one colour to another in pygame?
How to fade in a text or an image with PyGame
Pygame: Frame ghosting?

How can I visualize the overlapping area of 2 masks in pygame?

When i create a mask in Pygame, for example:
self.mask = pygame.mask.from_surface(self.image)
Can i see the result, the grid with Boolean values?
And question 2, can I make an overlap visible when two mask overlap?
Can i see the result, the grid with Boolean values.
You can only see what you are drawing on the screen. A mask cannot be drawn on the screen so you cannot see it.
Can I make an overlap visible when two mask overlap?
Create a Mask containing the overlapping set bits between 2 Masks with pygame.mask.Mask.overlap_mask.
Convert the Mask to a Surface with pygame.mask.Mask.to_surface. e.g.:
overlap_mask = mask1.overlap_mask(mask2, (offset_x, offset_y))
overlap_surf = overlap_mask.to_surface(setcolor= (255, 0, 0))
overlap_surf.set_colorkey((0, 0, 0))
Minimal example:
The overlapping pixels are colored red.
import pygame, math
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
image1 = pygame.image.load("Banana.png")
image2 = pygame.image.load("Bird.png")
rect1 = image1.get_rect(center = (165, 150))
rect2 = image1.get_rect(center = (135, 150))
mask1 = pygame.mask.from_surface(image1)
mask2 = pygame.mask.from_surface(image2)
angle = 0
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
angle += 0.01
rect1.centery = 150 + round(60 * math.sin(angle))
offset_x = rect2.x - rect1.x
offset_y = rect2.y - rect1.y
overlap_mask = mask1.overlap_mask(mask2, (offset_x, offset_y))
overlap_surf = overlap_mask.to_surface(setcolor = (255, 0, 0))
overlap_surf.set_colorkey((0, 0, 0))
window.fill(0)
window.blit(image1, rect1)
window.blit(image2, rect2)
window.blit(overlap_surf, rect1)
pygame.display.flip()
pygame.quit()
exit()

How do I focus light or how do I only draw certain circular parts of the window in pygame?

For this, if you're familiar with it, think the dark mode in the boo levels in Super Mario Maker 2. I'm trying to create a circular spotlight around the character that will also make anything within the circles range visible (eg part of the floor being stood on, an enemy or anything else from the scene). My plan to do that is to first draw the circle/spotlight, then the scene and then the character. Then I want anything not highlighted by the spotlight to be blacked out.
So my question is:
Does anybody know how to fill the entire screen with the exception of what's within the circle?
I suggest a solution, which combines a clipping region pygame.Surface.set_clip and drawing a black rectangle with a circular transparent area in the center.
Define a radius and create a square pygame.Surface with twice the radius.
radius = 50
cover_surf = pygame.Surface((radius*2, radius*2))
Set a white color key which identifies the transparent color (set_colorkey) a nd draw a white (transparent) circle on the surface:
cover_surf.set_colorkey((255, 255, 255))
pygame.draw.circle(cover_surf, (255, 255, 255), (radius, radius), radius)
Define the center of the circular region which you want to see (in the following clip_center).
In the main application loop, clear the display and set the clipping region, the draw the scene. Before you update the display draw cover_surf in the clipping region:
while run:
# [...]
# clear screen and set clipping region
screen.fill(0)
clip_rect = pygame.Rect(clip_center[0]-radius, clip_center[1]-radius, radius*2, radius*2)
screen.set_clip(clip_rect)
# draw the scene
# [...]
# draw transparent circle and update display
screen.blit(cover_surf, clip_rect)
pygame.display.flip()
Minimal example: repl.it/#Rabbid76/PyGame-ClipCircularRegion-2
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
radius = 50
cover_surf = pygame.Surface((radius*2, radius*2))
cover_surf.fill(0)
cover_surf.set_colorkey((255, 255, 255))
pygame.draw.circle(cover_surf, (255, 255, 255), (radius, radius), radius)
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
clip_center = pygame.mouse.get_pos()
# clear screen and set clipping region
screen.fill(0)
clip_rect = pygame.Rect(clip_center[0]-radius, clip_center[1]-radius, radius*2, radius*2)
screen.set_clip(clip_rect)
# draw the scene
for x in range(10):
for y in range(10):
color = (255, 255, 255) if (x+y) % 2 == 0 else (255, 0, 0)
pygame.draw.rect(screen, color, (x*50, y*50, 50, 50))
# draw transparent circle and update display
screen.blit(cover_surf, clip_rect)
pygame.display.flip()
If you want multiple circular drawing areas, then create a pygame.Surface.set_clip with the same size as the display and set whit color key:
cover_surf = pygame.Surface((400, 400))
cover_surf.set_colorkey((255, 255, 255))
Fill the entire surface black and draw white circles on the surface:
cover_surf.fill(0)
pygame.draw.circle(cover_surf, (255, 255, 255), (100, 100), 50)
pygame.draw.circle(cover_surf, (255, 255, 255), (300, 300), 70)
Blit the cover_surf on the window, before updating the display:
while run:
# [...]
# draw transparent circle and update display
screen.blit(cover_surf, (0, 0))
pygame.display.flip()
Minimal example: repl.it/#Rabbid76/PyGame-ClipCircularRegion-3
import pygame
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
cover_surf = pygame.Surface((400, 400))
cover_surf.set_colorkey((255, 255, 255))
px = [100, 200, 300]
dx = [1, 2, 3]
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# create cover surface
cover_surf.fill(0)
for i in range(3):
radius = 40 + i*20
pygame.draw.circle(cover_surf, (255, 255, 255), (px[i], 100+(i*100)), radius)
px[i] += dx[i]
if px[i] < radius or px[i] > 400 - radius:
dx[i] = -dx[i]
# draw the scene
for x in range(10):
for y in range(10):
color = (255, 255, 255) if (x+y) % 2 == 0 else (255, 0, 0)
pygame.draw.rect(screen, color, (x*50, y*50, 50, 50))
# draw transparent circle and update display
screen.blit(cover_surf, (0, 0))
pygame.display.flip()

How to change rect color on mouse click Python (pygame)

I created a window with a width and height of 800 pixels using pygame then drew rectangles with size 32 to make the window a 25x25 grid. What I want to do is change the color of the rectangle I click to change.
My Code:
def createGrid():
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
frame.fill(WHITE)
for y in range(SCREEN_HEIGHT):
for x in range(SCREEN_WIDTH):
rect = pygame.Rect(x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1)
pygame.draw.rect(frame, (0,250,0), rect)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
First you should create list with all rects and they colors.
Next you should draw all rect inside while loop.
And finally you have to use event.MOUSEBUTTONDOWN to get mouse click and compare mouse position with every rect on list and change color on list.
import pygame
import sys
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
BLOCK_SIZE = 32
WHITE = (255,255,255)
pygame.init()
frame = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("PathFinder")
# create list with all rects
all_rects = []
for y in range(0, SCREEN_HEIGHT, BLOCK_SIZE):
row = []
for x in range(0, SCREEN_WIDTH, BLOCK_SIZE):
rect = pygame.Rect(x, y, BLOCK_SIZE-1, BLOCK_SIZE-1)
row.append([rect, (0, 255, 0)])
all_rects.append(row)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# check which rect was clicked and change its color on list
for row in all_rects:
for item in row:
rect, color = item
if rect.collidepoint(event.pos):
if color == (0, 255, 0):
item[1] = (255, 0, 0)
else:
item[1] = (0, 255, 0)
# draw all in every loop
frame.fill(WHITE)
for row in all_rects:
for item in row:
rect, color = item
pygame.draw.rect(frame, color, rect)
pygame.display.flip()
Eventually you could draw all rects before loop and use event to draw new rect on clicked rect. But you still need list with all rects to check which rect was click and what is its color.
It depends on what your code is intended to do? This example shows how to paint to the primary display surface but doesn't keep track of which colour is where.
import pygame
white = (255, 255, 255)
red = (255, 0, 0)
size = 32
pygame.init()
s = pygame.display.set_mode((800, 800))
s.fill(white)
# press escape to exit example
while True:
e = pygame.event.get()
if pygame.key.get_pressed()[pygame.K_ESCAPE]: break
x = int(pygame.mouse.get_pos()[0] / size) * size
y = int(pygame.mouse.get_pos()[1] / size) * size
if pygame.mouse.get_pressed()[0]:
pygame.draw.rect(s, red, (x, y, size, size), 0)
pygame.display.update()
pygame.time.Clock().tick(60)
pygame.quit()

Pygame: Rescale pixel size

With pygame, I created a 20x20 pixel window and added a 2x2 pixel rectangle.
When I run the program, the window size is super small and I can barely see the rectangle. How can I increase the window size whilst keeping the number of pixels constant, i.e. increase the pixel size? I am aware of this similar question, but there a somewhat more complicated case is discussed.
import pygame
screen_width, screen_height = 20, 20
x, y = 10, 10
rect_width, rect_height = 2, 2
vel = 2
black = (0, 0, 0)
white = (255, 255, 255)
pygame.init()
win = pygame.display.set_mode((screen_width, screen_height))
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.fill(black)
pygame.draw.rect(win, white, (x, y, rect_width, rect_height))
pygame.display.update()
pygame.quit()
Don't draw directly to the screen, but to another Surface.
Then scale that new Surface to the size of the screen and blit it onto the real screen surface.
Here's an example:
import pygame
screen_width, screen_height = 20, 20
scaling_factor = 6
x, y = 10, 10
rect_width, rect_height = 2, 2
vel = 2
black = (0, 0, 0)
white = (255, 255, 255)
pygame.init()
win = pygame.display.set_mode((screen_width*scaling_factor, screen_height*scaling_factor))
screen = pygame.Surface((screen_width, screen_height))
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.fill(black)
pygame.draw.rect(screen, white, (x, y, rect_width, rect_height))
win.blit(pygame.transform.scale(screen, win.get_rect().size), (0, 0))
pygame.display.update()
pygame.quit()

Categories