레이블과 제어 흐름

label 문

label 문은 프로그램의 특정 지점에 이름을 붙입니다. Ren'Py 스크립트, Python 함수 또는 스크린에서 호출하거나 이동하기 위한 용도로만 존재합니다.

label sample1:
    "여기는 'sample1' 레이블입니다."

label sample2(a="default"):
    "여기는 'sample2' 레이블입니다."
    "a = [a]"

label 문에는 블록을 연결할 수 있습니다. 레이블에 도달하면 블록에 들어가고, 블록 끝에 도달하면 label 문 다음부터 계속 실행합니다. 다음 코드는 "origin" 레이블로 이동하면 "a, b, c" 순서로 실행됩니다.

label origin:
"a"
label hasblock:
    "b"
"c"
return

레이블에는 전역과 지역 두 종류가 있습니다. 전역 레이블은 모든 프로젝트 파일이 공유하는 하나의 전역 네임스페이스에 있으므로 게임 안에서 이름이 고유해야 합니다. 지역 레이블은 전역 레이블에 속하므로, 서로 다른 전역 레이블에 속한다면 같은 이름을 사용할 수 있습니다. 지역 레이블을 선언하려면 이름 앞에 마침표 .를 붙이고 소속될 전역 레이블 아래에 놓습니다. 예를 들면 다음과 같습니다.

label global_label:
    "전역 레이블 아래에는..."
label .local_label:
    "...지역 레이블이 있습니다."
    jump .another_local
label .another_local:
    "여기에도 하나 더 있어요!"
    jump .local_label

전역 이름과 지역 이름을 점으로 연결한 전체 이름으로 지역 레이블을 선언할 수도 있습니다. 이 경우 전역 레이블은 선언하지 않습니다.

label global_label.yet_another_local:
    "또 하나 있어요!"
    jump .local_label

지역 레이블은 선언한 전역 레이블 안에서는 직접 참조할 수 있고, 전역과 지역 부분을 합친 전체 이름으로도 참조할 수 있습니다.

label another_global:
    "이제 다른 곳에 있는 지역 레이블로 점프해 보겠습니다."
    jump global_label.local_name

label 문에는 선택적인 매개변수 목록을 지정할 수 있습니다. 두 예외를 제외하면 PEP 570에 따라 처리합니다.

첫째, 매개변수의 기본값은 호출 시점에 평가합니다.

둘째, 변수는 어휘적 스코프가 아닌 동적 스코프를 사용합니다. 레이블 매개변수로 값을 받은 변수는 return 문에 도달하면 이전 값으로 돌아가며, 원래 없던 변수라면 사라집니다. 또한 특정 변수를 사용하는 문에서 그 변수가 레이블 매개변수의 값을 받는지는 해당 문에 도달한 경로에 따라 달라질 수 있습니다. 순수 Python 코드에서는 불가능한 동작입니다.

default a = 3

label start:
    menu:
        "호출하기":
            call label_with_params(5)
        "점프하기":
            jump label_without_params
    jump start

label label_with_params(a):
label label_without_params:
    e "a = [a]" # displays 5 or 3 depending on what path was taken
    return

매개변수가 있는 레이블에 jump나 앞선 문의 순차 실행으로 도달하는 것은 일반적으로 적절하지 않습니다. 매개변수 사용 예는 call 문을 참고하세요.

jump 문

jump 문은 지정한 레이블로 제어를 넘깁니다.

expression 키워드가 있으면 뒤의 표현식을 평가하고 결과 문자열을 이동할 레이블 이름으로 사용합니다. expression이 없으면 레이블 이름을 명시해야 합니다.

expression 사용 여부와 관계없이 지역 레이블 이름을 전달할 수 있습니다. "global_label.local_label"처럼 전역 이름을 붙이거나 ".local_label"처럼 점으로 시작할 수 있습니다.

call과 달리 jump는 다음 문을 스택에 넣지 않습니다. 따라서 이동하기 전 위치로 돌아갈 방법이 없습니다.

label loop_start:

    e "이런! 무한 루프에 갇힌 것 같아요."

    jump loop_start

call 문

