Оптимизация языка экрана (Screen Language Optimization)

Материал из Ren'Py Wiki
Перейти к навигации Перейти к поиску

Ren'Py использует ряд методов для оптимизации скорости языка экрана. При использовании Ren'Py для создания сложных интерфейсов, таких как те, которые используются в симуляторах, это может помочь понять, как работает язык экрана, чтобы вы могли достичь максимальной производительности.

Это руководство применимо ко второй реализации языка экрана, которая была добавлена в Ren'Py 6.18. Если ваша игра была создана в Ren'Py 6.17 или более ранней версии, может потребоваться выбрать опцию «Перекомпилировать проект» в Ren'Py Launcher, чтобы обеспечить обновление её экранов до последней версии.

Это руководство не является заменой практики программирования. Если экран использует вложенные циклы для выполнения большого количества непродуктивной работы, он будет медленнее, чем экран, избегающий таких циклов. Хотя понимание методов, описанных в этом руководстве, важно, полностью избегать работы всегда лучше, чем позволить Ren'Py оптимизировать работу для вас.

Список параметров (Parameter List)[править | править код]

Для лучшей производительности все экраны должны быть определены со списком параметров. Когда экран не принимает параметры, он должен быть определен с пустым списком параметров. Экран:

screen test():
    vbox:
        for i in range(10):
            text "[i]"

быстрее, чем:

screen test:
    vbox:
        for i in range(10):
            text "[i]"

Если экран определен без списка параметров, любое имя, используемое на этом экране, может быть переопределено при отображении экрана. Это требует, чтобы Ren'Py был более консервативен при анализе экрана, что может ограничить выполняемую им оптимизацию.

Прогнозирование (Prediction)[править | править код]

Экраны работают лучше, когда они спрогнозированы заранее. Это связано с тем, что Ren'Py будет запускать экран во время прогнозирования и загружать изображения, используемые экраном.

Ren'Py может автоматически прогнозировать экраны двумя способами:

  • Ren'Py будет прогнозировать экраны, отображенные операторами show screen и call screen.
  • Ren'Py будет прогнозировать экраны, которые будут показаны действиями Show() и ShowMenu().


Если экраны отображаются из Python, рекомендуется начать прогнозирование экрана до его отображения. Чтобы начать прогнозирование экрана, используйте функцию renpy.start_predict_screen(). Чтобы прекратить прогнозирование, используйте функцию renpy.stop_predict_screen().

Многократное использование отображаемого объекта (Displayable Reuse)[править | править код]

При оценке оператора языка экрана, который создаёт отображаемый объект, Ren'Py проверит не совпадают ли позиционные аргументы и свойства, заданные для этого отображаемого объекта, с позиционными аргументами и свойствами, заданным при последнем вычислении этого оператора. Если они есть, вместо того, чтобы создать новый отображаемый объект, Ren'Py будет повторно использовать существующий отображаемый объект.

Многократное использование отображаемого объекта имеет ряд последствий для производительности. Это экономит затраты на создание нового отображаемого элемента, что может быть важно для отображаемых объектов, в которых содержится много информации о внутреннем состоянии. Что еще более важно, повторное использование отображаемого объекта означает, что во многих случаях Ren'Py не нужно будет повторно отображать отображаемый объект, прежде чем показывать его пользователю, что может привести к ещё одному значительному ускорению.

Чтобы сравнить позиционные аргументы и свойства, Ren'Py использует понятие равенства, воплощенное оператором Python ==. Мы расширили это понятие равенства до действий, решив, что два действия должны быть равны, когда они неотличимы друг от друга - когда не имеет значения, какое действие вызывается или какое действие запрашивается для определения чувствительности или выбранности.

Все действия, предусмотренные 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).

Постоянные выражения и чистые функции (Const Expressions and Pure Functions)[править | править код]

Ren'Py может использовать свойства постоянных переменных и чистых функций, чтобы повысить скорость вычисления экрана и полностью избежать оценки некоторых частей экранов.


◇ Определения[править | править код]

Выражение является постоянным, если оно всегда представляет одно и то же значение при вычислении. Для целей Ren'Py выражение является постоянным тогда и только тогда, когда следующие выражения всегда вычисляются с одним и тем же постоянным значением или не определены:

  • Применение к выражению любого унарного, двоичного или троичного оператора при условии, что другие операнды также являются постоянными.
  • Доступ к полю в выражении.
  • Индексирование выражения с использованием числа или объекта.


