스프라이트

Ren'Py는 많은 이미지를 한꺼번에 표시할 수 있도록 스프라이트 시스템을 제공합니다. 각 스프라이트는 디스플레이어블 하나를 담으며 화면 위치와 앞뒤 순서를 바꿀 수 있습니다.

성능을 제외하면 스프라이트 시스템은 개념적으로 Transform()들을 Fixed()로 감싼 것과 비슷합니다. 스프라이트는 트랜스폼보다 훨씬 빠르지만 유연성은 낮습니다. 큰 성능 이점은 같은 디스플레이어블을 여러 스프라이트가 사용해도 프레임마다 한 번만 렌더링한다는 점입니다. 대신 Transform의 다양한 속성을 사용할 수 없고 xoffset과 yoffset만 바꿀 수 있습니다.

스프라이트 시스템을 사용하려면 SpriteManager 객체를 만든 뒤 create 메서드로 새 입자를 만듭니다. 필요에 따라 각 스프라이트의 xoffset, yoffset, zorder 필드를 갱신하여 화면에서 이동합니다. SpriteManager에 update와 event 인수를 전달하면 시간에 따라 변하거나 사용자 입력에 반응하게 할 수 있습니다.

스프라이트 클래스

class Sprite

SpriteManager가 관리하는 스프라이트입니다. 화면 배치를 제어하는 필드를 가집니다. 직접 생성하지 말고 SpriteManager.create()를 호출하여 만들어야 합니다.

스프라이트 객체의 필드는 다음과 같습니다.

x, y

SpriteManager를 기준으로 한 스프라이트 왼쪽 위 모서리의 x, y 좌표입니다.

zorder

SpriteManager 안의 다른 스프라이트와 비교한 표시 순서를 제어하는 정수입니다. 값이 클수록 사용자에게 가깝습니다.

events

True이면 자식에 이벤트를 전달합니다. 기본값인 False이면 자식이 이벤트를 무시하므로 처리 시간도 소모하지 않습니다.

Sprite 객체의 메서드는 다음과 같습니다.

destroy()

이 스프라이트를 파괴하여 표시되지 않도록 하고 SpriteManager에서 제거합니다.

set_child(d)

이 스프라이트에 연결된 디스플레이어블을 d로 바꿉니다.

class SpriteManager(update=None, event=None, predict=None, ignore_time=False, animation=True, **properties)

스프라이트들을 관리하고 가능한 한 빠르게 표시하는 디스플레이어블입니다.

update

None이 아니면 이 스프라이트 관리자가 스프라이트를 렌더링할 때마다 호출하는 함수입니다. 관리자를 처음 표시한 뒤 지난 초 수를 인수 하나로 받습니다. 함수와 SpriteManager를 다시 호출하고 렌더링할 때까지의 초 수를 반환해야 합니다.

event

None이 아니면 이벤트 발생 시 호출하는 함수입니다. pygame 이벤트 객체, 이벤트의 x 좌표, y 좌표, 스프라이트 관리자를 처음 표시한 뒤 지난 시간을 인수로 받습니다. None이 아닌 값을 반환하면 상호작용이 끝나고 그 값이 반환됩니다.

predict

None이 아니면 디스플레이어블 리스트를 반환하는 함수입니다. 스프라이트 관리자를 예측 로딩할 때 이 디스플레이어블들도 예측 로딩합니다.

ignore_time

True이면 디스플레이어블을 렌더링할 때 시간을 무시합니다. 상대적으로 작은 이미지 집합을 사용하고 이미지가 시간에 따라 변하지 않을 때 사용합니다. SpriteManager가 존재하는 동안 사용한 모든 디스플레이어블을 메모리에 유지하므로 디스플레이어블 수가 적을 때만 사용해야 합니다.

animation

True이면 애니메이션 시간 기준을 사용합니다. 두 번 표시할 때 상태가 초기화되는 것을 막습니다.

SpriteManager는 한 번 렌더링한 뒤 update 함수를 호출하기 전에 다음 필드를 갖습니다.

width, height

이 SpriteManager의 너비와 높이로, 픽셀 단위입니다.

SpriteManager의 메서드는 다음과 같습니다.

create(d)

디스플레이어블 d의 새 Sprite를 만들고 이 SpriteManager에 추가합니다.

redraw(delay=0)

delay초 뒤에 이 SpriteManager를 다시 그리도록 합니다.