call 문은 지정한 레이블로 제어를 넘깁니다. 다음 문을 호출 스택에 넣으므로 return 문으로 call 다음의 문에 복귀할 수 있습니다.

expression 키워드가 있으면 뒤의 표현식을 평가하고 결과 문자열을 호출할 레이블 이름으로 사용합니다. expression이 없으면 레이블 이름을 명시해야 합니다.

expression 사용 여부와 관계없이 지역 레이블 이름을 전달할 수 있습니다. "global_label.local_label"처럼 전역 이름을 붙이거나 ".local_label"처럼 점으로 시작할 수 있습니다.

선택적인 from 절을 지정하면 call 바로 다음에 해당 이름의 label 문을 넣는 효과가 있습니다. 명시적 레이블이 있으면 스크립트 변경 후 복귀 스택이 있는 저장 파일을 불러와도 올바른 위치로 돌아가도록 보장하는 데 도움이 됩니다.

call 문은 인수를 받을 수 있으며, PEP 448에 따라 처리합니다.

call expression에 인수 목록을 사용하면 표현식과 인수 목록 사이에 pass 키워드를 넣어야 합니다. 그렇지 않으면 인수 목록을 call의 일부가 아니라 표현식의 일부로 파싱합니다.

label start:

    e "먼저 서브루틴을 호출하겠습니다."

    call subroutine

    call subroutine(2)

    call expression "sub" + "routine" pass (count=3)

    return

# ...

label subroutine(count=1):

    e "여기에 [count]번 왔습니다."
    e "다음으로 서브루틴에서 돌아가겠습니다."

    return

경고

나중에 업데이트를 배포할 계획이라면 각 call 문에 from 절을 넣지 않은 채 게임을 배포하는 것은 위험합니다. 해당 절 없이 call이 담긴 파일을 수정하면, 호출한 레이블 안에서 만든 저장 파일이 손상될 수 있습니다.

배포판을 빌드할 때 호출에 from 절 추가(Add from clauses to calls) 옵션을 사용하면 해결할 수 있습니다.

return 문

return 문은 호출 스택의 맨 위 문을 꺼내 그곳으로 제어를 넘깁니다. 스택이 비어 있으면 Ren'Py를 다시 시작하고 메인 메뉴로 돌아갑니다.

선택적인 표현식을 지정하면 평가한 결과를 _return 변수에 저장합니다. 이 변수는 컨텍스트별 동적 스코프를 사용합니다.

특수 레이블

Ren'Py는 다음 레이블을 사용합니다.

start

기본적으로 게임을 시작하면 이 레이블로 이동합니다.

quit

있으면 사용자가 게임을 종료할 때 새 컨텍스트에서 호출합니다.

after_load

있으면 게임을 불러올 때 호출합니다. 업데이트에 맞춰 데이터를 수정하는 데 사용할 수 있습니다. 데이터를 바꿨다면 renpy.block_rollback()을 호출해야 불러온 지점 이전으로 롤백할 때 변경이 취소되지 않습니다.

before_load

있으면 게임을 불러오기 직전에 새 컨텍스트에서 호출합니다. 음악을 바꾸거나 애니메이션을 표시하는 등 불러오기를 준비할 수 있습니다.

레이블이 반환하면 불러오기를 진행합니다. 불러오기가 끝날 때까지 화면을 갱신하지 않으므로 애니메이션은 실행되지 않습니다. 끝나면 게임 상태가 불러온 상태로 교체되므로 여기서 바꾼 내용은 사라집니다.

splashscreen

있으면 게임을 처음 실행할 때 메인 메뉴 표시 전에 호출합니다. 시작 화면 추가하기를 참고하세요.

before_main_menu

있으면 메인 메뉴 전에 호출합니다. 배경 동영상 재생 등 메인 메뉴를 준비하는 드문 경우에 사용합니다.

main_menu

있으면 메인 메뉴 대신 호출합니다. 반환하면 start 레이블에서 게임을 시작합니다. 예를 들어 다음은 메인 메뉴 없이 즉시 게임을 시작합니다.

label main_menu:
    return
after_warp

있으면 워프 후 목적지의 문을 실행하기 전에 호출합니다. 특정 줄로 워프하기를 참고하세요.

