Слоёные изображения
Когда набор спрайтов достигает определённого уровня сложности, объявление каждой возможной комбинации может стать громоздким. Например, для персонажа с 4 нарядами, 4 причёсками и 6 эмоциями уже существует 96 возможных комбинаций. Создание статических изображений для каждой из них заняло бы много места на диске и времени программиста.
Для решения этой проблемы Ren'Py вводит способ объявления изображения, состоящего из нескольких спрайтов, организованных в слои. (Для целей этого раздела рассматривайте слои как слои в графическом редакторе, вроде Photoshop или GIMP, а не как слои, используемые в других частях Ren'Py). Элементы этих слоёв можно выбирать с помощью атрибутов, переданных изображению, или с помощью условий, которые вычисляются во время выполнения.
Эти изображения можно объявить с помощью инструкции layeredimage, используя специальный язык. Объект LayeredImage() является его Python-альтернативой; он не является отображаемым объектом, но может быть присвоен инструкции image и использоваться как таковой.
Внизу этой страницы содержатся советы и примеры использования.
Объявление слоёных изображений
Язык, используемый для объявления слоёных изображений, состоит всего из нескольких инструкций для введения слоёв. Вот пример, который, хоть и не имеет большого практического смысла, технически корректен и демонстрирует синтаксис layeredimage:
layeredimage augustina:
zoom 1.4
at recolor_transform
always:
"augustina_base"
attribute base2 default
group outfit:
attribute dress default:
"augustina_dress"
attribute uniform
attribute psychedelic:
image:
"augustina_dress"
matrixcolor TintMatrix("#f00")
linear 1 matrixcolor TintMatrix("#0f0")
linear 1 matrixcolor TintMatrix("#00f")
repeat
group face auto:
pos (100, 100)
attribute neutral default
label start:
show augustina # отображаются dress и neutral
aug "Мне нравится это платье."
show augustina happy # авто-определён в auto группе
aug "Но что мне нравится ещё больше..."
show augustina uniform -happy # uniform заменяет dress, neutral заменяет happy
aug "Так это эта униформа!"
Layeredimage
Инструкция layeredimage начинает объявление. Эта инструкция является частью языка сценариев Ren'Py и выполняется во время фазы инициализации. Подобно инструкции image в ATL, она принимает имя изображения и открывает блок, хотя содержимое блока сильно отличается. Имя изображения может содержать пробелы, как и любое другое имя изображения в Ren'Py.
Внутри блока могут находиться инструкции, описанные ниже, а также следующие необязательные свойства.
- image_format
Когда данное изображение является строкой и это свойство указано, имя изображения подставляется в image_format для создания файла изображения. Например, "sprites/eileen/{image}.png" будет искать изображение в подкаталоге sprites. (Это не используется auto-группами, которые ищут определённые изображения, а не файлы изображений).
- format_function
Функция, которая используется вместо
layeredimage.format_function()для форматирования информации об изображении в отображаемый объект во время объявления изображения на этапе инициализации.- attribute_function
Функция или вызываемый объект, используемый для настройки того, какие атрибуты в итоге будут отображаться. Он вызывается с набором атрибутов, переданных изображению, и должен возвращать набор атрибутов, который будет использоваться для выбора слоёв. Это можно использовать для выражения сложных зависимостей между атрибутами или для выбора атрибутов случайным образом. См. процесс выбора атрибутов для получения дополнительной информации о том, когда и как это вызывается.
- at
Преобразование или список преобразований, применяемых к слоёному изображению. Может также использоваться в виде
at transform:с последующим ATL-блоком для определения ATL-преобразования, которое будет применено к layeredimage.- Свойства преобразования
Если указаны, они используются для создания преобразования, которое применяется к отображаемому объекту.
- offer_screen
Если это True, layeredimage будет размещать свои дочерние элементы и определять их размер (для элементов с переменным размером) так, как если бы ему была предоставлена область, соответствующая всему экрану игры. Если False, указанные действия будут выполняться с учётом доступной области, которая, например, будет меньше в hbox, содержащем другие элементы, и отображение layeredimage не будет постоянным при каждом показе.
Если None (значение по умолчанию), используется
config.layeredimage_offer_screen, которое по умолчанию равно True.
Always
Инструкция always объявляет изображение, которое всегда отображается внутри layeredimage и не привязывается к атрибуту. Ей должен быть передан отображаемый объект, и она также может принимать свойства. И то, и другое можно разместить на одной строке или внутри блока. Отображаемый объект может быть объявлен с помощью слова image:, за которым следует ATL-блок, что аналогично инструкции image в ATL.
Инструкция always принимает следующие свойства:
- when
Выражение When в скобках. Если оно указано, этот слой отображается только в том случае, если набор атрибутов, вызванных для layeredimage, удовлетворяет этому выражению when.
- Свойства преобразования
Если указаны, они используются для создания преобразования, которое применяется к отображаемому объекту.
- at
Преобразование или список преобразований, применяемых к предоставленному отображаемому объекту. Может также использоваться в виде
at transform:с последующим ATL-блоком для определения ATL-преобразования, которое будет применено к отображаемоemu объекту.
If
Инструкция if (или, в более полном виде, инструкция if-elif-else) позволяет вам указать одно или несколько условий, которые вычисляются во время выполнения. Каждое условие связано с отображаемым объектом, и отображается тот, чьё условие истинно первым. Если ни одно условие не истинно, отображается слой else, если он указан.
Более полный пример инструкции if может выглядеть так:
if glasses == "evil":
"augustina_glasses_evil"
elif glasses == "normal":
"augustina_glasses"
elif glasses == "funky":
"augustina_glasses_clown"
else:
"augustina_nose_mark"
Каждому предложению должен быть предоставлен отображаемый объект, что можно сделать с помощью синтаксиса image:, описанного ранее. Ему также могут быть переданы следующие свойства:
- when
Выражение When в скобках. Если оно указано, это условие отображается только в том случае, если набор атрибутов, вызванных для layeredimage, удовлетворяет выражению when.
- Свойства преобразования
Если указаны, они используются для создания преобразования, которое применяется к отображаемому объекту.
- at
Преобразование или список преобразований, применяемых к отображаемому объекту. Может также использоваться в виде
at transform:с последующим ATL-блоком для определения ATL-преобразования, которое будет применено к отображаемому объекту.
Инструкция if преобразуется в ConditionSwitch() при выполнении инструкции layeredimage.
- layeredimage.predict_all = None
Устанавливает значение predict_all для ConditionSwitch, создаваемых инструкциями
ifв layeredimage.
Когда predict_all не является true, следует избегать изменения условия инструкции if, пока слоёное изображение отображается или готовится к отображению, так как это может привести к загрузке непредсказанного изображения. Это предназначено для использования в опциях кастомизации персонажа, которые не меняются часто.
Attribute
Инструкция attribute добавляет отображаемый объект, который становится частью итогового изображения, когда для его отображения используется указанный атрибут. Например, используя предыдущий пример, вызов show augustina dress приведёт к тому, что "augustina_dress" будет показан как часть изображения "augustina".
Предложение attribute принимает имя атрибута, которое является одним словом (точнее, одним компонентом имени изображения, см. элементы инструкций). Оно также может принимать два ключевых слова. Ключевое слово default указывает, что атрибут должен присутствовать по умолчанию, если не вызван другой атрибут из той же группы. Ключевое слово null предотвращает привязку отображаемого объекта к этому предложению, что может быть полезно для учёта и создания условий условного отображения с использованием when, attribute_function,
config.adjust_attributes или config.default_attribute_callbacks.
Одно и то же имя атрибута
можно использовать в нескольких предложениях attribute (а также в автоматически определённых атрибутах в составе auto-групп, о чём позже), при этом все соответствующие отображаемые объекты будут показаны одновременно (свойство when может это изменить).
Может быть указан отображаемый объект, опционально с использованием синтаксиса image:, описанного ранее. Если отображаемый объект не указан явно, он будет вычислен на основе имени layeredimage, группы (если есть), варианта группы или атрибута (если есть) и самого атрибута. См. раздел шаблон для получения дополнительной информации.
Инструкция attribute принимает следующие свойства:
- when
Выражение When в скобках. Если оно указано, этот слой отображается только в том случае, если набор атрибутов, вызванных для layeredimage, удовлетворяет выражению when.
- Свойства преобразования
Если указаны, они используются для создания преобразования, которое применяется к слою.
- at
Преобразование или список преобразований, применяемых к слою. Может также использоваться в виде
at transform:с последующим ATL-блоком для определения ATL-преобразования, которое будет применено к слою.- variant
Слово, которое добавляется в начало имени атрибута (с подчёркиванием) при поиске отображаемого объекта для этого атрибута. Это свойство допустимо только для атрибутов без явно заданного отображаемого объекта и которые ещё не находятся в группе с вариантом.
Проверка предложения when основана на списке атрибутов итогового изображения, как объяснено здесь, но оно не изменяет этот список.
layeredimage eileen:
attribute a
attribute b default when not a
attribute c default when not b
В этом примере атрибуты b и c всегда являются частью списка атрибутов (из-за их предложения default). При вызове show eileen a атрибут a будет отображён, как и запрошено, а атрибут b — нет, из-за его свойства when. Но даже если он не отображается, атрибут b всё равно будет частью списка атрибутов, что означает, что атрибут c также не будет отображаться.
Group
Инструкция group объединяет атрибуты, делая их взаимоисключающими. Когда атрибуты a и b находятся в одной группе, будет ошибкой включать оба атрибута одновременно, например, с помощью show eileen a b, за исключением случаев, когда группа является multiple. В том же примере вызов атрибута a скроет атрибут b, и наоборот. Однако обратите внимание, что несколько предложений attribute могут иметь одно и то же имя, даже в пределах одной группы. В этом случае они будут рассматриваться как один атрибут, содержащий несколько спрайтов — подробнее об этом в конце этого раздела.
Инструкция group принимает имя. Это имя используется не так часто, за исключением генерации имён атрибутов по умолчанию внутри группы.
Имя группы multiple зарезервировано, оно имеет специфическое поведение, делая группу multiple. В этом случае несовместимость не применяется к атрибутам, объявленным внутри блока. Это полезно для автоматического определения группой нескольких атрибутов, которые не являются эксклюзивными, или для применения одних и тех же свойств к набору атрибутов одновременно. Это конфликтует с использованием ключевого слова default для одного из атрибутов. Обратите внимание, что multiple группы сильно отличаются от других, обычных групп, и большая часть того, что верно для групп, к ним не относится. В частности, считается, что у них вообще нет имени.
За именем может следовать ключевое слово auto. Если оно присутствует, то после объявления всех атрибутов в группе Ren'Py просканирует свой список изображений на предмет тех, которые соответствуют шаблону группы (см. ниже), с той особенностью, что в этом случае format_function, переданная в layeredimage, игнорируется. Любые найденные изображения, за исключением тех, которые соответствуют явно объявленным атрибутам, добавляются в группу так, как если бы они были объявлены с помощью инструкции attribute внутри блока группы. См. раздел примеры layeredimage для практической демонстрации.
После этого необязательного ключевого слова на первой строке группы можно объявить свойства, и она может содержать блок со свойствами и атрибутами.
Инструкция group принимает те же свойства, что и attribute, такие как
when, at и так далее. Свойства, предоставленные группе, передаются
атрибутам внутри группы, если они не переопределены тем же свойством
самого атрибута. Два свойства более специфичны для групп:
- variant
Это свойство аналогично тому, что принимает
attribute, за исключением того, что его нельзя передать одновременно и группе, _и_ атрибутам внутри неё. Если указано, это должно быть слово. Если оно присутствует, оно используется как вариант для автоматической генерации имён изображений для атрибутов без явно переданных отображаемых объектов (см. раздел шаблон для получения дополнительной информации), и оно используется в шаблоне для поиска изображений при автоматическом определении атрибутов вauto-группах (см. выше).- prefix
Если указано, это префикс, который объединяется с помощью подчёркивания с именами атрибутов, определённых вручную или автоматически. Так, если префикс —
leftarm, и встречаетсяattribute hip, тоshow eileen leftarm_hipотобразит его.
Атрибут также может быть частью нескольких групп, и в этом случае он будет несовместим со всеми другими атрибутами в каждой группе, к которой он принадлежит. Это может быть полезно, например, для атрибута dress, чтобы он скрывал и верх, и штаны, которые могут быть показаны, когда он отображается:
layeredimage eileen:
attribute base default
group bottom:
attribute jeans default
attribute dress null
group top:
attribute shirt default
attribute dress
Когда в одном и том же layeredimage определено несколько блоков group с одинаковым именем, они считаются частями одной группы. Например:
layeredimage eileen sitting:
attribute base default
group arms variant behind:
attribute on_hips
attribute on_knees
attribute mixed
attribute table default
group arms variant infront:
attribute on_table default
attribute holding_margarita
attribute mixed
В этом примере eileen_sitting_arms_behind_mixed.png будет содержать её левую руку за столом, а eileen_sitting_arms_infront_mixed.png — её правую руку на столе. При вызове show eileen sitting mixed оба изображения будут показаны одновременно, соответственно, за и перед столом. В этом примере атрибут on_hips несовместим с атрибутом on_table, потому что, хотя они не объявлены в одном блоке, они оба находятся в одной группе.
When
Выражение when представляет собой логическое условие, зависящее от набора атрибутов, активных в данный момент для layeredimage.
Оно состоит из одного или нескольких имён атрибутов, разделённых ключевыми словами "and" и "or", и, возможно, с отрицанием "not". Вы также можете использовать скобки для задания приоритета.
Пример:
always:
"eileen_red_dress"
when b and not c
# изображение будет показано, когда активен атрибут b и не активен атрибут c
if var:
"eileen_blue_ribbon"
when not (a or b)
# изображение будет показано, когда переменная var истинна
# и ни атрибут a, ни атрибут b не активны
attribute a:
"eileen_a"
when b or e
# изображение будет показано, когда активен атрибут "a"
# и активен либо атрибут "b", либо "e"
Устаревшие свойства
Следующие свойства инструкций always, if и attribute
устарели, но могут присутствовать в играх, написанных до
добавления предложения when. Они всё ещё работают, но предпочтительнее использовать when.
- if_all
Строка или список строк, содержащих имена атрибутов. Если указано, этот слой отображается только в том случае, если все названные атрибуты присутствуют.
- if_any
Строка или список строк, содержащих имена атрибутов. Если указано, этот слой отображается только в том случае, если любой из названных атрибутов присутствует.
- if_not
Строка или список строк, содержащих имена атрибутов. Если указано, этот слой отображается только в том случае, если ни один из названных атрибутов не присутствует.
Для преобразования в синтаксис when вы можете заменить:
if_any ["a", "b"]
if_all ["c", "d"]
if_not ["e", "f"]
на более лаконичное:
when (a or b) and c and d and not (e or f).
Шаблон и функция форматирования
Шаблон, используемый для поиска изображений для атрибутов, когда им явно не присвоено изображение, состоит из:
Имени layeredimage, с заменой пробелов на подчёркивания.
Имени группы, если мы находимся в группе, не являющейся
multiple.Имени варианта, если он есть.
Имени атрибута.
всех частей, объединённых подчёркиваниями. Например, если у нас есть слоёное изображение с именем "augustina work" и группа "eyes", это будет соответствовать изображениям, которые совпадают с шаблоном augustina_work_eyes_attribute. С variant blue это будет соответствовать шаблону augustina_work_eyes_blue_attribute. В следующем примере:
layeredimage augustina work:
group eyes variant blue:
attribute closed
Атрибут связывается с изображением "augustina_work_eyes_blue_closed". Это может быть файлом изображения с именем augustina_work_eyes_blue_closed.png, но также может быть явно определено, например, с помощью инструкции image.
Всё поведение шаблона можно изменить с помощью format_function:
layeredimage.format_function() — это функция, которая используется "под капотом" для реализации описанного выше поведения. Вы можете посмотреть, какие аргументы она принимает, если захотите предоставить свою собственную format_function для её замены.
- layeredimage.format_function(what, name, group, variant, attribute, image, image_format, **kwargs)
Эта функция вызывается для форматирования информации об атрибуте или условии в displayable. Она может быть заменена создателем, но новая функция должна игнорировать неизвестные kwargs.
- what
Строка, дающая описание форматируемого элемента, которое используется для создания более качественных сообщений об ошибках.
- name
Имя layeredimage.
- group
Группа атрибута, None, если не указана или если это часть условия.
- variant
Аргумент variant для группы или None, если он не указан.
- attribute
Сам атрибут.
- image
Либо displayable, либо строка.
- image_format
Аргумент image_format для LayeredImage.
Если image равен None, то name, group (если не None), variant (если не None) и attribute объединяются с помощью подчёркиваний для создания image, которое затем будет строкой.
Если images является строкой, а image_format не равен None, image форматируется в строку для получения конечного displayable.
Так, если name — "eileen", group — "expression", и attribute — "happy", image будет установлено в "eileen_expression_happy". Если image_format — "images/{image}.png", конечное изображение, которое найдёт Ren'Py, будет "images/eileen_expression_happy.png". Но обратите внимание, что он нашёл бы то же изображение и без аргумента форматирования.
Но обратите внимание, что переданная format_function _не_ меняет то, как auto-группы находят свои изображения: они всегда будут использовать точный шаблон, описанный выше.
Проксирование слоёных изображений
Иногда бывает полезно (и даже необходимо) создать прокси для слоёного изображения, чтобы использовать одно и то же слоёное изображение в нескольких местах. Одной из причин этого может быть необходимость иметь трансформированную версию данного layeredimage, а другой — использовать его как изображение сбоку (side image).
Объект LayeredImageProxy() делает это, принимая одно слоёное изображение и дублируя его в другом месте. Например:
image dupe = LayeredImageProxy("augustina")
создаёт дубликат изображения, который можно отображать независимо. Он также принимает аргумент transform, что делает его полезным для позиционирования изображения сбоку, например, так:
image side augustina = LayeredImageProxy("augustina", Transform(crop=(0, 0, 362, 362), xoffset=-80))
Смотрите разницу:
image sepia_augustina_one = Transform("augustina", matrixcolor=SepiaMatrix())
image sepia_augustina_two = LayeredImageProxy("augustina", Transform(matrixcolor=SepiaMatrix()))
sepia_augustina_one будет версией в сепии оригинальной версии слоёного изображения "augustina", другими словами, тем, что отображается, когда вы не предоставляете ему никаких атрибутов. Напротив, sepia_augustina_two будет принимать любые атрибуты, которые принимает "augustina", а затем применять эффект сепии к результату. Если вы можете сделать это:
show augustina happy eyes_blue dress
тогда:
show sepia_augustina_one happy eyes_blue dress
# не сработает, потому что Transform не принимает атрибуты
show sepia_augustina_two happy eyes_blue dress
# сработает, и покажет "augustina happy eyes_blue dress" с эффектом сепии
- class LayeredImageProxy(name, transform=None)
Это объект, подобный изображению, который проксирует переданные ему атрибуты другому layered image.
- name
Строка, задающая имя layeredimage, на которое будет осуществляться проксирование.
- transform
Если задано, трансформация или список трансформаций, которые применяются к изображению после проксирования.
Выбор атрибутов для отображения
Несколько факторов влияют на то, что отображается после выполнения определённой инструкции show. Чтобы внести больше ясности в то, что происходит и в каком порядке, этот раздел демонстрирует жизнь набора атрибутов, от инструкции show до отображения на экране.
Инструкция
showпредоставляет начальный набор атрибутов после тега изображения.Если существует функция
config.adjust_attributes, соответствующая тегу изображения, она вызывается и возвращает потенциально другой набор атрибутов. Если да, то он заменяет предыдущий набор, который забывается.Если существует функция
config.default_attribute_callbacksи если её условия срабатывания выполнены, она вызывается и потенциально добавляет атрибуты в набор.
Предыдущие этапы не являются специфичными для layeredimages, потому что только после этого этапа Ren'Py определяет, какое изображение или layeredimage будет вызвано для отображения. По этой причине данный набор атрибутов должен приводить к одному, и только одному, определённому изображению (или layeredimage, Live2D...), используя поведение, описанное в разделе инструкции show.
Затем предоставленные атрибуты объединяются с атрибутами, определёнными в layeredimage, отбрасывая некоторые ранее показанные атрибуты и сохраняя другие. На этом этапе также обнаруживаются нераспознанные атрибуты и генерируются соответствующие ошибки. Если таких ошибок нет, новые атрибуты, а также те, которые не были отброшены, будут распознаны Ren'Py как набор атрибутов, связанный с этим тегом изображения. Это вычисление учитывает некоторые ограничения несовместимости, но не все. Например, несовместимости из-за нахождения атрибутов в одной и той же группе (не multiple) сработают на этом этапе, а предложения
when— нет. Вот почему атрибут, вызванный, но отменённый таким предложением, будет считаться активным Ren'Py и, например, станет видимым без повторного вызова, если в какой-то момент условие предложения if_x больше не будет выполняться.Если для layeredimage была предоставлена
attribute_function, она вызывается с набором оставшихся атрибутов. Она возвращает потенциально другой набор атрибутов.Этот набор снова сопоставляется с ограничениями несовместимости layeredimage, на этот раз в полной мере. Это заключительный этап, и оставшиеся атрибуты вызываются для отображения.
Советы
Используйте подчёркивания в именах файлов изображений.
По умолчанию слоёные изображения Ren'Py используют подчёркивания для разделения частей имён изображений. Может возникнуть соблазн использовать изображения с пробелами между частями, но это может привести к проблемам в будущем.
В Ren'Py есть правило: если вы показываете изображение с точным именем того, что уже отображается, оно показывается вместо него. Это может обойти определённое вами слоёное изображение и показать спрайт напрямую, что может привести к странным проблемам, таким как пара глаз, парящих в пространстве.
Если у каждого спрайта будет свой тег, отличный от основного изображения, эта проблема исчезнет.
Обрезка слоёв не обязательна.
Ren'Py оптимизирует изображения, обрезая их до ограничивающей рамки непрозрачных пикселей перед загрузкой в ОЗУ. В результате, при условии, что изображения правильно предсказываются, самостоятельная обрезка изображений, как правило, не сильно улучшит производительность или размер изображений.
Слоёные изображения не должны использовать данные, изменяющиеся во время выполнения.
Обратите внимание, что за исключением условий в инструкции if, все выражения, написанные в блоке layeredimage, вычисляются во время инициализации, когда слоёное изображение определяется впервые. Это не относится, например, к ATL-преобразованиям или ко всему, что происходит в config.adjust_attributes,
config.default_attribute_callbacks или attribute_function, но это относится к format_function, которая также вызывается только при определении layeredimage.
Выбор синтаксиса для использования
Если вы хотите, чтобы спрайт был всегда виден, используйте либо предложение always, либо синтаксис attribute x default. always потребует от вас явного предоставления отображаемого объекта (автоматическое присваивание с использованием шаблона будет недоступно), а attribute займёт имя атрибута "x", который всегда будет активен для этого layeredimage.
Если вы хотите, чтобы он появлялся в зависимости от атрибутов, передаваемых layeredimage в момент инструкции show, например, show eileen happy вместо show eileen jeans, используйте инструкцию attribute, внутри или вне блока group (или неявно определённую в auto-группе).
Если вы хотите, чтобы он появлялся в зависимости от переменной или условия Python, используйте инструкцию if.
Если вы хотите, чтобы он зависел от обоих (например, чтобы show eileen ribbon показывал синюю или красную ленту в зависимости от переменной, но лента не появлялась, пока вы не запросите её с атрибутом ribbon), объявите все версии как атрибуты и используйте специальную функцию config.adjust_attributes.
Примеры
Шаблон и auto-группы
Из следующих файлов в каталоге images/ (или одном из его подкаталогов) и написанного кода:
francis_base.png
francis_face_neutral.png
francis_face_angry.png
francis_face_happy.png
francis_face_very_happy.png
francis_face annoyed.png
francis_supersad.png
layeredimage francis:
attribute base default
group face auto:
attribute neutral default
attribute supersad image:
"#00c3"
xysize (100, 100)
Слоёное изображение francis объявит (по умолчанию) атрибут base и свяжет с ним (автоматически определённое) изображение "francis_base", используя шаблон: имя layeredimage ("francis"), имя группы (здесь нет), имя варианта (здесь нет) и имя атрибута ("base"), разделённые подчёркиваниями.
Затем, в группе face, явный атрибут neutral связывается с изображением "francis_face_neutral", следуя тому же шаблону, но используя "face" в качестве имени группы и "neutral" в качестве имени атрибута.
После того как все явные атрибуты получат свои изображения, поскольку face является auto-группой, существующие изображения (автоматически определённые или нет) сканируются на соответствие шаблону. Здесь находятся три соответствия: "francis_face_angry", "francis_face_happy" и "francis_face_very_happy". Они связываются с атрибутами angry, happy и very_happy соответственно, используя тот же шаблон, что и раньше. Однако атрибут annoyed не определяется, поскольку изображение "francis_face annoyed" содержит пробел там, где шаблон ожидал подчёркивание.
Наконец, объявляется атрибут supersad, но поскольку отображаемый объект предоставлен явно, шаблон не ищет соответствующее изображение.
Изображения "francis_supersad" и "francis_face annoyed" автоматически определяются из имени файла в рамках обычного протокола Ren'Py, но эти спрайты не находят соответствия ни с одним атрибутом или auto-группой, поэтому они в конечном итоге не используются в layeredimage francis.
Как видите, использование шаблона для связывания изображений с атрибутами и использование auto-групп значительно сокращает код. То же самое слоёное изображение заняло бы 13 строк, если бы всё было объявлено явно (попробуйте!), и этот синтаксис позволяет геометрически наращивать набор спрайтов — например, добавление любого количества новых лиц не потребует никаких изменений в коде.
Динамика в атрибутах
Вот пример определения атрибутов в зависимости от переменных (как упоминалось в разделе "Советы"):
layeredimage eileen:
attribute base default
group outfit auto
group ribbon prefix ribbon:
attribute red
attribute blue
default eileen_ribbon_color = "red"
init python:
def eileen_adjuster(names):
atts = set(names[1:])
if "ribbon" in atts:
atts.remove("ribbon")
atts.add("ribbon_" + eileen_ribbon_color)
return names[0], *atts
define config.adjust_attributes["eileen"] = eileen_adjuster
Включение групп в when
Свойство when не позволяет указывать имя группы, чтобы данный слой отображался только тогда, когда отображается какой-либо или ни один из атрибутов в данной группе. Однако, есть способ сделать это, используя различные другие части синтаксиса layeredimage.
Предположим, что у группы нет атрибута по умолчанию, вы можете добавить атрибут null по умолчанию с выбранным вами именем. Таким образом, если ни один атрибут из группы не показан, этот атрибут активируется (хотя ничего не отображается), и он снова деактивируется, если атрибут группы скрывается.
layeredimage eliza:
attribute head_and_hair default
group top auto
group headtop prefix headtop:
attribute notop null default
attribute hat
attribute cap
attribute hair_patch when notop
Здесь атрибут hair_patch будет показывать дополнительную часть волос Элизы поверх её верхнего слоя (например, на плечах) только в том случае, если нет шляпы, чтобы их скрыть.