SnowBlossom(d, count=10, border=50, xspeed=(20, 50), yspeed=(100, 200), start=0, fast=False, horizontal=False, distribution: Callable[[float, float], float] | str = 'linear', animation=False)

snowblossom 효과는 같은 스프라이트의 여러 인스턴스를 화면 위, 아래, 왼쪽 또는 오른쪽으로 이동합니다. 스프라이트가 화면을 벗어나면 시작점으로 되돌립니다.

d

스프라이트에 사용할 디스플레이어블입니다.

border

화면 테두리 영역의 크기입니다. 스프라이트가 이 영역을 완전히 벗어날 때까지 화면 안에 있는 것으로 간주하여 갑자기 사라지지 않게 합니다.

xspeed, yspeed

각각 가로와 세로 이동 속도입니다. 숫자 하나 또는 숫자 두 개의 튜플을 사용할 수 있습니다. 튜플이면 각 입자에 두 숫자 사이의 무작위 속도를 지정합니다. 양수와 음수 모두 가능하지만 튜플의 두 번째 숫자가 첫 번째보다 커야 합니다.

start

각 입자를 추가하기 전의 지연 시간으로, 초 단위입니다. 입자들이 화면 위쪽에서 시작하더라도 파도처럼 한꺼번에 밀려오는 모양이 되지 않도록 할 수 있습니다.

fast

참이면 입자들이 가장자리에서만 시작하지 않고 화면 중앙에서도 시작합니다.

horizontal

참이면 입자들이 위나 아래 대신 왼쪽이나 오른쪽에서 나타납니다.

distribution

입자의 시작 위치를 정하는 함수 또는 내장 분포 함수의 이름입니다.

문자열이면 다음 중 하나여야 합니다. linear: 축의 모든 위치가 같은 확률을 가집니다. gaussian: 가운데 근처일 확률이 높고 가장자리일 확률은 낮습니다. arcsine: 가장자리일 확률이 높고 가운데일 확률은 낮습니다.

함수이면 float 인수 두 개를 받아 float 하나를 반환해야 합니다.

기본값은 linear입니다.

animation

True이면 SnowBlossom이 애니메이션 시간 기준을 사용합니다. 두 번 표시할 때 상태가 초기화되는 것을 막습니다.

스프라이트 예제

SnowBlossom 클래스는 화면에 떨어지는 대상을 쉽게 배치하는 방법입니다.

image snow = SnowBlossom("snow.png", count=100)

SpriteManager로 복잡한 동작을 만드는 예제입니다. 입자 400개를 표시하고 마우스를 피하게 합니다.

init python:
    import math

    def repulsor_update(st):

        # If we don't know where the mouse is, give up.
        if repulsor_pos is None:
            return .01

        px, py = repulsor_pos

        # For each sprite...
        for i in repulsor_sprites:

            # Compute the vector between it and the mouse.
            vx = i.x - px
            vy = i.y - py

            # Get the vector length, normalize the vector.
            vl = math.hypot(vx, vy)
            if vl >= 150:
                continue

            # Compute the distance to move.
            distance = 3.0 * (150 - vl) / 150

            # Move
            i.x += distance * vx / vl
            i.y += distance * vy / vl

            # Ensure we stay on the screen.
            if i.x < 2:
                i.x = 2

            if i.x > repulsor.width - 2:
                i.x = repulsor.width - 2

            if i.y < 2:
                i.y = 2

            if i.y > repulsor.height - 2:
                i.y = repulsor.height - 2

        return .01

    # On an event, record the mouse position.
    def repulsor_event(ev, x, y, st):
        store.repulsor_pos = (x, y)


label repulsor_demo:

    python:
        # Create a sprite manager.
        repulsor = SpriteManager(update=repulsor_update, event=repulsor_event)
        repulsor_sprites = [ ]
        repulsor_pos = None

        # Ensure we only have one smile displayable.
        smile = Image("smile.png")

        # Add 400 sprites.
        for i in range(400):
            repulsor_sprites.append(repulsor.create(smile))

        # Position the 400 sprites.
        for i in repulsor_sprites:
            i.x = renpy.random.randint(2, 798)
            i.y = renpy.random.randint(2, 598)

        del smile
        del i

    # Add the repulsor to the screen.
    show expression repulsor as repulsor

    "..."

    hide repulsor

    # Clean up.
    python:
        del repulsor
        del repulsor_sprites
        del repulsor_pos