Числа и строки в Python являются постоянными, как и литералы list, tuple, set и dict, для которых все компоненты постоянны. Ren'Py помечает переменные, определенные с помощью оператора define, как константу. Функции renpy.const() и renpy.not_const() могут использоваться для дальнейшего управления тем, что Ren'Py считает постоянным. Список постоянных имён по умолчанию приведён в разделе «Постоянные имена», описанном ниже.

Если у вас есть переменная, которая никогда не изменится, имеет смысл использовать оператор define как для определения, так и для объявления её постоянной. Например:

define GRID_WIDTH = 20
define GRID_HEIGHT = 10


Вызываемая функция, класс или действие являются чистыми, если, когда все их аргументы являются постоянными значениями, они всегда дают одно и то же постоянное значение. Кроме того, выражение, вызывающее чистую функцию с постоянным выражением, также является постоянным выражением. Большое количество функций, классов и действий по умолчанию помечены как чистые. Эти функции перечислены в разделе «Чистые имена», описанном ниже.

Функции объявляются в чистом виде с помощью функции renpy.pure(), которая может использоваться в качестве декоратора для функций, объявленных в хранилище по умолчанию.

Постоянные выражения и чистые функции не должны сохранять одно и то же значение для следующих событий:

  • Конец фазы инициализации.
  • Смена языка.
  • Восстановление стиля.


◇ Как константа оптимизирует язык экрана[править | править код]

Существует три преимущества для обеспечения того, чтобы аргументы и свойства языка экрана были постоянными.

Во-первых, постоянные аргументы и свойства вычисляются при подготовке экранов, то есть в конце фазы инициализации, когда изменяется язык или перестраиваются стили. После этого больше не нужно тратить время на оценку постоянных аргументов и свойств.

Во-вторых, константа хорошо работает с многократным использованием отображаемого объекта. Когда все аргументы и свойства отображаемого объекта постоянны, отображаемый объект всегда можно использовать повторно, что даёт все преимущества повторного использования отображаемого объекта.

И наконец, когда Ren'Py сталкивается с деревом отображаемых объектов таким образом, что все аргументы, свойства и выражения, влияющие на поток управления, также являются постоянными, Ren'Py будет повторно использовать все дерево без вычисления выражений или создания отображаемых объектов. Это может привести к значительному повышению производительности.

Например, следующий экран не выполняет код на Python и не создаёт никаких отображаемых элементов после первого прогнозирования или отображения:

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 = "Привет, мир."
text "[t]"

Предоставление переменной, содержащей текст напрямую, обычно не является константой:

$ t = "Привет, мир."
text t

Также не используется процентная подстановка:

$ t = "Привет, мир."
text "%s" % t

Наконец, обратите внимание, что функция перевода текста _ является чистой, поэтому, если она содержит строку, все выражение является константой:

text _("Ваш счет: [score]")


◇ Постоянные функции[править | править код]

renpy.const(name)

Объявляет переменную в хранилище как постоянную.
Переменная является постоянной, если ничто не может изменить её значение или любое значение, достигнутое путём индексации или доступа к ее атрибутам. Переменные должны оставаться неизменными вне блоков define, init и translate python.
name
Строка, задающая имя переменной для объявления константы.


renpy.not_const(name)

Объявляет имя в хранилище как непостоянное.
Отменяет эффект вызовов renpy.const() и renpy.pure().
name
Для объявления имени, которое не является постоянным.


renpy.pure(fn)

Объявляет функцию как чистую. Чистая функция всегда должна возвращать одно и то же значение, когда она вызывается с теми же аргументами, вне блоков define, init и translate python.
fn
Имя функции для объявления чистой. Это может быть либо строка, содержащая имя функции, либо сама функция.
Возвращает fn, позволяя использовать эту функцию в качестве декоратора.

Профилирование (Profiling)[править | править код]

Ren'Py поддерживает выполнение экрана профилирования с помощью функции renpy.profile_screen:

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 будет фиксировать информацию о том, как оцениваются экраны, в том числе:
  • какие отображаемые объекты Ren'Py считает постоянными.
  • какие аргументы, если таковые имеются, необходимо оценить.
  • которые были повторно использованы для отображения.
