사용자 정의 디스플레이어블

Ren'Py의 동작을 사용자 지정하는 가장 복잡하면서도 강력한 방법은 제작자 정의 디스플레이어블을 사용하는 것입니다. 제작자 정의 디스플레이어블은 임의의 pygame 이벤트를 받을 수 있습니다. 다른 디스플레이어블을 렌더링하고 화면의 임의 위치에 배치할 수도 있습니다. 따라서 Ren'Py에서 제공하는 도구만으로 표현하기 어려운 2D 미니게임을 만드는 데 적합합니다. 비슷한 작업의 상당수를 더 높은 수준에서 구현하는 방법을 설명한 스프라이트 절도 참고하세요.

제작자 정의 디스플레이어블은 전적으로 Python으로 작성합니다. 만들기 전에 객체 지향 Python 프로그래밍을 어느 정도 익힐 것을 권장합니다.

예제

다음은 제작자 정의 디스플레이어블의 예입니다. 자식의 중심에서 마우스 포인터까지의 거리에 따라 알파 값을 정하고, 그 값으로 자식을 렌더링합니다.

init python:

    import math

    class Appearing(renpy.Displayable):

        def __init__(self, child, opaque_distance, transparent_distance, **kwargs):

            # Pass additional properties on to the renpy.Displayable
            # constructor.
            super(Appearing, self).__init__(**kwargs)

            # The child.
            self.child = renpy.displayable(child)

            # The distance at which the child will become fully opaque, and
            # where it will become fully transparent. The former must be less
            # than the latter.
            self.opaque_distance = opaque_distance
            self.transparent_distance = transparent_distance

            # The alpha channel of the child.
            self.alpha = 0.0

            # The width and height of us, and our child.
            self.width = 0
            self.height = 0

        def render(self, width, height, st, at):

            # Create a transform, that can adjust the alpha channel of the
            # child.
            t = Transform(child=self.child, alpha=self.alpha)

            # Create a render from the child.
            child_render = renpy.render(t, width, height, st, at)

            # Get the size of the child.
            self.width, self.height = child_render.get_size()

            # Create the render we will return.
            render = renpy.Render(self.width, self.height)

            # Blit (draw) the child's render to our render.
            render.blit(child_render, (0, 0))

            # Return the render.
            return render

        def event(self, ev, x, y, st):

            # Compute the distance between the center of this displayable and
            # the mouse pointer. The mouse pointer is supplied in x and y,
            # relative to the upper-left corner of the displayable.
            distance = math.hypot(x - (self.width / 2), y - (self.height / 2))

            # Base on the distance, figure out an alpha.
            if distance <= self.opaque_distance:
                alpha = 1.0
            elif distance >= self.transparent_distance:
                alpha = 0.0
            else:
                alpha = 1.0 - 1.0 * (distance - self.opaque_distance) / (self.transparent_distance - self.opaque_distance)

            # If the alpha has changed, trigger a redraw event.
            if alpha != self.alpha:
                self.alpha = alpha
                renpy.redraw(self, 0)

            # Pass the event to our child.
            return self.child.event(ev, x, y, st)

        def visit(self):
            return [ self.child ]

이 제작자 정의 디스플레이어블을 사용하려면 인스턴스를 만든 뒤 스크린에 추가하면 됩니다.

screen alpha_magic:
    add Appearing("logo.png", 100, 200):
        xalign 0.5
        yalign 0.5

label start:
    show screen alpha_magic

    "로고를 찾을 수 있겠어요?"

    return

renpy.Displayable

제작자 정의 디스플레이어블은 renpy.Displayable 클래스를 상속하여 만듭니다. render 메서드는 반드시 재정의해야 하며, 다른 메서드도 재정의할 수 있습니다.

디스플레이어블 객체는 피클로 직렬화할 수 있어야 합니다. 따라서 피클로 직렬화할 수 없는 객체의 참조를 포함해서는 안 됩니다. 특히 Render 객체를 제작자 정의 디스플레이어블에 저장할 수 없습니다.

디스플레이어블 클래스의 메서드는 재정의하여 사용하므로 self 매개변수를 포함해 설명합니다.

class renpy.Displayable

제작자 정의 디스플레이어블의 기반 클래스입니다.

__init__(**properties)

하위 클래스는 새 매개변수 등을 추가하기 위해 생성자를 재정의할 수 있습니다. 이때 알 수 없는 모든 키워드 인수를 다음 호출로 renpy.Displayable 생성자에 전달해야 합니다.

super(MyDisplayable, self).__init__(**properties)
render(self, width, height, st, at)

하위 클래스는 이 메서드를 반드시 재정의하여 renpy.Render 객체를 반환해야 합니다. Render 객체는 화면에 무엇을 표시할지, 또는 아무것도 표시하지 않을지 결정합니다.

