Layered Images - Ren'Py Documentation

Download as pdf or txt
Download as pdf or txt
You are on page 1of 1

Ren'Py Documentation Home Page Online Documentation Search

Layered Images
When a sprite-set gets to a certain level of complexity, defining every possible combination may become unwieldy.
For example, a sprite with 4 outfits, 4 hairstyles, and 6 emotions already has 96 possible combinations. Creating
static images for each possible combination would consume a lot of disk space and programmer time.

To address this use case, Ren'Py has a way of defining an image consisting of multiple layers. (For the purpose of
this, consider layers to be the layers in a paint program like Photoshop or the GIMP, and not the layers used
elsewhere in Ren'Py.) Layers can be shown unconditionally, or can be selected by attributes provided to the image
or conditions that are evaluated at runtime.

To make defining layered images easier, Ren'Py has the layeredimage statement, which introduces a domain-
specific language that lets you define a layered image. There's also the LayeredImage() object, which isn't an
image but can be assigned to an image statement and used like one.

Defining Layered Images


The layered image domain-specific language consists of only a few statements, one of which is also a script
language statement to introduce the image, followed by statements to introduce the layers and groups of layers.

To introduce the language, here's a layered image that uses the available features, with things that could be implied
instead explicitly given.

layeredimage augustina:

always:
"augustina_base"

group outfit:

attribute dress:
"augustina_outfit_dress"

attribute jeans:
"augustina_outfit_jeans"

group eyes:

attribute open default:


"augustina_eyes_open"
default True

attribute wink:
"augustina_eyes_wink"

group eyebrows:

attribute normal default:


"augustina_eyebrows_normal"

attribute oneup:
"augustina_eyebrows_oneup"

group mouth:

pos (100, 100)

attribute smile default:


"augustina_mouth_smile"

attribute happy:
"augustina_mouth_happy"

if evil:
"augustina_glasses_evil"
else:
"augustina_glasses"

That is a large amount of script, but it's very regular, and below we'll show how to simplify it.

First off, the layeredimage statement introduces a layered image with the name of the sprite. This statement is part
of the Ren'Py script language, and runs at init time.

The block of a layered image can contain always, group, and if statements. A group statement can take attributes.
The always and if statements must be supplied displayables, while the attribute statement can optionally be
supplied one. All statements can be supplied properties.

The always statement declares a layer that is always displayed, like the background of a sprite.

The group statement introduces a group of attributes, where only one of the attributes can be displayed at a time.
So this layered image can only have one outfit, and one post for each of the eyes, eyebrows, and mouth. Properties
given to the group are passed on to the attributes, and a group can automatically declare attributes.

The attribute statement introduces a layer that is displayed if an attribute is supplied to the image. For example,
"augustina_outfit_dress" is only displayed if if the "dress" attribute is supplied. If given the default keyword, the
attribute is displayed if no conflicting attributes are provided; in this example, "augustina_eyes_open" is displayed
unless the unless the "wink" attribute is given.

Finally, the if statement adds a layer that selects between displayables using a Python statement. This is
evaluated constantly, and the first condition that evaluates to true is the one that's displayed.

Properties consist of a property name and a simple expression, and can be given to each layer. Some properties
change the functioning of a statement. If one or more transform properties are given, a Transform() is created that
wraps the display. The at property can be given a transform or list of transforms that also wrap the displayable. For
example, the pos property here creates a transform that moves the top-left corner of each mouth image.

The resulting image is the size of the bounding box of all the layers, so it probably makes sense to have one layer
the full size of the image, which no other layer goes outside of. The first layer is in the back of the image, while the
last is in front – in this example, the glasses will be on top of the other layers. It's recommended to avoid properties
that assume the size of the containing image, like xcenter and xalign , as these properties do not work well the
when the image size is not known.

Groups and attributes may appear more than once in a layered image, with all of the layers with an attribute being
displayed.

With the exception of the condition in an if statement, all Python expressions are evaluated at init time.

