본문 바로가기

렌파이/개발 편의

나만의 렌파이 명령문 만들기 - 사용자 정의 명령문

렌파이에서는 이미 정의된 명령문 외에 사용자가 임의로 명령문을 만들어 사용할 수 있습니다.

다음은 아날로그: 증오록 스크립트에서 사용된 사용자 정의 명령문입니다(안에 적힌 영문은 게임에 사용된 한국어로 바꿨습니다).



렌파이를 어느 정도 사용해왔다면 알겠지만 위에서 사용된 message 라는 명령문은 기존의 렌파이에서 볼 수 없었던 명령문입니다. 제작자가 자신이 만든 서신 시스템을 편하게 사용하기 위해 새로 만들어낸 명령문이기 때문입니다.


분석

위에서 사용한 스크립트가 게임에서는 스크린 언어를 통해 이렇게 표시되는 것이지요. 우선은 이 게임에 사용된 스크립트를 해석하여 사용자 정의 명령문을 어떻게 만드는 것인지 알아보도록 하겠습니다.

제가 새 명령문을 만든 전체 스크립트를 가져오긴 했습니다만 옆에 스크립트 에디터를 켜 두시고 이 글에 적힌 설명과 전체 스크립트를 번갈아가며 확인하는 것이 이해하기에 훨씬 편할 겁니다.



python early:

python 블럭 명령문에 early를 붙이면 렌파이가 해당 블럭을 제일 먼저 읽습니다. 명령문을 새로 등록하는 것이니 먼저 읽어들이도록 지정할 필요가 있겠죠. 참고로 렌파이가 블럭을 읽는 순서는 다음과 같습니다.

python early > init -숫자 > init 숫자 -> label splashscreen: > label start:



renpy.statements.register("message", parse=parse_message, execute=execute_message)

사용자 정의 명령문을 등록하는 renpy.statements.register()함수입니다.


"message"
명령문을 message 로 지정했습니다.

parse=parse_message
message 라는 명령문을 사용한 줄을 해석하기 위해 사용되는 함수의 이름을 적은 것입니다. 위 스크립트에서는 message 명령문이 적힌 줄을 해석하는 데에 parse_message 함수를 사용합니다.

execute=execute_message
message 명령문이 작동하는 방식을 설정해둔 함수의 이름을 적은 것입니다. 위 스크립트에서는 message 명령문을 실행하기 위해 execute_message 함수를 사용합니다.



def parse_message(lex):

parse_message()라는 함수를 정의한 줄입니다. renpy.statements.register() 에서 사용될 줄을 해석하는 방식이 적힌 함수입니다.
괄호 안에 적힌 lex는 message 명령문이 적힌 줄에서 명령문을 제외한 나머지 내용을 가리킵니다.


그러니 여기서라면 "2-1288" 부터 "....만개하기를 꿈꾸노니." 까지를 lex 로 받는 것이죠.

아래는 함수 블럭 안에 적힌 내용입니다. 이 내용은 제작자가 임의로 명령문 해석 방법을 설정하기 위해 입력한 내용입니다.


id = lex.string()

message 다음에 처음 적인 렌파이 문자열을 id에 배정합니다. 예제에서 보면 "2-1288"이 id 가 됩니다. (게임에서는 이 id 를 입력하여 탐색을 통해서가 아니라 공개되지 않은 서신을 곧바로 확인할 수 있고 이렇게 확인한 서신이 엔딩에 영향을 미치기도 합니다. 아무튼 실제로 이용되는 정보라서 만들어뒀나보네요.)


.string() 은 명령문이 적힌 줄에 등장하는 문자열을 의미합니다. 위에서 함수를 만들 때 명령문 줄을 lex 라는 이름으로 정했으니 lex.string()이 되는 거죠.


아무튼 예제스크립트에서는 "2-1288"이 id로 배정됩니다.


title = lex.string()

id 다음에 적힌 렌파이 문자열을 title에 배정합니다. 예제 스크립트에서는 "아름다운 꽃"이 title.


creator = lex.string()

title 다음에 적힌 렌파이 문자열을 creator 에 배정합니다. 예제 스크립트에서는 "하나"가 creator


date = lex.string()

title 다음에 적힌 렌파이 문자열을 date 에 배정합니다. 예제 스크립트에서는 "321년 12월 5일"이 date


pages = [ ]

서신 내용을 저장하기 위한 리스트입니다.


