Efekty nad fontem

... Petr Blahoš, 19. 12. 2023 Font Python

Minule jsme si ze znaků připravili svg. Teď to svg vezmeme, a tam, kde jsou čáry, si uděláme kuličky, a s nimi pak něco provedeme.

Paricles

Mám dilema, jak důkladně popisovat mechaniky pygame. Zůstanu asi jen u principu spritů a skupin. Sprite je něco, co má obdélník (rect), obrázek (image), a metodu update. Skupina obsahuje sprity, a umí na nich volat update, a vykreslit je.

Zkusme něco jednoduchého:

class Particle(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.start = (x, y)
        self.x = x
        self.y = y
        self.r = 0
        self.speed = random.randint(1, 5)

        self.image = pygame.Surface((self.r * 2, self.r * 2), pygame.SRCALPHA)
        self.rect = self.image.get_rect(center=(self.x, self.y))

    def update(self, dt):
        self.r += self.speed * dt / 400
        self.image = pygame.Surface((self.r * 2, self.r * 2), pygame.SRCALPHA)
        pygame.draw.circle(self.image, (192, 255, 255), (self.r, self.r), self.r, width=0)
        pygame.draw.circle(self.image, (0, 128, 128), (self.r, self.r), self.r, width=2)
        self.rect = self.image.get_rect(center=(self.x, self.y))

Tento particle vznikne, a pak se zvětšuje náhodnou rychlostí. To je vše. Pár jich naaranžujeme to kolečka, a necháme to běžět:

Růst

    self.particles = pygame.sprite.Group()
    for i in range(100):
        angle = random.random() * math.pi * 2
        self.particles.add(Particle(x + math.sin(angle) * r, y + math.cos(angle) * r))

    dt = 1
    clock = pygame.time.Clock()
    while do_run:
        self.screen.fill((0, 0, 0))
        self.particles.update(dt=dt)
        self.particles.draw(self.screen)

        pygame.display.flip()
        dt = clock.tick(60)

Celé je to tady.

Na tenhle styl to pak bude všechno ostatní. Chcete, aby částice padaly? Letěly na stranu? Měnily barvu? Jenom přepište update. Co třeba:

Hvězdičko blýskavá

Jak tušíte, tady používáme 2 typy spritů, nebo mohu-li to tak říct, particlů. Máme hvězdu, která vznikne v konstantní vzdálenosti od středu, a pak se pohybuje ven, a zároveň rotuje oproti středu. Když vypadne z obrazu, tak se zresetuje:

class Star(pygame.sprite.Sprite):
    def __init__(self, center, r, angle):
        super().__init__()
        self.center = center
        self.angle = angle
        self.dist0 = self.dist = r
        self.r = random.randrange(2, 6)
        self.speed = random.randint(1, 5)

        self.image = pygame.Surface((self.r * 2, self.r * 2), pygame.SRCALPHA)
        pygame.draw.circle(self.image, (192, 255, 255), (self.r, self.r), self.r, width=0)
        pygame.draw.circle(self.image, (0, 128, 128), (self.r, self.r), self.r, width=2)

    def update(self, dt):
        self.dist += self.speed * dt / 40
        self.angle += self.speed * dt / 40000
        self.speed *= 1.1
        x = self.center[0] + math.sin(self.angle) * self.dist
        y = self.center[1] + math.cos(self.angle) * self.dist
        self.rect = self.image.get_rect(center=(x, y))

        if x < 0 or x > PGMTest.WIDTH or y < 0 or y > PGMTest.HEIGHT:
            self.reset()

    def reset(self):
        self.dist = self.dist0
        self.angle = random.random() * math.pi * 2
        self.speed = random.randint(1, 5)

Pak máme tu žhavou kružnici, ve které zůstávají particly ve stejné vzdálenosti od středu, jen rotují.

class Glow(pygame.sprite.Sprite):
    def __init__(self, center, r, angle):
        super().__init__()
        self.center = center
        self.angle = angle
        self.dist0 = self.dist = r
        self.r = random.randrange(6, 12)
        self.speed = random.randint(15, 25)

        self.image = pygame.Surface((self.r * 2, self.r * 2), pygame.SRCALPHA)
        pygame.draw.circle(self.image, (255, random.randrange(78, 220), 1), (self.r, self.r), self.r, width=0)
        pygame.draw.circle(self.image, (255, 78, 1), (self.r, self.r), self.r, width=1)

    def update(self, dt):
        self.angle += self.speed * dt / 40000
        x = self.center[0] + math.sin(self.angle) * self.dist
        y = self.center[1] + math.cos(self.angle) * self.dist
        self.rect = self.image.get_rect(center=(x, y))

Celý kód najdete zde.

Zpět k fontu

Kružnice je jednoduchá. Teď ale musíme získat seznam bodů, na kterých vytvoříme ty particly, které pak necháme animovat. Vzpomínáte si z minula na náš self.bg_img a self.fg_img? Paradoxně, self.bg_img je ta kontura a self.fg_img je černě vyplněná plocha. Takže když si z self.bg_img vezmeme body, na ně umístíme particly, a pak to necháme běžet, měli bychom dostat to, co chceme.

Tato funkce vezme obrázek, rozčtverečkuje jej na čtverce o velikosti modulo, a pokud je v nějakém čtverci nějaká barva, tak vrátí souřadnice toho bodu:

    def extract_points(self, image: pygame.Surface, modulo: int = 2) -> list[tuple[int, int]]:
        points = []
        for x in range(0, image.get_width() // modulo * modulo, modulo):
            for y in range(0, image.get_height() // modulo * modulo, modulo):
                v = 0
                out = False
                for i in range(modulo):
                    for j in range(modulo):
                        if sum(image.get_at((x + i, y + j))[0:3]):
                            points.append((x + i, y + j))
                            out = True
                            break
                    if out:
                        break
        return points

Začneme prostě s těma zvětšujícíma se kuličkama:

Kuličky, kuličky

    def create_particles(self):
        self.points = self.extract_points(self.bg_img, 20)
        random.shuffle(self.points)
        self.particles = pygame.sprite.Group()
        for i in self.points:
            self.particles.add(Particle(*i))

Jak Particle jsme použili náš první Particle, a obrázek je starý známý self.fg_img. Celý kód naleznete zde.

O dalších efektech se nechci dál rozepisovat, vidíte asi sami, že na tom nic těžkého není. Dovolte mi jen zmínit, že pro efekt hoření jsem použil tento kód.

Cesta je prach a štěrk

Příště zkusíme, zda bychom dokázali jít po té cestě, která tvoří obrys písmena.


Komentáře byly zrušeny
V EU teď máme složitou situaci s Cookies. Na komentáře jsem používal jistou službu třetí strany. Ta však používá Cookies poměrně, ehm, benevolentně. Tak jsem se rozhodl komentáře zrušit. Pokud chcete, můžete mi napsat přímo