hide_windows

있으면 플레이어가 마우스 오른쪽 버튼이나 H 키로 창을 숨길 때 호출합니다. 참을 반환하면 이미 숨겼다고 가정하여 기본 숨기기를 취소하고, 그 밖에는 숨기기를 계속합니다.

일부 특수 스크린을 표시할 때도 다음 레이블을 사용합니다.

  • main_menu_screen

  • load_screen

  • save_screen

  • preferences_screen

  • joystick_preferences_screen

레이블과 제어 흐름 함수

renpy.call_stack_depth()

현재 컨텍스트의 호출 스택 깊이를 반환합니다. 반환하거나 스택에서 꺼내지 않은 호출의 수입니다.

renpy.dynamic(*variables, **kwargs)

하나 이상의 변수 이름을 인수로 받습니다. 변수를 현재 호출의 동적 스코프로 만듭니다. 호출에서 돌아오면 이 함수를 호출한 시점의 값으로 복원합니다.

이름 있는 스토어의 변수도 지원합니다.

키워드 인수로 전달하면 해당 인수 값을 그 이름의 변수에 대입합니다.

호출 예는 다음과 같습니다.

$ renpy.dynamic("x", "y", "z")
$ renpy.dynamic("mystore.serial_number")
$ renpy.dynamic(players=2, score=0)
renpy.get_all_labels()

라이브러리 내부용 레이블을 포함하여 프로그램에 정의된 모든 레이블의 집합을 반환합니다.

renpy.get_return_stack()

현재 복귀 스택을 나타내는 문 이름 리스트를 반환합니다.

문 이름은 레이블이면 문자열, 그 밖의 문이면 불투명 튜플입니다.

renpy.has_label(name)

name이 프로그램의 유효한 레이블이면 참, 그렇지 않으면 거짓을 반환합니다.

name

존재를 확인할 레이블 이름 문자열이어야 합니다. 레이블이 아닌 문의 이름을 나타내는 불투명 튜플일 수도 있습니다.

renpy.mark_label_seen(label)

지정한 레이블이 현재 사용자의 시스템에서 이미 실행된 것으로 표시합니다.

renpy.mark_label_unseen(label)

지정한 레이블이 현재 사용자의 시스템에서 아직 실행되지 않은 것으로 표시합니다.

renpy.pop_call()

복귀하지 않고 현재 호출을 스택에서 꺼냅니다. Ren'Py return 문처럼 dynamic 변수의 값도 복원합니다.

호출한 레이블이 호출자에게 돌아가지 않기로 한 경우 사용할 수 있습니다.

renpy.seen_label(label)

현재 사용자의 시스템에서 지정한 레이블이 한 번 이상 실행되었으면 참, 아니면 거짓을 반환합니다. 장면 갤러리 잠금 해제 등에 사용할 수 있습니다.

renpy.set_return_stack(stack)

현재 복귀 스택을 설정합니다. 문 이름 리스트입니다.

문 이름은 레이블이면 문자열, 그 밖의 문이면 불투명 튜플일 수 있습니다.

가장 흔한 사용법은 다음과 같이

renpy.set_return_stack([])

복귀 스택을 비우는 것입니다.

컨텍스트

Ren'Py는 변경하고 저장할 수 있는 게임 상태를 관리하기 위해 내부적으로 컨텍스트를 사용합니다. 다음이 포함됩니다.

  • 현재 실행 중인 Ren'Py 문.

  • 위에서 설명한 호출 스택, renpy.dynamic()으로 만든 동적 변수의 이름과 이전 값.

  • 현재 표시 중인 이미지와 그 속성, 적용한 트랜스폼 등의 정보.

  • 표시 중인 스크린과 내부 변수.

  • 재생 중이거나 예약된 오디오.

대부분의 경우 컨텍스트가 하나뿐이므로 각 요소도 하나씩 존재합니다. 메인 메뉴나 게임 메뉴에 들어가면 달라집니다. 위 요소들을 모두 바꿀 수 있고 메뉴 컨텍스트에서 나오면 복원됩니다. 일부 변경은 자동으로 이루어집니다. 예를 들어 컨텍스트에 들어갈 때 screens 레이어를 비웁니다.