Using an Layered Image


To use this (but not other) layered images, the evil variable must be given a value, for example with:

default evil = True

Then the layered image can be shown like any other image. Almost certainly, one of the outfits should be given –
while Ren'Py doesn't enforce this, this image requires one:

show augustina jeans

While a sprite is being shown, additional attributes will be added to those already showing provided they do not
conflict. (This is the case in all of Ren'Py when an image being shown does not match one that's already defined,
something that is never the case with a layered image.) So,

show augustina wink

Will activate the layers associated with the wink attribute. We could stop winking with:

show augustina open

As the open eyes conflict with the winking eyes. Or we could simply remove the wink attribute using:

show augustina -wink

Which would display the layer with the open attribute, as it is the default.

Layered images can also be used with the scene statement.

Automatic Attributes
There's a lot of repetition our first example, when it comes to the attribute names and the displayables that define
the attribute. To save you from having to do a lot of redundant typing, Ren'Py can automatically determine a
displayable name from the image name, group name, and attribute name. This is done by combining the names with
underscores.

When doing this, you can also take advantage of another feature of attributes – it's possible to add any properties to
the first line and omit the block entirely.

Here's our example of having done that:

layeredimage augustina:

always:
"augustina_base"

group outfit:
attribute dress
attribute jeans

group eyes:
attribute open default
attribute wink

group eyebrows:
attribute normal default
attribute oneup

group mouth:
pos (100, 100)
attribute smile default
attribute happy

if evil:
"augustina_glasses_evil"
else:
"augustina_glasses"

This example is equivalent to the first one (as we gave the same names for the displayables in the first example).
For example, the dress attribute in the outfit group uses "augustina_outfit_dress" for the displayable, a displayable
that references the image with that name.

It's possible to go even further than this, by automatically defining the attributes in a group. This is done by giving a
group the auto keyword, which causes the group to search for defined images that would match the pattern, then
define the attribute if it does not already exist.

As with attribute , properties can be placed on the first line of the group and the block omitted. The displayable
and properties of the always statement can be put on the first line the same way.

Here's an example of the final form:

layeredimage augustina:

always "augustina_base"

group outfit auto

group eyes auto:


attribute open default

group eyebrows auto:


attribute normal default

group mouth auto:


pos (100, 100)
attribute smile default

if evil:
"augustina_glasses_evil"
else:
"augustina_glasses"

This is about as simply as we can define that image, without changing what we define. The savings with auto-
definition increases as we have more attributes per group. We could also save lines if we didn't need default
attributes. In that case, all of the groups could be written on single lines.

There's no way to omit the displayables from the always or if statements, so this is as short as it gets – but with a
few more images with proper names, it's possible to use this to define thousands or even millions of combinations of
layers.

Statement Reference
Note that with the conditions in the if statement, all expressions are evaluated at init time, when the layered image
is first defined.

Layeredimage
The layeredimage statement is a statement in the Ren'Py script language that introduces a layered image. It starts
with an image name, and takes a block that can contain attribute, group, and if statements.

Layeredimage takes the following properties:

image_format
When a given image is a string, and this is supplied, the image name is interpolated into image_format to make
an image file. For example, "sprites/eileen/{image}.png" will look for the image in a subdirectory of sprites. (This is
not used by auto groups, which look for images and not image files.)

format_function
A function that is used instead of layeredimage.format_function to format the image information into a displayable.

transform properties
If present, these are used to construct a Transform() that is applied to the displayable.

at
A transform or list of transforms that are applied to the layered image.

Attribute
The attribute statement adds a layer that is displayed when the given attribute is used to display the image. The
same attribute can be used with multiple layers, with all layers corresponding to the attribute being shown (the if_all,
if_any, and if_not properties can change this).

An attribute takes an attribute name. It can also take two keywords. The default keyword indicates that the
attribute should be present by default if no attribute in its group conflicts. The null keyword prevents Ren'Py from
automatically searching for a displayable corresponding to this attribute, which is useful to have an attribute that is
intended solely for use with if_all, if_any, or if_not.