width, height

이 디스플레이어블이 사용할 수 있는 공간입니다. 단위는 픽셀입니다.

st

표시 시간 기준의 초 단위 실수입니다. 표시 시간 기준은 이 디스플레이어블이 화면에 처음 표시될 때 시작합니다.

at

애니메이션 시간 기준의 초 단위 실수입니다. 애니메이션 시간 기준은 같은 태그의 이미지가 표시된 뒤 숨겨지지 않은 시점부터 시작합니다. 디스플레이어블을 태그 없이 표시하면 표시 시간 기준과 같습니다.

render 메서드는 디스플레이어블을 처음 표시할 때 호출됩니다. 이 객체에 대해 renpy.redraw()를 호출하면 다시 호출될 수 있습니다.

event(self, ev, x, y, st)

event 메서드는 제작자 정의 디스플레이어블에 pygame 이벤트를 전달하기 위해 호출됩니다. None 이외의 값을 반환하면 그 값이 상호작용의 결과로 반환됩니다. None을 반환하면 이벤트를 다른 디스플레이어블에 전달합니다.

None을 반환하지 않고 이벤트를 무시하려면 renpy.IgnoreEvent를 발생시킵니다.

다른 디스플레이어블에도 event 메서드가 있으므로 제작자 정의 디스플레이어블이 이벤트를 전달할 수 있습니다.

ev

이벤트 객체입니다.

x, y

디스플레이어블의 왼쪽 위 모서리를 기준으로 하는 이벤트의 x, y 좌표입니다. pygame 이벤트 객체의 위치 정보보다 이 좌표를 우선 사용해야 합니다.

st

표시 시간 기준의 초 단위 실수입니다.

각 상호작용이 시작할 때 이벤트가 발생하며, renpy.timeout()으로 추가 이벤트를 발생시킬 수 있습니다.

per_interact(self)

각 상호작용이 시작할 때 호출됩니다. 다시 그리기를 요청하는 데 사용할 수 있으며, 객체가 롤백에 참여한다면 이 메서드에서 다시 그리기를 요청하는 것이 좋습니다.

visit(self)

자식 디스플레이어블이 있다면 이 메서드를 재정의하여 자식들의 리스트를 반환해야 합니다. 그러면 자식의 per_interact 메서드도 호출되고, 자식에서 사용하는 이미지의 예측 로딩도 가능해집니다.

place(self, dest, x, y, width, height, surf, main=True)

이 디스플레이어블의 Render를 경계 영역 안에 배치합니다. 디스플레이어블을 배치한 위치의 (x, y) 튜플을 반환합니다.

dest

None이 아니면 계산한 좌표의 dest에 surf를 블릿합니다.

x, y, width, height

경계 영역입니다.

surf

배치할 Render입니다.

main

Render.blit()에 전달합니다.

renpy.Render

제작자 정의 디스플레이어블은 renpy.Render 객체를 사용합니다. 디스플레이어블에 renpy.render() 함수를 호출하면 Render 객체를 반환합니다. 제작자 정의 디스플레이어블은 render 메서드에서 renpy.Render를 호출하여 Render 객체를 만들어야 합니다.

Render 객체는 상속해서 사용하도록 설계되지 않았으므로, 암시적인 self 매개변수는 생략합니다.

class renpy.Render(width, height)

새 Render 객체를 만듭니다.

width, height

Render 객체의 픽셀 단위 너비와 높이입니다.

blit(source, pos, main=True)

이 Render 객체 안에 다른 Render 객체를 그립니다.

source

그릴 Render 객체입니다.

pos

그릴 위치입니다. 대상 Render의 왼쪽 위 모서리를 기준으로 한 픽셀 좌표의 (x, y) 튜플입니다.

main

키워드 전용 매개변수입니다. 참이면 스타일 검사기에 source가 표시됩니다.

place(d, x=0, y=0, width=None, height=None, st=None, at=None, render=None, main=True)

디스플레이어블 d를 렌더링하고, Ren'Py의 표준 배치 알고리즘으로 x, y, width, height가 정하는 사각형 안에 배치합니다. 배치한 위치의 (x, y) 튜플을 반환합니다. 위치는 Displayable.place() 메서드를 호출해 계산합니다.

x, y, width, height

배치할 사각형입니다. width 또는 height가 None이면 각각 이 Render의 너비와 높이를 사용합니다.

st, at

Render에 전달할 시간입니다. None이면 이 메서드를 호출한 render 메서드에 전달된 시간을 기본값으로 사용합니다.

render

None이 아니면 d를 렌더링하는 대신 이 값을 사용합니다.

main

.blit()과 같습니다.

canvas()

캔버스 객체를 반환합니다. 캔버스 객체에는 pygame.draw 함수에서 첫 번째 매개변수인 surface를 생략한 형태의 메서드가 있습니다.

