렌파이는 스크린 언어의 속도를 최적화하기 위해 다양한 기법을 사용합니다. 렌파이로 시뮬레이션 게임에서 사용하는 것과 같은 복잡한 인터페이스를 만들고자 할 때, 스크린 언어가 작동하는 방식을 이해한다면 최고의 성능을 이끌어낼 수 있을 것입니다.
이 문서는 렌파이 6.18 버전에 추가됐던 두 번째로 구현된 스크린 언어에 적합합니다. 렌파이 6.17 혹은 그 이전 버전으로 만들어진 게임이라면 "강제로 재컴파일" 하기 옵션을 선택해 스크린이 최신 버전으로 업그레이드되도록 해야할 것입니다.
이 문서는 프로그래밍 실습용으로 적합한 문서가 아닙니다. 스크린이 비생산적인 작동을 계속하는 중첩 반복문을 사용한다면, 이런 반복이 발생하지 않는 스크린보다 작동이 느릴 것입니다. 이 문서에 있는 테크닉을 이해하는 것이 중요하기는 하나,
성능을 최고조로 발휘하기 위해서는 모든 스크린이 매개변수와 함께 정의되어야 합니다. 스크린이 매개변수를 받지 않는다면, 빈 매개변수 목록과 함께 정의될 것입니다. 아래의 스크린은:
screen test():
vbox:
for i in range(10):
text "[i]"
이 아래의 스크린보다 빠릅니다.:
screen test:
vbox:
for i in range(10):
text "[i]"
어떤 스크린이 매개변수 목록 없이 정의되었을 때, 그 스크린 안에서 사용된 모든 이름은 스크린이 나타날 때 재정의될 수 있습니다. 이는 렌파이가 스크린을 분석할 때 조심스럽게 작동하므로, 성능 최적화를 제한할 수도 있습니다.
스크린은 미리 예측된다면 더 나은 성능을 발휘할 수 있습니다. 그 이유는 렌파이가 예측 과정에서 스크린을 실행하고, 해당 스크린에서 사용하는 이미지를 불러오기 때문입니다.
렌파이가 자동으로 스크린을 예측하는 방법은 두 가지가 있습니다.:
show screen
과 call screen
문으로 나타나는 스크린을
예측합니다.Show()
와 ShowMenu()
액션으로 나타나는 스크린을
예측합니다.스크린이 파이썬 코드에서 나타난다면, 스크린이 나타나기 전에
예측을 시작하는 것이 좋은 생각입니다. 스크린 예측을 시작하려면
renpy.start_predict_screen()
함수를 사용하세요. 예측을 중단하려면
renpy.stop_predict_screen()
함수를 사용하세요.
디스플레이어블을 생산하는 스크린 언어 명령문을 계산할 때 렌파이는 디스플레이어블이 받은 위치 인수 및 속성이 가장 최근에 명령문을 계산했을 때 만들어진 디스플레이어블과 동일한 위치 인수 및 속성을 받는지 확인합니다. 동일하다면 렌파이가 새로운 디스플레이어블을 만드는 대신에 기존의 디스플레이어블을 재사용할 것입니다.
디스플레이어블 재사용은 성능에 지대한 영향을 미칩니다. 새로운 디스플레이어블을 생성하는 것을 방지해 수많은 내부 상태를 포함한 디스플레이어블에 중요한 영향을 미칠 것입니다. 더불어, 디스플레이어블을 재사용하는 것이 대부분의 경우에 렌파이가 사용자에게 디스플레이어블을 표시하기 전에 다시 렌더할 필요가 없다는 뜻하는데, 이로 인해 속도가 눈에 띄게 빨라질 수 있습니다.
위치 인수와 속성을 비교하는 것으로 렌파이는 파이썬의 == 연산자로 구체화된 동등 개념을 사용합니다. 이러한 동등 개념은 두 가지 액션이 서로 구별되지 않을 때 (어느 액션이 실행되더라도 상관이 없는 때라든가, 디스플레이어블의 작동 가능 상태나 선택 상태를 결정하기 위한 질문형 액션이 있을 때 등) 두 액션을 동일한 액션으로 결정하는 방법으로 액션에까지 확장시켰습니다.
렌파이에서 제공하는 모든 액션은 이 정의에 따릅니다. 자신만의 고유한 액션을 정의할 때 이 동등 개념을 추가하는 것이 타당할 것입니다. 액션에 적절한 __eq__ 메소드를 주면 동등 개념을 추가할 수 있습니다. 아래는 예제입니다.
class TargetShip(Action):
def __init__(self, ship):
self.ship = ship
def __eq__(self, other):
if not isinstance(other, TargetShip):
return False
return self.ship is other.ship
def __call__(self):
global target
target = self.ship
__eq__ 메소드는 조심해서 정의해야 합니다. 이 메소드는 모든 필드를 비교해야 하며 동등(==)과 동일(is) 비교를 적절하게 사용해야 합니다.
렌파이는 불변 변수와 순수 함수의 속성을 활용해 스크린 계산 속도를 개선하며 스크린의 일부분은 영원히 계산하지 않습니다.
계산할 때마다 같은 값을 표현하는 표현식은 불변 입니다. 렌파이의 의도에 따라 아래에 나열된 표현식이 정의되지 않거나, 혹은 항상 같은 불변 변수를 계산해낸다면 표현식은 불변입니다. :
파이썬 숫자와 문자열은 불변이며, 마찬가지로 리스트, 튜플, 셋, 딕셔너리,
상수 등 모든 요소은 불변입니다. 렌파이는 define
문으로
정의한 변수도 불변으로 봅니다. renpy.const()
와
renpy.not_const()
함수는 렌파이에서 무엇을 불변으로 여길지
정할 때 사용할 수 있습니다. 기본적인 불변 이름의 목록은
불변 이름 항목을
참고하세요.
변하지 않는 변수는 define
으로 정의하고 불변이라는 것을 선언해주는
것이 적절할 것입니다. 다음은 예제입니다.
define GRID_WIDTH = 20
define GRID_HEIGHT = 10
모든 인수가 불변 값인 콜러블 함수, 클래스, 액션은 순수 입니다. 아니면 불변 표현식을 통해 순수 함수를 실행시키는 표현식 또한 불변 표현식일 것입니다.
대부분의 기본 함수, 클래스 및 액션은 순수인 것으로 간주합니다. 이 함수는 밑에 있는 순수 이름 항목에 나열되어 있습니다.
renpy.pure()
함수를 사용해하면 함수를
순수로 선언할 수 있습니다. 이 함수는 기본 보관 영역 내에서 선언된 함수들을
가리키는 데코레이터로 사용될 수 있습니다.
불변 표현식과 순수 함수는 아래와 같은 이벤트에서는 같은 값을 계속 유지할 필요가 없습니다.:
스크린 언어 인수와 속성이 불변으로 만들면 3가지 장점이 있습니다.
첫 번째는 스크린을 준비할 때(초기화 단계의 마지막에), 언어가 변경될 때, 또는 스타일이 재작성될 때 불변 인수와 속성이 계산된다는 점입니다. 그 이후에는 불변 인수와 속성을 계산하기 위해 시간을 낭비할 필요가 없다는 의미입니다.
두 번째는 불변이 디스플레이어블 재사용과 잘 어울려 작동한다는 점입니다. 디스플레이어블의 모든 인수와 속성이 불변이라면, 디스플레이어블은 언제나 재사용할 수 있습니다. 즉, 디스플레이어블 재사용의 이점을 모두 활용할 수 있게 됩니다.
마지막으로 제어 흐름에 영향을 미치는 모든 인수, 속성, 표현식이 불변인 디스플레이어블 트리 구조를 렌파이가 접했을 때, 렌파이는 모든 디스플레이어블을 생성하거나 표현식을 계산하지 않고 전체 트리 구조를 재사용할 것입니다. 이로 인해 성능이 현저히 향상될 것입니다.
예를 들어, 다음과 같은 스크린은 처음으로 예측하거나 화면에 등장한 뒤로는 어떤 코드도 실행하지 않고 어떤 디스플레이어블도 생성하지 않을 것입니다.:
screen mood_picker():
hbox:
xalign 1.0
yalign 0.0
textbutton "기쁨" action SetVariable("mood", "happy")
textbutton "슬픔" action SetVariable("mood", "sad")
textbutton "분노" action SetVariable("mood", "angry")
텍스트를 정의할 때 다음처럼 새로운 스타일의 텍스트 대입 기호가 포함된 문자열은 불변입니다.
$ t = "Hello, world."
text "[t]"
텍스트가 배정된 변수를 직접 설정하는 것은 불변이 아닙니다.
$ t = "Hello, world."
text t
퍼센트 대입 기호를 사용하는 것도 불변이 아닙니다.:
$ t = "Hello, world."
text "%s" % t
마지막으로 텍스트 번역 함수 _() 는 순수이며, 그 안에 문자열이 포함되어 있다면, 전체 표현식은 불변입니다.
text _("Your score is: [score]")
renpy.
const
(name)¶스토어에 있는 변수를 불변으로 선언한다.
불변 변수는 무엇도 그 변수의 값을 바꿀 수 없거나, 어떤 값도 변수의 값을 인덱싱하거나 값의 속성에 접근하지 않는 변수를 가리킨다. 변수는 define, init, translate python 블럭 외부에서도 불변이어야 한다.
renpy.
not_const
(name)¶스토어에 있는 이름을 비불변으로 선언하다.
renpy.const()
와 renpy.pure()
을 호출한
효과를 원상복구한다.
renpy.
pure
(fn)¶fn 함수를 순수로 선언한다. 순수 함수는 동일한 인수와 함께 호출되었을 때, define, init, translate python 블럭 외부에서도 항상 같은 값을 반환해야 한다.
이 함수를 장식자로 사용할 수 있도록 하는 fn 을 반환한다.
렌파이는 스크린 실행에 대해 Ren``renpy.profile_screen`` 함수로 정보 수집을 진행합니다 :
renpy.
profile_screen
(name, predict=False, show=False, update=False, request=False, time=False, debug=False, const=False)¶name 이라는 스크린의 정보 수집을 요청합니다. 이 때 `name`은 반드시 문자열이어야 합니다.
`name`외에 모든 인수는 키워드 인수로서 입력 받아야 한다. 이 함수는 세 가지 인수 그룹을 받는다.
첫 번째 인수 그룹은 정보 수집이 발생하는 시기를 결정한다.
두 번째 인수 그룹은 정보 수집이 시작될 때 정보 수집 결과물을 무엇으로 출력할 것인지 결정한다.
이 값이 참이면 렌파이는 아래 사항을 포함해 스크린이 계산된 방법을 기록한다. :
이런 디버그 정보를 만들어내고 저장하는 데에는 상당한 시간이 소요되므로, debug 값을 설정한 경우에 time 정보는 신뢰도가 낮을 것입니다.
마지막 인수 그룹은 렌파이가 실행할 때마다 생산할 출력물이다.
정보 분석 과정에서 기록된 모든 출력물은 game 폴더에 있는 profile_screen.txt 에 기록된다.
아래와 같은 이름들은 기본적으로 변하지 않고 순수합니다.
AddToSet()
AlphaBlend()
AlphaDissolve()
AlphaMask()
AnimatedValue()
At()
Character()
Color
ComposeTransition()
ConditionSwitch()
CropMove()
DictValue()
Dissolve()
Drag
DynamicDisplayable()
EndReplay()
Fade()
FieldValue()
FileDelete()
FilePage()
FileTakeScreenshot()
Fixed()
Flatten()
FontGroup()
Frame()
Grid()
HBox()
Help()
Hide()
HideInterface()
If()
Image()
ImageDissolve()
InvertSelected()
Jump()
Language()
LiveComposite()
LiveCrop()
LiveTile()
MainMenu()
MixerValue()
MouseMove()
MoveTransition()
Movie()
MultipleTransition()
Notify()
Null()
NullAction()
OpenURL()
ParameterizedText()
Pause()
Pixellate()
Preference()
Queue()
QuickLoad()
QuickSave()
Quit()
RemoveFromSet()
Replay()
RestartStatement()
Return()
RollForward()
Rollback()
Screenshot()
SelectedIf()
SensitiveIf()
SetCharacterVolume()
SetDict()
SetField()
SetMixer()
SetMute()
SetScreenVariable()
SetVariable()
SetVoiceMute()
Show()
ShowMenu()
ShowTransient()
ShowingSwitch()
Skip()
SnowBlossom()
Solid()
Start()
StaticValue()
StylePreference()
Text()
ToggleDict()
ToggleField()
ToggleMute()
ToggleScreenVariable()
ToggleSetMembership()
ToggleVariable()
ToggleVoiceMute()
Transform
VBox()
VariableValue()
VoiceReplay()
_()
blinds
dissolve
fade
hpunch
irisin
name_only
narrator
pixellate
renpy.ParameterizedText()
renpy.check_text_tags()
renpy.exists()
renpy.fsdecode()
renpy.fsencode()
renpy.get_all_labels()
renpy.has_label()
renpy.image_size()
renpy.known_languages()
renpy.list_files()
renpy.loadable()
renpy.variant()
renpy.version()
slideawayleft
slideleft
squares
vpunch
wipeleft
zoomin
zoominout
zoomout