Оптимизация языка экранов
Ren'Py использует ряд техник для оптимизации скорости языка экранов. При использовании Ren'Py для создания сложных интерфейсов, например, для симуляторов, полезно понимать, как работает язык экранов, чтобы достичь максимальной производительности.
Это руководство применимо ко второй реализации языка экранов, которая была добавлена в Ren'Py 6.18. Если ваша игра была создана в Ren'Py 6.17 или ранее, может потребоваться выбрать опцию "Force Recompile" (Принудительная перекомпиляция) в лаунчере, чтобы обеспечить обновление её экранов до последней версии.
Это руководство не заменяет хорошие практики программирования. Если экран использует вложенные циклы для выполнения большого количества непродуктивной работы, он будет медленнее, чем экран, который избегает таких циклов. Хотя понимание техник из этого руководства важно, полное избегание лишней работы всегда лучше, чем позволять Ren'Py оптимизировать работу за вас.
Список параметров
Для лучшей производительности все экраны должны быть определены со списком параметров. Когда экран не принимает параметров, он должен быть определен с пустым списком параметров. Этот экран:
screen test():
vbox:
for i in range(10):
text "[i]"
быстрее, чем этот:
screen test:
vbox:
for i in range(10):
text "[i]"
Когда экран определяется без списка параметров, любое имя, используемое в этом экране, может быть переопределено при его показе. Это требует от Ren'Py быть более консервативным при анализе экрана, что может ограничить выполняемую оптимизацию.
Предсказание
Экраны работают лучше, когда они предсказаны заранее. Это потому, что Ren'Py выполнит код экрана во время предсказания и загрузит изображения, используемые экраном.
Существует два способа, которыми Ren'Py автоматически предсказывает экраны:
Ren'Py будет предсказывать экраны, показанные операторами
show screenиcall screen.Ren'Py будет предсказывать экраны, которые будут показаны действиями
Show()иShowMenu().
Если экраны показываются из Python, хорошей практикой является начать
предсказание экрана до того, как он будет показан. Чтобы начать
предсказание экрана, используйте функцию
renpy.start_predict_screen(). Чтобы остановить предсказание
экрана, используйте функцию renpy.stop_predict_screen().
Повторное использование отображаемых объектов
При вычислении оператора языка экранов, который создает отображаемый объект (displayable), Ren'Py проверяет, равны ли позиционные аргументы и свойства, переданные этому объекту, позиционным аргументам и свойствам, переданным при последнем вычислении этого оператора. Если они равны, вместо создания нового отображаемого объекта, Ren'Py повторно использует существующий.
Повторное использование отображаемых объектов имеет ряд преимуществ для производительности. Это экономит затраты на создание нового объекта, что может быть значительно для объектов с большим количеством внутренних состояний. Что еще важнее, повторное использование отображаемого объекта означает, что во многих случаях Ren'Py не нужно будет повторно отрисовывать объект перед его показом пользователю, что может привести к еще одному значительному ускорению.
Для сравнения позиционных аргументов и свойств Ren'Py использует понятие равенства, воплощенное оператором == в Python. Мы расширили это понятие равенства на действия, решив, что два действия должны быть равны, когда они неотличимы друг от друга — когда не имеет значения, какое действие вызывается или какое действие опрашивается для определения чувствительности (sensitivity) или выбранности (selectedness).
Все действия, поставляемые с Ren'Py, соответствуют этому определению.
При определении собственных действий имеет смысл обеспечить их этим
понятием равенства. Это можно сделать, предоставив соответствующий
метод __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) соответствующим образом.
Константные выражения и чистые функции
Ren'Py может использовать свойства константных переменных и чистых функций для повышения скорости вычисления экранов и полного исключения вычисления некоторых их частей.
Определения
Выражение является const (сокращение от constant — константа), если оно всегда представляет одно и то же значение при вычислении. Для целей Ren'Py выражение является константным тогда и только тогда, когда следующие выражения всегда вычисляются в одно и то же константное значение или не определены:
Применение любого унарного, бинарного или тернарного оператора к выражению, при условии, что другие операнды также являются константными.
Доступ к полю выражения.
Индексирование выражения, будь то с помощью числа или объекта.
Числа и строки Python являются константными, так же как и литералы
list, tuple, set и dict, у которых все компоненты константны. Ren'Py
помечает переменные, определенные с помощью оператора define, как
константные. Функции renpy.const() и renpy.not_const()
могут быть использованы для дальнейшего контроля того, что Ren'Py
считает константным. Список константных имен по умолчанию приведен
в разделе Const Names ниже.
Если у вас есть переменная, которая никогда не будет меняться, имеет
смысл использовать define как для ее определения, так и для
объявления ее константной. Например:
define GRID_WIDTH = 20
define GRID_HEIGHT = 10
Вызываемая функция, класс или действие являются чистыми (pure), если при передаче константных значений в качестве всех своих аргументов они всегда возвращают одно и то же константное значение. Альтернативно, выражение, вызывающее чистую функцию с константными аргументами, также является константным выражением.
Большое количество стандартных функций, классов и действий помечены как чистые. Эти функции перечислены в разделе Pure Names ниже.
Функции объявляются чистыми с помощью функции renpy.pure(),
которую можно использовать как декоратор для функций, объявленных
в хранилище по умолчанию (default store).
Константные выражения и чистые функции не обязаны сохранять одно и то же значение после следующих событий:
Конец фазы init.
Смена языка.
Перестройка стилей.
Как константы оптимизируют язык экранов
Существует три преимущества в обеспечении константности аргументов и свойств языка экранов.
Во-первых, константные аргументы и свойства вычисляются при подготовке экранов, то есть в конце фазы init, при смене языка или при перестройке стилей. После этого больше не нужно тратить время на вычисление константных аргументов и свойств.
Во-вторых, константы хорошо сочетаются с повторным использованием отображаемых объектов. Когда все аргументы и свойства отображаемого объекта константны, его всегда можно повторно использовать, что дает все преимущества такого подхода.
Наконец, когда Ren'Py встречает дерево отображаемых объектов, у которого все аргументы, свойства и выражения, влияющие на поток управления, также константны, Ren'Py повторно использует все дерево, не вычисляя выражений и не создавая отображаемых объектов. Это может дать значительный прирост производительности.
Например, следующий экран не выполняет никакого Python-кода и не создает никаких отображаемых объектов после первого его предсказания или показа:
screen mood_picker():
hbox:
xalign 1.0
yalign 0.0
textbutton "Happy" action SetVariable("mood", "happy")
textbutton "Sad" action SetVariable("mood", "sad")
textbutton "Angry" 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]")
Если переменная, содержащая текст, содержит подстановку, необходимо
использовать флаг преобразования !i:
$ who = "Jane"
$ t = "Hello, [who]!"
text 'Then I told her, "[t!i]"'
Константные функции
- renpy.const(name)
Объявляет переменную в хранилище (store) константой.
Переменная является константой, если ничто не может изменить ее значение или любое значение, доступное через индексацию или обращение к ее атрибутам. Переменные должны оставаться константными вне блоков define, init и translate python.
- name
Строка, содержащая имя переменной, которую нужно объявить константой.
- renpy.not_const(name)
Объявляет имя в хранилище (store) неконстантным.
Отменяет эффект вызовов
renpy.const()иrenpy.pure().- name
Имя, которое нужно объявить неконстантным.
- renpy.pure(fn)
Объявляет функцию чистой (pure). Чистая функция должна всегда возвращать одно и то же значение при вызове с одними и теми же аргументами вне блоков define, init и translate python.
- fn
Имя функции, которую нужно объявить чистой. Это может быть либо строка с именем функции, либо сама функция. Если передается строка, и функция находится внутри модуля, эта строка должна содержать имя модуля с точкой.
Возвращает fn, что позволяет использовать эту функцию как декоратор.
Профилирование
Ren'Py поддерживает профилирование выполнения экранов с помощью функции
renpy.profile_screen:
- class renpy.profile_screen(name, predict=False, show=False, update=False, request=False, time=False, debug=False, const=False)
Запрашивает профилирование экрана с именем name, которое должно быть строкой.
За исключением name, все аргументы должны передаваться как именованные. Эта функция принимает три группы аргументов.
Первая группа аргументов определяет, когда происходит профилирование.
- predict
Если true, профилирование происходит, когда экран предсказывается.
- show
Если true, профилирование происходит, когда экран показывается впервые.
- update
Если true, профилирование происходит, когда экран обновляется.
- request
Если true, профилирование происходит по запросу при нажатии F8.
Вторая группа аргументов управляет тем, какой вывод профилирования создается при профилировании.
- time
Если true, Ren'Py запишет в лог количество времени, затраченное на вычисление экрана.
- debug
Если true, Ren'Py запишет в лог информацию о том, как вычисляются экраны, включая:
Какие displayable Ren'Py считает константными.
Какие аргументы, если таковые имеются, требовали вычисления.
Какие displayable были повторно использованы.
Создание и сохранение этой отладочной информации занимает заметное количество времени, поэтому вывод time не следует считать надежным, если установлен debug.
Последняя группа аргументов управляет тем, какой вывод создается один раз за запуск Ren'Py.
- const
Отображает переменные на экране, которые помечены как const и не-const.
Весь вывод профилирования будет записан в файл profile_screen.txt в директории игры.
Константные имена
Следующие имена по умолчанию являются константными.
False
None
True
config
Чистые имена
Следующие имена по умолчанию являются и чистыми, и константными.
ADVCharacter
ADVSpeaker
Alpha
Animation
DynamicCharacter
FactorZoom
ImageReference
LiveComposite
LiveCrop
LiveTile
Motion
Move
MoveFactory
MoveIn
MoveOut
NVLCharacter
OldMoveTransition
Pan
Particles
Position
Revolve
RevolveInOut
RotoZoom
SizeZoom
Speaker
SubTransition
Viewport
Window
Zoom
ZoomInOut
_CharacterVolumeValue
_DisplayReset
_InputValueAction
_TouchKeyboardBackspace
_TouchKeyboardReturn
_TouchKeyboardTextInput
__renpy__dict__
__renpy__list__
__renpy__set__
_m1_00gallery__GalleryAction
_m1_00gallery__GalleryToggleSlideshow
_m1_00musicroom__MusicRoomPlay
_m1_00musicroom__MusicRoomRandomPlay
_m1_00musicroom__MusicRoomStop
_m1_00musicroom__MusicRoomTogglePause
_m1_00musicroom__MusicRoomTogglePlay
_m1_00preferences__DisplayAction
_m1_00preferences__ResetPreferences
_movebottom
_moveleft
_moveright
_movetop
_notify_transform
_touch_keyboard
abs
all
any
ascii
bin
bool
bytes
callable
chr
color
complex
dict
dir
dissolve
divmod
enumerate
filter
float
format
frozenset
getattr
hasattr
hash
hex
int
irisout
isinstance
issubclass
len
list
map
max
min
oct
ord
pow
pushdown
pushleft
pushup
range
renpy.Keymap
renpy.ParameterizedText
renpy.curried_call_in_new_context
renpy.curried_invoke_in_new_context
renpy.curry
renpy.easy_displayable
renpy.has_screen
renpy.image_exists
renpy.munged_filename
renpy.partial
renpy.unelide_filename
repr
reversed
round
set
slice
slideawaydown
slideawayright
slideawayup
slidedown
slideright
slideup
sorted
str
sum
tuple
type
ui.callsinnewcontext
ui.gamemenus
ui.invokesinnewcontext
ui.jumps
ui.jumpsoutofcontext
ui.returns
wipedown
wiperight
wipeup
zip