If the displayable is not present, it will be computed from the name of the layer, group, group variant, and attribute,
by replacing all spaces with underscores and using underscores to combine everything together. So if we have an
image named "augustina", the group "eyes" and the attribute "closed", the image "augustina_eyes_closed" will be
used. (The layered image's format function is used to do this, defaulting to layeredimage.format_function() .)

If an attribute is not inside a group, it's placed in a group with the same name, but that group is not used to compute
the displayable name. (So it would look for "image_attribute", not "image_attribute_attribute").

The attribute statement takes the following properties:

if_all
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if all of the
named attributes are present.

if_any
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if any of the
named attributes are present.

if_not
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if none of the
named attributes are present.

transform properties
If present, these are used to construct a transform that is applied to the layer.

at
A transform or list of transforms that are applied to the layer.

Group
The group statement groups together alternative layers. When an attribute is inside a group, it is an error to include
any of the other attributes in that group. (But it's fine to include the same attribute twice. The multiple keyword
removes this restriction.)

The group statement takes a name. The name isn't used for very much, but is used to generate the default names
of attributes inside the group.

The name may be followed by the auto keyword. If it's present, after any attributes in the group have been
declared, Ren'Py will scan its list of images for those that match the group's pattern (see below). Any images that
are found that do not correspond to declared attributes are then added to the group as if declared with the attribute
statement.

This can be followed by the multiple keyword. If present, more than one member of the group can be selected at
the same time. This is useful to have a group auto-define multiple attributes that are not exclusive. This conflicts with
the default keyword being given to one of the attributes.

Properties can then be declared on the first line of the group, and it can take a block that contains properties and
attributes.

There are two properties that are specific to groups.

variant
If given, this should be a string. If present, it adds a variant element that becomes part of automatically-generated
image names and the pattern used to search for automatically-defined attributes.

prefix
If given, this is a prefix that is concatenated using an underscore with the manually or automatically defined
attribute names. So if prefix is "leftarm", and the attribute name "hip" is encountered, the attribute "leftarm_hip" is
defined instead.

The group statement also takes the same properties attribute does. Properties supplied to the group are passed
to the attributes inside the group, unless overridden by the same property of the attribute itself.

Pattern. The image pattern used consists of:

The name of the image, with spaces replaced with underscores.


The name of the group.
The name of the variant.
The name of the attribute.

all combined with underscores. For example, if we have a layered image with the name "augustina work", and the
group "eyes", this will match images that match the pattern augustina_work_eyes_attribute. With a variant of blue, it
would match the pattern augustina_work_eyes_blue_attribute.

Always
The always statement declares a layer that is always shown. It must be supplied a displayable, and can take
properties also. Both can be placed on the same line or inside a block.

The always statement takes the following properties:

if_all
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if all of the
named attributes are present.

if_any
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if any of the
named attributes are present.

if_not
A string or list of strings giving the names of attributes. If this is present, this layer is only displayed if none of the
named attributes are present.

transform properties
If present, these are used to construct a transform that is applied to the layer.

at
A transform or list of transforms that are applied to the layer.

If
The if statement (or more fully the if-elif-else) statement allows you to supply one or more conditions that are
evaluated at runtime. Each condition is associated with a layer, with the first true condition being the one that is
shown. If no condition is true, the else layer is shown if present.

A more complete example of an if statement might look like:

if glasses == "evil":
"augustina_glasses_evil"
elif glasses == "normal":
"augustina_glasses"
else:
"augustina_nose_mark"

Each layer must have a displayable given. It can also be given these properties:

if_all
A string or list of strings giving the names of attributes. If this is present, this condition is only considered if all of
the named attributes are present.

if_any
A string or list of strings giving the names of attributes. If this is present, this condition is only considered if any of
the named attributes are present.

if_not
A string or list of strings giving the names of attributes. If this is present, this condition is only considered if none of
the named attributes are present.

transform properties
If present, these are used to construct a transform that is applied to the layer.

at
A transform or list of transforms that are applied to the layer.

The if statement is transformed to a ConditionSwitch() when the layeredimage statement runs.

When predict_all is not true, changing the condition of the if statement should be avoided while the layered
image is shown or about to be shown, as it would lead to an unpredicted image load. It's intended for use for
character customization options that change rarely.

Poses
It's possible to have a character that has sprites in multiple poses, where everything – or at least everything of
interest – is different. For example, if a character has standing and sitting poses, all the image parts will be in
different places.

In that case, it makes sense to define multiple layered images for the same image tag. The layeredimage
statement makes this possible by allowing you to include attributes as part of the image name. So we can have:

layeredimage augustina sitting:


...

layeredimage augustina standing:


...

This is especially useful when using a layered image to compose a side image, where the side images of different
characters will have nothing to do with each other.

layeredimage side eileen:


...

layeredimage side lucy:


...

Advice
Use underscores in image names. By default, Ren'Py's layered images use underscores to separate sections of
image names. It might be tempting to use images with spaces between sections, but that could lead to problems
later on.

Ren'Py has a rule that if you show an image with the exact name as one that's being shown, it's shown instead. This
can bypass the layered image you defined and show the layer directly, which can lead to weird problems like a pair
of eyes floating in space.

By having each layer have a different tag from the main image, this is no longer a problem.

Cropping layers isn't necessary. Ren'Py optimizes images by cropping them to the bounding box of the non-
transparent pixels before loading them into RAM. As a result, assuming the images are being predicted properly, it
generally won't improve performance or image size much to crop the images yourself.

Layered images shouldn't use data that changes at runtime. With the exception of the condition inside an if
statement, all of the expressions in a layered images are run at init time. The layered image will not pick up changes
in variables that occur after the game starts. (However, expressions in ATL transforms will be run each time the
image is show, as with other ATL transforms.)

Python
Of course, the layeredimage statements have a Python equivalents. The group statement does not – the group is
supplied to attribute , and the auto functionality can be implemented using renpy.list_images() .

Attribute(group, attribute, image=None, default=False, group_args={}, **kwargs)


This is used to represent a layer of an LayeredImage that is controlled by an attribute. A single attribute can
control multiple layers, in which case all layers corresponding to that attribute will be displayed.

group
A string giving the group the attribute is part of. This may be None, in which case a group with the same name
as the attribute is created.

attribute
A string giving the name of the attribute.

image
If not None, this should be a displayable that is displayed when this attribute is shown.

default
If True, and no other attribute for the group is selected, this attribute is.

The following keyword arguments are also known:

at
A transform or list of transforms that are applied to the image.

if_all
An attribute or list of attributes. The displayable is only shown if all of these are showing.

if_any
An attribute or list of attributes. if not empty, the displayable is only shown if any of these are showing.

if_not
An attribute or list of attributes. The displayable is only shown if none of these are showing.

Other keyword arguments are interpreted as transform properties. If any are present, a transform is created that
wraps the image. (For example, pos=(100, 200) can be used to offset the image by 100 pixels horizontally and
200 vertically.)

If the image parameter is omitted or None, and the LayeredImage has been given the image_format parameter,
the image_format is used to generate an image filename.

Condition(condition, image, **kwargs)


This is used to represent a layer of an LayeredImage that is controlled by a condition. When the condition is true,
the layer is displayed. Otherwise, nothing is displayed.

condition
This should be a string giving a Python condition that determines if the layer is displayed.

image
If not None, this should be a displayable that is displayed when the condition is true.

if_all
An attribute or list of attributes. The condition is only evaluated if all of these are showing.

if_any
An attribute or list of attributes. If not empty, the condition is only evaluated if any of these are showing.

if_not
An attribute or list of attributes. The condition is only evaluated if none of these are showing.

at
A transform or list of transforms that are applied to the image.

Other keyword arguments are interpreted as transform properties. If any are present, a transform is created that
wraps the image. (For example, pos=(100, 200) can be used to offset the image by 100 pixels horizontally and
200 vertically.)

LayeredImage(attributes, at=[], name=None, image_format=None, format_function=None,


attribute_function=None, **kwargs)
This is an image-like object that, when shown with the proper set of attributes, shows a displayable created by
compositing together the displayables associated with those attribute.

attributes
This must be a list of Attribute objects. Each Attribute object reflects a displayable that may or may not be
displayed as part of the image. The items in this list are in back-to-front order, with the first item further from the
viewer and the last closest.

at
A transform or list of transforms that are applied to the displayable after it is parameterized.

name
The name of the layeredimage. This is used as part of the names of image components.

image_format
When a given image is a string, and this is supplied, the image name is interpolated into image_format to make
an image file. For example, "sprites/eileen/{image}.png" will look for the image in a subdirectory of sprites. (This
is not used by auto groups, which look for images and not image files.)

format_function
A function that is used instead of layeredimage.format_function to format the image information into a
displayable.

attribute_function
If not None, a function that's called with a set of attributes supplied to the image, and returns the set of
attributes used to select layers. This is called when determining the layers to display, after the attribute
themselves have been chosen. It can be used to express complex dependencies between attributes or select
attributes at random.

Additional keyword arguments may contain transform properties. If any are present, a transform is created that
wraps the result image. Remaining keyword arguments are passed to a Fixed that is created to hold the layer.
Unless explicitly overridden, xfit and yfit are set to true on the Fixed, which means it will shrink to the smallest size
that fits all of the layer images it is showing.

A LayeredImage is not a displayable, and can't be used in all the places a displayable can be used. This is
because it requires an image name (generally including image attributes) to be provided. As such, it should either
be displayed through a scene or show statement, or by an image name string used as a displayable.

layeredimage.format_function() is a function that is used to format attributes and displayables into image files.
It's supplied so you can see how it's documented, and the arguments it takes if you want to supply your own
format_function to replace it.

layeredimage.format_function(what, name, group, variant, attribute, image, image_format, **kwargs)


This is called to format the information about an attribute or condition into a displayable. This can be replaced by a
creator, but the new function should ignore unknown kwargs.

what
A string giving a description of the thing being formatted, which is used to create better error messages.

name
The name of the layeredimage.

group
The group of an attribute, None if not supplied or if it's part of a condition.

variant
The variant argument to the group, or None if it is not supplied.

attribute
The attribute itself.

image
Either a displayable or string.

image_format
The image_format argument of the LayeredImage.

If image is None, then name, group (if not None), variant (if not None), and attribute are combined with
underscores to create image, which will then be a string.

If images is a string, and image_format is not None, image is formatted into the string to get the final displayable.

So if name is "eileen", group is "expression", and attribute is "happy", image would be set to
"eileen_expression_happy". If image_format is "images/{image}.png", the final image Ren'Py finds is
"images/eileen_expression_happy.png". But note that it would have found the same image without the format
argument.

Proxying Layered Images


Sometimes, it's necessary to proxy a layered image, to use the same layered image in multiple places. One reason
for this would be to have the same sprite at multiple sizes, while another would be to use it as a side image.

The LayeredImageProxy() object does this, taking one layered image and duplicating it somewhere else.

For example:

image dupe = LayeredImageProxy("augustina")

creates a duplicate of the image that can be displayed independently. This also takes a transform argument that
makes it useful to position a side image, like this:

image side augustina = LayeredImageProxy("augustina", Transform(crop=(0, 0, 362, 362), xoffset=-80)

LayeredImageProxy(name, transform=None)
This is an image-like object that proxies attributes passed to it to another layered image.

name
A string giving the name of the layered image to proxy to.

transform
If given, a transform or list of transforms that are applied to the image after it has been proxied.

© Copyright 2012-2021, Tom Rothamel. Back to top


Created using Sphinx 1.6.6.

You might also like