import math
import random
import pygame
# Parametry okna
WIDTH, HEIGHT = 900, 520
FPS = 60
# Kolory
BG_COLOR = (30, 40, 50) # tło za szybą
SNOW_FLAKE = (240, 240, 255) # kolor płatków (wizualizacja)
WIPER_COLOR = (20, 20, 25) # ramię wycieraczki
# Parametry wycieraczki
PIVOT = (120, HEIGHT - 80) # punkt obrotu (x, y)
WIPER_LEN = 520 # długość pióra
WIPER_WIDTH = 10 # grubość ramienia (wizualizacja)
SWEEP_MIN_DEG = 10 # minimalny kąt
SWEEP_MAX_DEG = 120 # maksymalny kąt
SWEEP_SPEED_DEG = 120 # deg/s
WIPE_BAND = 36 # szerokość „czyszczonego” pasa w px
# Parametry śniegu
FALL_COUNT = 140
FALL_SPEED_MIN = 70
FALL_SPEED_MAX = 140
FALL_WIND_MIN = -20
FALL_WIND_MAX = 30
FALL_SIZE_MIN = 2
FALL_SIZE_MAX = 4
ACCUM_ALPHA = 15 # jak mocno płatki „brudzą” szybę
def deg2rad(a):
return a * math.pi / 180.0
def clamp(v, lo, hi):
return max(lo, min(hi, v))
class Snowflake:
def __init__(self):
self.reset()
def reset(self):
self.x = random.uniform(0, WIDTH)
self.y = random.uniform(-HEIGHT, 0)
self.vy = random.uniform(FALL_SPEED_MIN, FALL_SPEED_MAX)
self.vx = random.uniform(FALL_WIND_MIN, FALL_WIND_MAX)
self.r = random.randint(FALL_SIZE_MIN, FALL_SIZE_MAX)
def update(self, dt):
self.x += self.vx * dt
self.y += self.vy * dt
if self.x < -10 or self.x > WIDTH + 10 or self.y > HEIGHT + 10:
self.reset()
def draw(self, surf):
pygame.draw.circle(surf, SNOW_FLAKE, (int(self.x), int(self.y)), self.r)
def draw_wiper(surf, pivot, angle_deg, length, color):
ax = pivot[0] + length * math.cos(deg2rad(angle_deg))
ay = pivot[1] - length * math.sin(deg2rad(angle_deg))
pygame.draw.line(surf, color, pivot, (ax, ay), WIPER_WIDTH)
# końcówka pióra
tip_w = 18
nx = -math.sin(deg2rad(angle_deg))
ny = math.cos(deg2rad(angle_deg))
tip = (ax, ay)
p1 = (tip[0] + nx * tip_w, tip[1] + ny * tip_w)
p2 = (tip[0] - nx * tip_w, tip[1] - ny * tip_w)
pygame.draw.polygon(surf, color, [p1, p2, tip])
def wipe_clear(mask_surf, pivot, angle_deg, length, band_px):
# Czyści pas wzdłuż ramienia: wypełnia przezroczystością po łuku
clear = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
# wyznacz łuk: próbkuj punkty co kilka stopni wokół pivotu
steps = 48
angle = deg2rad(angle_deg)
dx = math.cos(angle)
dy = -math.sin(angle)
tip = (pivot[0] + length * dx, pivot[1] + length * dy)
# Rysuj gruby przezroczysty „pędzel” na linii od pivot do tip
# Realizujemy to jako wiele kół wzdłuż segmentu
segs = max(16, int(length / 12))
for i in range(segs + 1):
t = i / segs
x = pivot[0] + (tip[0] - pivot[0]) * t
y = pivot[1] + (tip[1] - pivot[1]) * t
pygame.draw.circle(clear, (0, 0, 0, 0), (int(x), int(y)), band_px // 2)
# Mieszamy tak, aby tam gdzie clear jest „puste”, maska stawała się przezroczysta
# Użyj operacji blit z flagą specjalną: per-pixel alpha wymazująca
mask_surf.blit(clear, (0, 0), special_flags=pygame.BLEND_RGBA_MIN)
def accumulate_snow(mask_surf, flakes):
# Dodaj delikatny „brudzący” ślad płatków na masce
painter = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
for f in flakes:
# imitacja osiadania: delikatne półprzezroczyste białe plamki
alpha = ACCUM_ALPHA
pygame.draw.circle(painter, (255, 255, 255, alpha), (int(f.x), int(f.y)), f.r + 1)
# Na maskę dodajemy alfa
mask_surf.blit(painter, (0, 0))
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Symulacja wycieraczek i śniegu (Pygame)")
clock = pygame.time.Clock()
# Tło (możesz podmienić na zdjęcie wnętrza auta)
background = pygame.Surface((WIDTH, HEIGHT))
background.fill(BG_COLOR)
# Maska „śniegu” na szybie (cała lekko przysypana)
snow_mask = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
snow_mask.fill((255, 255, 255, 140)) # startowy nalot śnieżny
# Płatki
flakes = [Snowflake() for _ in range(FALL_COUNT)]
# Ruch wycieraczki — wahadło
angle = SWEEP_MIN_DEG
direction = 1 # 1 w górę, -1 w dół
running = True
font = pygame.font.SysFont(None, 20)
while running:
dt = clock.tick(FPS) / 1000.0
for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
# Aktualizacja kąta
angle += direction * SWEEP_SPEED_DEG * dt
if angle >= SWEEP_MAX_DEG:
angle = SWEEP_MAX_DEG
direction = -1
elif angle <= SWEEP_MIN_DEG:
angle = SWEEP_MIN_DEG
direction = 1
# Aktualizacja płatków
for f in flakes:
f.update(dt)
# Rysowanie tła
screen.blit(background, (0, 0))
# Akumulacja śniegu
accumulate_snow(snow_mask, flakes)
# Czyszczenie pasem wycieraczki
wipe_clear(snow_mask, PIVOT, angle, WIPER_LEN, WIPE_BAND)
# Nałóż maskę na tło
screen.blit(snow_mask, (0, 0))
# Rysuj płatki (wizualnie)
for f in flakes:
f.draw(screen)
# Rysuj wycieraczkę
draw_wiper(screen, PIVOT, angle, WIPER_LEN, WIPER_COLOR)
# HUD
hud = font.render("ESC zamknij, FPS ~{}".format(int(clock.get_fps())), True, (220, 220, 230))
screen.blit(hud, (10, 10))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()