Ren'Py에서는 arc와 ellipse 함수가 구현되어 있지 않습니다.

캔버스 객체에는 기반 pygame Surface를 반환하는 get_surface() 메서드도 있습니다.

get_size()

이 Render의 크기를 나타내는 (width, height) 튜플을 반환합니다.

subsurface(rect)

이 Render에서 사각형 영역을 잘라 낸 Render를 반환합니다.

rect

(x, y, width, height) 튜플입니다.

zoom(xzoom, yzoom)

이 디스플레이어블의 자식에 적용할 가로축과 세로축 확대율을 설정합니다. 자식만 확대되며 너비, 높이, 블릿 좌표는 확대되지 않습니다.

다음 속성과 메서드는 모델 기반 렌더링이 활성화된 경우에만 사용합니다.

mesh

이 필드는 이 Render에 모델 기반 렌더링을 활성화합니다. 참이면 다음과 같이 처리합니다.

True로 설정하면 다음과 같이 처리합니다.

  • 이 디스플레이어블의 모든 자식을 텍스처로 렌더링합니다.

  • 첫 번째 자식 크기의 메시를 이 디스플레이어블에 연결합니다.

  • 이 Render에 연결된 메시, 셰이더, 유니폼, 속성으로 모델을 만듭니다.

그런 다음 모델을 한 번의 연산으로 그립니다.

add_shader(shader)

이 Render 또는 자식을 그릴 때 셰이더 파트 shader를 사용합니다. 파트는 문자열이어야 하며, "-"로 시작하는 문자열을 지정하여 셰이더가 사용되지 않도록 할 수도 있습니다.

add_uniform(name, value)

이 Render 또는 자식을 그릴 때 유니폼 name의 값을 value로 설정합니다.

add_property(name, value)

이 Render 또는 자식을 그릴 때 GL 속성 name의 값을 value로 설정합니다.

유틸리티 함수와 클래스

renpy.displayable(d: DisplayableLike, scope: dict[str, Any] | None = None) → Displayable

디스플레이어블 객체나 문자열인 d를 받습니다. 문자열이면 일반적인 규칙에 따라 디스플레이어블로 변환합니다.

renpy.end_interaction(value)

value가 None이 아니면 현재 상호작용을 즉시 끝내고 value를 결과로 반환하게 합니다. value가 None이면 아무 일도 하지 않습니다.

제작자 정의 디스플레이어블의 render 및 event 메서드 안에서 호출할 수 있습니다.

renpy.is_pixel_opaque(d, width, height, st, at, x, y)

renpy.render(d, width, height, st, at)로 이 디스플레이어블을 렌더링했을 때 (x, y)의 픽셀이 불투명한지 반환합니다.

renpy.load_image(im) → 'renpy.display.render.Render'

이미지 캐시를 사용해 이미지 매니퓰레이터 im을 불러오고 Render를 반환합니다.

renpy.load_rgba(data: bytes, size: tuple[int, int]) → Any

이미지 데이터 bytes를 size 크기의 텍스처로 불러와 반환합니다.

data

RGBA8888 순서의 이미지 데이터를 담은 bytes 객체여야 합니다.

renpy.load_surface(im) → Surface

이미지 캐시를 사용해 이미지 매니퓰레이터 im을 불러오고 pygame Surface를 반환합니다.

renpy.map_event(ev, keysym)

pygame 이벤트 ev가 keysym과 일치하면 참을 반환합니다.

keysym

다음 중 하나입니다.

renpy.render(d, width, height, /, st, at)

디스플레이어블을 렌더링하고 renpy.Render 객체를 반환합니다.

d

렌더링할 디스플레이어블입니다.

width, height

디스플레이어블이 렌더링에 사용할 수 있는 너비와 높이입니다.

st, at

표시 시간 기준과 애니메이션 시간 기준입니다.

이 객체가 반환한 Render는 캐시될 수 있으므로 가져온 뒤에는 수정하지 않아야 합니다.

renpy.timeout(seconds)

seconds초가 지나기 전에 이벤트를 발생시킵니다. 사용자 정의 디스플레이어블의 event 메서드가 호출되도록 보장합니다.

renpy.redraw(d, when)

when초가 지나면 디스플레이어블 d를 다시 그립니다. 즉, render 메서드를 호출합니다. 자식을 다시 그리는 등의 이유로 그 전에 다시 그릴 수도 있으며, 이 경우 예약된 다시 그리기는 취소됩니다.

exception renpy.IgnoreEvent

이 예외를 발생시키면 Ren'Py가 이벤트를 무시합니다. event 메서드 안에서 발생시키려면 다음과 같이 작성합니다.

raise renpy.IgnoreEvent()