while not lex.eol():
    pages.append(lex.string())


예제 스크립트에서는 문장이 하나만 적혀있지만 게임을 하다보면 페이지가 여럿인 서신이 있습니다. 해당 줄이 끝날 때까지 나타나는 문자열들을 pages 라는 리스트에 입력한다는 의미입니다. 위에 적은 예제 스크립트에서는 "아름다운 꽃이여, ~만개하기를 꿈꾸노니." 라는 문자열 하나만 pages 에 저장하지만 만약 날짜 뒤에 문자열이 여럿 적혀 있다면 줄이 끝날 때까지 적힌 문자열을 모두 pages에 저장합니다.



위 같은 스크립트라면 "은미 언니께, ..인형을 가져와서" "저에게 주셨습니다! ...사랑을 담아" 이렇게 두 개의 문자열이 pages 에 저장됩니다. 


return id, title, creator, date, pages
parse_message 함수는 이렇게 각각 저장한 문자열들을 모두 반환합니다. 이 함수가 반환한 값은 renpy.statements.register()에서 execute 에 적은 함수, 이 예제에서는 execute_message 함수에서 사용하게 됩니다.



def execute_message(o):

명령문을 실행할 때 활용할 함수를 정의한 것입니다. 여기에서 o는 parse_message 에서 반환한 값 덩어리, id, title, creator, date, pages 묶음을 뜻합니다.

id, title, creator, date, pages = o

그래서 다음줄에서 바로 이 덩어리에 담긴 값을 하나하나 풀어주었죠.


store.blocks[int(id.split("-")[0])-1].contents.append(Message(id, title, creator, date, pages))


store.blocks는 script.rpy 에서 정의된 리스트입니다. 위의 명령문은 id를 통해 리스트의 인덱스를 밝혀내서 해당하는 블록에 컨텐츠를 추가한다는 의미입니다.


$ store.blocks = [ Block("Block 1"), Block("Block 2"), Block("Block 3"), Block("Block 4"), Block("Block 5"), Block("Block 6"), Block("Block 7"), Block("Block 8"), Block("Block 9"), Block("Block 10"), Block("Helpful notes", True), Block("*Mute's questions") ]


예를 들어 아까 "아름다운 꽃"이라는 서신의 id가 "2-1288"이었으니 int(id.split("-")[0])-1 를 풀어보면 store.blocks[1], 즉 Block("Block 2")를 의미하는 것입니다.


Block과 Message 는 클래스이니 이 execute_message함수는 "아름다운 꽃" 명령문을 해석한 내용을 받아 store.blocks[1] 즉, Block("Block 2")객체의 contents 리스트에 Message("2-1288", "아름다운 꽃", "하나", "321년 12월 5일", "아름다운 꽃이여, ...만개하기를 꿈꾸노니.") 라는 객체를 추가하게 됩니다.



다시 차근차근 설명하자면..



1. renpy.statements.register("message", parse=parse_message, execute=execute_message) 라고 새로운 명령문 message 을 등록합니다.


2. 스크립트에서 위와 같은 message 명령문을 발견합니다.


3. parse_message 함수가 이 줄을 해석하여

id는 "2-1288", title은 "아름다운 꽃", creator는 "하나", date는 "321년 12월 5일", pages 리스트에는 "아름다운 꽃이여, ... 만개하기를 꿈꾸노니." 라는 내용을 배정하고 이를 통채로 반환합니다.


4. parse_message 함수가 반환한 값들을 execute_message 함수가 넘겨받아 해석하여 store.blocks의 n-1 번째에 적힌 Block 객체.content 리스트에 Message("2-1288", "아름다운 꽃", "하나", "321년 12월 5일", "아름다운 꽃이여, ...만개하기를 꿈꾸노니.") 객체를 추가합니다.


오류 검출

renpy.lint.check_text_tags (s):

s에 있는 텍스트 태그의 정확성을 확인합니다. 오류가 없으면 None을, 오류가 있다면 오류 문자열을 반환합니다.


아날로그 증오록에서는 사용되지 않았지만 위키에 나와있는 예제 스크립트를 통해 쉽게 해석할 수 있습니다.



참고

렌파이 위키 영문 - User-Defines Statements

렌파이 위키 번역 - 사용자 정의 명령문 (번역 상태가 좋지 못합니다;)

(이 내용은 새 매뉴얼에 아직 추가되지 않았습니다.)