Создание и сохранение этой отладочной информации занимает довольно много времени, поэтому вывод time не считается надежным, если установлен debug.


Последняя группа аргументов управляет тем, какие выходные данные генерируется один раз за запуск Ren'Py.
const
Отображает на экране переменные, помеченные как const и не const.


Все выходные данные профилирования будут записаны в profile_screen.txt в директории игры.

Постоянные имена (Const Names )[править | править код]

Следующие имена являются постоянными по умолчанию:

  • False
  • None
  • True
  • config
  • style

Чистые имена (Pure Names)[править | править код]

Следующие имена являются чистыми и постоянными по умолчанию.

  • ADVCharacter
  • ADVSpeaker
  • [[#|AddToSet()
  • Alpha
  • [[#|AlphaBlend()
  • [[#|AlphaDissolve()
  • [[#|AlphaMask()
  • [[#|AnimatedValue()
  • Animation
  • [[#|At()
  • [[#|AudioPositionValue()
  • [[#|Call()
  • [[#|Character()
  • [[#|Color
  • [[#|ComposeTransition()
  • [[#|ConditionSwitch()
  • [[#|Confirm()
  • [[#|CropMove()
  • [[#|DictInputValue()
  • [[#|DictValue()
  • [[#|DisableAllInputValues()
  • [[#|Dissolve()
  • [[#|Drag
  • DynamicCharacter
  • [[#|DynamicDisplayable()
  • [[#|EndReplay()
  • FactorZoom
  • [[#|Fade()
  • [[#|FieldInputValue()
  • [[#|FieldValue()
  • [[#|FileDelete()
  • [[#|FilePage()
  • [[#|FilePageNameInputValue()
  • [[#|FileTakeScreenshot()
  • [[#|Fixed()
  • [[#|Flatten()
  • [[#|FontGroup()
  • [[#|Frame()
  • [[#|Grid()
  • [[#|HBox()
  • [[#|Help()
  • [[#|Hide()
  • [[#|HideInterface()
  • [[#|If()
  • [[#|Image()
  • [[#|ImageDissolve()
  • ImageReference
  • [[#|InputValue
  • [[#|InvertSelected()
  • [[#|Jump()
  • [[#|Language()
  • LiveComposite
  • LiveCrop
  • LiveTile
  • [[#|MainMenu()
  • [[#|MixerValue()
  • Motion
  • [[#|MouseMove()
  • Move
  • MoveFactory
  • MoveIn
  • MoveOut
  • [[#|MoveTransition()
  • [[#|Movie()
  • [[#|MultipleTransition()
  • NVLCharacter
  • [[#|Notify()
  • [[#|Null()
  • [[#|NullAction()
  • OldMoveTransition
  • [[#|OpenURL()
  • Pan
  • [[#|ParameterizedText()
  • Particles
  • [[#|Pause()
  • [[#|PauseAudio()
  • [[#|Pixellate()
  • Play
  • [[#|PlayCharacterVoice()
  • Position
  • [[#|Preference()
  • [[#|PushMove()
  • [[#|Queue()
  • [[#|QueueEvent()
  • [[#|QuickLoad()
  • [[#|QuickSave()
  • [[#|Quit()
  • [[#|RemoveFromSet()
  • [[#|Replay()
  • [[#|RestartStatement()
  • [[#|Return()
  • Revolve
  • RevolveInOut
  • [[#|RollForward()
  • [[#|Rollback()
  • RotoZoom
  • [[#|ScreenVariableValue()
  • [[#|Screenshot()
  • [[#|Scroll()
  • [[#|SelectedIf()
  • [[#|SensitiveIf()
  • [[#|SetCharacterVolume()
  • [[#|SetDict()
  • [[#|SetField()
  • [[#|SetMixer()
  • [[#|SetMute()
  • [[#|SetScreenVariable()
  • [[#|SetVariable()
  • [[#|SetVoiceMute()
  • [[#|Show()
  • [[#|ShowMenu()
  • [[#|ShowTransient()
  • [[#|ShowingSwitch()
  • SizeZoom
  • [[#|Skip()
  • [[#|SnowBlossom()
  • [[#|Solid()
  • Speaker
  • [[#|Start()
  • [[#|StaticValue()
  • Stop
  • [[#|StylePreference()
  • SubTransition
  • [[#|Text()
  • [[#|ToggleDict()
  • [[#|ToggleField()
  • [[#|ToggleMute()
  • [[#|ToggleScreen()
  • [[#|ToggleScreenVariable()
  • [[#|ToggleSetMembership()
  • [[#|ToggleVariable()
  • [[#|ToggleVoiceMute()
  • [[#|Transform
  • Update
  • [[#|VBox()
  • [[#|VariableInputValue()
  • [[#|VariableValue()
  • Viewport
  • [[#|VoiceReplay()
  • Window
  • Zoom
  • ZoomInOut
  • [[#|_()
  • _DisplayReset
  • _InputValueAction
  • __renpy__dict__
  • __renpy__list__
  • __renpy__set__
  • _m1_00gallery__GalleryAction
  • _m1_00gallery__GalleryToggleSlideshow
  • _m1_00musicroom__MusicRoomPlay
  • _m1_00musicroom__MusicRoomRandomPlay
  • _m1_00musicroom__MusicRoomStop
  • _m1_00musicroom__MusicRoomTogglePlay
  • _m1_00preferences__DisplayAction
  • _movebottom
  • _moveleft
  • _moveright
  • _movetop
  • _narrator
  • _notify_transform
  • [[#|_p()
  • abs
  • absolute
  • all
  • any
  • apply
  • bin
  • [[#|blinds
  • bool
  • bytes
  • callable
  • centered
  • chr
  • cmp
  • color
  • dict
  • [[#|dissolve
  • divmod
  • [[#|fade
  • filter
  • float
  • frozenset
  • getattr
  • globals
  • [[#|gui.SetPreference()
  • [[#|gui.TogglePreference()
  • [[#|gui.preference()
  • hasattr
  • hash
  • hex
  • [[#|hpunch
  • int
  • [[#|irisin
  • irisout
  • isinstance
  • len
  • list
  • long
  • map
  • max
  • min
  • [[#|name_only
  • [[#|narrator
  • oct
  • ord
  • [[#|pixellate
  • pow
  • pushdown
  • pushleft
  • [[#|pushright
  • pushup
  • range
  • reduce
  • renpy.Keymap
  • renpy.ParameterizedText
  • [[#|renpy.check_text_tags()
  • renpy.curried_call_in_new_context
  • renpy.curried_invoke_in_new_context
  • renpy.curry
  • renpy.easy_displayable
  • [[#|renpy.exists()
  • [[#|renpy.filter_text_tags()
  • [[#|renpy.fsdecode()
  • [[#|renpy.fsencode()
  • [[#|renpy.get_all_labels()
  • [[#|renpy.has_label()
  • renpy.has_screen
  • renpy.image_exists
  • [[#|renpy.image_size()
  • [[#|renpy.known_languages()
  • [[#|renpy.license
  • [[#|renpy.list_files()
  • [[#|renpy.loadable()
  • renpy.munged_filename
  • renpy.partial
  • renpy.unelide_filename
  • [[#|renpy.variant()
  • [[#|renpy.version()
  • [[#|renpy.version_name
  • [[#|renpy.version_only
  • [[#|renpy.version_string
  • [[#|renpy.version_tuple
  • repr
  • round
  • set
  • slideawaydown
  • [[#|slideawayleft
  • slideawayright
  • slideawayup
  • slidedown
  • [[#|slideleft
  • slideright
  • slideup
  • sorted
  • [[#|squares
  • str
  • sum
  • tuple
  • ui.callsinnewcontext
  • ui.gamemenus
  • ui.invokesinnewcontext
  • ui.jumps
  • ui.jumpsoutofcontext
  • ui.returns
  • unichr
  • unicode
  • vars
  • vcentered
  • [[#|vpunch
  • wipedown
  • [[#|wipeleft
  • wiperight
  • wipeup
  • zip
  • [[#|zoomin
  • [[#|zoominout
  • [[#|zoomout

Источник[править | править код]

Статья "Screen Language Optimization" на английском языке.


На главную страницу Официальная документация движка Вернуться к началу статьи