리플레이와 hiding the interface에서도 새 컨텍스트를 만듭니다.

스크린 언어가 도입된 뒤에는 컨텍스트를 만들 필요가 크게 줄었습니다.

롤백은 기본 컨텍스트, 즉 컨텍스트가 하나일 때만 활성화됩니다. 저장되는 것도 기본 컨텍스트뿐이며, 이것이 게임 메뉴에서 컨텍스트를 사용하는 이유입니다.

renpy.call_in_new_context(label, *args, **kwargs)

새 컨텍스트를 만들고 지정한 레이블부터 Ren'Py 스크립트를 실행합니다. 새 컨텍스트에서는 롤백을 비활성화하며 저장과 불러오기는 최상위 컨텍스트에서 이루어집니다.

상호작용 내부에서 사용자와 두 번째 상호작용을 시작할 때 사용합니다.

선택적인 키워드 인수를 받습니다.

_clear_layers

기본값인 True이면 새 상호작용 전에 레이어를 비웁니다. False이면 비우지 않고, 리스트이면 그 레이어들만 비웁니다.

renpy.context()

현재 컨텍스트에 고유한 객체를 반환합니다. 새 컨텍스트에 들어가면 복사되지만, 복사본을 바꿔도 원본은 바뀌지 않습니다.

이 객체는 저장되며 롤백에 참여합니다.

renpy.context_dynamic(*variables)

하나 이상의 변수 이름을 인수로 받습니다. 변수를 현재 컨텍스트의 동적 스코프로 만듭니다. 이전 컨텍스트로 돌아오면 함수 호출 당시의 값으로 복원합니다.

이름 있는 스토어의 변수도 지원합니다.

호출 예는 다음과 같습니다.

$ renpy.context_dynamic("x", "y", "z")
$ renpy.context_dynamic("mystore.serial_number")
renpy.context_nesting_level()

현재 컨텍스트의 중첩 수준을 반환합니다. 저장, 불러오기, 롤백의 대상인 가장 바깥쪽 컨텍스트는 0이며, 메뉴나 리플레이 등의 다른 컨텍스트는 0이 아닙니다.

renpy.invoke_in_new_context(callable, *args, **kwargs)

새 컨텍스트에서 지정한 Python 호출 가능 객체인 함수를 실행합니다. 반환하거나 예외를 발생시키면 원래 컨텍스트로 돌아갑니다. 보통 이벤트 처리기 안에서 확인 창처럼 플레이어에게 정보를 보여 주는 Python 함수를 호출할 때 사용합니다.

추가 위치 및 키워드 인수는 호출 가능 객체에 전달합니다.

이 컨텍스트는 Ren'Py 스크립트를 실행할 수 없습니다. renpy.jump()처럼 스크립트 흐름을 바꾸는 함수는 바깥 컨텍스트에서 처리합니다. Python 함수 대신 스크립트를 호출하려면 renpy.call_in_new_context()를 사용하세요.

선택적인 키워드 인수를 받습니다.

_clear_layers

기본값인 True이면 새 상호작용 전에 레이어를 비웁니다. False이면 비우지 않고, 리스트이면 그 레이어들만 비웁니다.

renpy.jump_out_of_context(label)

현재 컨텍스트를 나와 부모 컨텍스트에서 지정한 레이블로 제어를 넘깁니다.

renpy.reset_all_contexts()

모든 컨텍스트를 스택에서 꺼내며 동적 변수를 복원합니다. 이후 새 컨텍스트를 만들고 현재 문을 끝낸 뒤 다음 문부터 계속합니다. 데이터와 시작 지점을 제외하고 Ren'Py를 시작 당시 상태로 돌립니다.

표시 중인 이미지나 음악 등 게임의 모든 요소를 처음 시작한 것처럼 초기화하는 데 사용할 수 있습니다.

Ren'Py를 전면적으로 초기화하므로 현재 문을 즉시 끝냅니다.

주로 after_load 레이블에서 게임을 시작 당시 상태로 복원하는 용도입니다. 이후 게임에서 장면과 음악 등을 다시 설정하고 원하는 레이블로 이동해 계속 진행해야 합니다.