Transforms

Transforms are used in order to turn a displayable into another displayable. There are several kinds of transforms, and various ways to create them. The built-in transforms are used to control where an image is placed on the screen, while user-defined transforms can cause more complex effects, like motion, zoom, rotation, up to complex color effects.

Transforms can be applied to images by passing them to the at clause of the show or scene statements. The following applies the right transform to the eileen happy image:

show eileen happy at right

Multiple transforms can be applied by separating them with commas. These transforms are applied from left-to-right.

show eileen happy at halfsize, right

Applying transforms to displayables in Python

There are several ways to apply transform t to displayable d in Python:

  1. The most universal and most recommended way is At(d, t) (see below). It works with all transforms.

  2. d(child=t) works with all ATL transforms.

  3. t(d) works with all Python transforms, as well as with ATL transforms that don't have any positional parameters.

At(d, *args)

Given a displayable d, applies each of the transforms in args to it. The transforms are applied in left-to-right order, so that the outermost transform is the rightmost argument.

transform birds_transform:
    xpos -200
    linear 10 xpos 800
    pause 20
    repeat

image birds = At("birds.png", birds_transform)

Note

The resulting value may not be able to be displayed, if there remains parameters of the transform that have not been given a value, as can be the case with transforms defined using the Transform Statement.

Note

The resulting value may still be a transform that can be applied to yet another displayable (overriding its previous child) ; that's the case with ATL transforms which are still usable as transforms even when having their child set.

Built-in Transforms

Ren'Py ships with a number of transforms defined by default. These transforms position things on the screen. Here's a depiction of where each built-in transform will position an image.

             +-----------------------------------------------------------+
             |topleft, reset               top                   topright|
             |                                                           |
             |                                                           |
             |                                                           |
             |                                                           |
             |                          truecenter                       |
             |                                                           |
             |                                                           |
             |                                                           |
             |                                                           |
offscreenleft|left                   center, default                right|offscreenright
             +-----------------------------------------------------------+

The offscreenleft and offscreenright transforms position images off the edges of the screen. These transforms can be used to move things off the screen (remember to hide them afterwards, to ensure that they do not consume resources).

The transforms are:

center

Centers horizontally, and aligns to the bottom of the screen.

default

Centers horizontally, and aligns to the bottom of the screen. This can be redefined via config.default_transform to change the default placement of images shown with the show or scene statements.

left

Aligns to the bottom-left corner of the screen.

offscreenleft

Places the displayable off the left side of the screen, aligned to the bottom of the screen.

offscreenright

Places the displayable off the left side of the screen, aligned to the bottom of the screen.

reset

Resets the transform. Places the displayable in the top-left corner of the screen, and also eliminates any zoom, rotation, or other effects.

right

Aligns to the bottom-right corner of the screen.

top

Centers horizontally, and aligns to the top of the screen.

topleft

Aligns to the top-left corner of the screen.

topright

Aligns to the top-right corner of the screen.

truecenter

Centers both horizontally and vertically.

ATL - Animation and Transformation Language

The Animation and Transformation Language (ATL) is a high-level language which can create animations, move displayables across the screen, set their position, apply transformations, and more. These can be changed over time, and in response to events.

ATL transform objects, which are created using the Transform Statement down below, are displayables and can be used as such (even though they will be transparent when their child displayable is not set) : they can be passed to a screen's Add element, or to a Show expression statement, or to the renpy.show() function.

Ren'Py script statements

There are three Ren'Py script statements which can include ATL code.

Transform Statement

The transform statement creates a new transform. The syntax is:

atl_transform ::=  "transform" qualname ( "(" parameters ")" )? ":"
                      atl_block

The transform statement is run at init time. The transform may take a list of parameters, which works much the same way as a Python function definition, except that several kinds of parameters are currently forbidden, though they may be allowed in the future:

  1. Positional-only parameters

  2. Keyword-only parameters without a default value

  3. Variadic positional parameters (*args)

  4. Variadic keyword parameters (**kwargs)

The created object cannot be used as a transform until and unless all its parameters have been given a value.

See also : ATL curry and partial parameter passing

qualname, the name of the transform, must be a set of dot-separated Python identifiers. The transform created by the ATL block will be bound to that name, within the provided store if one was provided.

transform left_to_right:
    xalign 0.
    linear 2 xalign 1.
    repeat

transform ariana.left:
    xalign .3

transform animated_ariana_disp:
    "ariana"
    pause 1.
    "ariana_reverse"
    pause 1.
    repeat

The created object is both a transform and a displayable, but as opposed to the image statement, it is created as a variable (or a constant), rather than in the namespace of images.

Image Statement with ATL Block

The second way to include ATL code in a script is as part of an image statement. As its inline counterpart, it binds an image name (which may contain spaces) to the given transform. As there is no way to supply with parameters, it's only useful if the transform defines an animation. The syntax is:

atl_image ::=  "image" image_name ":"
                  atl_block
image animated_ariana_img:
    "ariana"
    pause 1.
    "ariana_reverse"
    pause 1.
    repeat

Scene and Show Statements with ATL Block

The final way to use ATL is as part of a show or scene statement. This wraps the image that's being shown inside an ATL transformation which is created on the fly and applied to the image. The syntax is:

atl_show  ::=  stmt_show ":"
                  atl_block
atl_scene ::=  stmt_scene ":"
                  atl_block
show eileen happy:
    xalign 1.

scene bg washington:
    zoom 2.

ATL Syntax and Statements

ATL statements may be inline, or make up a block within the ATL block in which it is written. With some exceptions described in the relevant statements, the statements in an ATL block are executed in order, from top to bottom.

If an ATL statement requires an expression to be evaluated, such evaluation occurs when the transform is first executed (that is when using a show statement, or displaying the transform as part of a screen), and not when the particular ATL statement is executed.

The following are the ATL statements.

Inline Contains Statement

The inline contains statement takes a single expression evaluating to a displayable.

atl_contains ::=  "contains" simple_expression

This statement sets (or replaces) the child of the current ATL transform to the value of the expression, making it useful for animation.

transform an_animation:
    "1.png"
    pause 2
    "2.png"
    pause 2
    repeat

image move_an_animation:
    contains an_animation

    # If we didn't use contains, we'd still be looping
    # and would never reach here.
    xalign 0.0
    linear 1.0 yalign 1.0

The Displayable Statement is less explicit and bears ambiguity with the transform expression statement, but it allows for a transition to be used for replacing the child. This statement can be particularly useful when an ATL transform wishes to contain, rather than include, a second ATL transform.

Number Statement

The number statement consists of a simple expression evaluating to an integer or floating-point number. It can optionally be preceded by the keyword "pause".

atl_number ::=  "pause"? simple_expression

It is used as a number of seconds to pause execution for.

image atl example:
    # Displays logo_base.png
    contains "logo_base.png"

    # Pause for 1.0 seconds.
    pause 1.0

    # Show logo_bw.png, with a dissolve.
    "logo_bw.png" with Dissolve(0.5, alpha=True)

    # Pause for 3 seconds
    3

    repeat

Properties Statement

This statement sets one or more transform properties to a new value.

atl_properties ::=  atl_property+
atl_property ::=  transform_property simple_expression

The statement first gives a series (at least one) of property names, each followed by the new value to set it to. See List of Transform Properties for a list of transform properties, their meaning and the values they take.

transform rightoid:
    xalign .9

transform ariana.left:
    xanchor .3 xpos 100

Interpolation Statement

The interpolation statement is the main way of getting smoothly animated transformations.

atl_interp ::=  ((warper simple_expression) | ("warp" simple_expression simple_expression)) (atl_interp_target+ | (":"
                   atl_interp_target+ ))
atl_interp_target ::=  (atl_property+ ("knot" simple_expression)* )
                       | atl_transform_expression
                       | "clockwise"
                       | "counterclockwise"
                       | ("circles" simple_expression)

Some sample interpolations:

show logo base:
    # Show the logo at the upper right side of the screen.
    xalign 1.0 yalign 0.0

    # Take 1.0 seconds to move things back to the left.
    linear 1.0 xalign 0.0

    # Take 1.0 seconds to move things to the location specified in the
    # truecenter transform. Use the ease warper to do this.
    ease 1.0 truecenter

    # Set the location to circle around.
    anchor (0.5, 0.5)

    # Use circular motion to bring us to spiral out to the top of
    # the screen. Take 2 seconds to do so.
    linear 2.0 yalign 0.0 clockwise circles 3

    # Use a spline motion to move us around the screen.
    linear 2.0 align (0.5, 1.0) knot (0.0, .33) knot (1.0, .66)

    # Changes xalign and yalign at the same time.
    linear 2.0 xalign 1.0 yalign 1.0

    # The same thing, using a block.
    linear 2.0:
        xalign 1.0
        yalign 1.0

The first part of the interpolation is used to select a function that time-warps the interpolation. That means, a function that maps linear time to non-linear time, see Warpers for more information about that. Selecting a warper can either be done by giving the name of a registered warper, or by giving the keyword "warp" followed by an expression giving a warping function.

In either case, it's followed by a number giving the number of seconds the interpolation should take.

transform builtin_warper:
    xpos 0
    ease 5 xpos 520

init python:
    def my_warper(t):
        return t**4.4

define my_warpers = [my_warper]

transform accessed_as_function:
    xpos 0
    warp my_warpers[0] 5 xpos 520
    warp my_warper 3 xpos 100

The interpolation will persist for the given amount of time, and at least one frame.

When Transform Properties are given, the value each is given is the value it will be set to at the end of the interpolation statement. This can be tweaked in several ways:

  • If the value is followed by one or more knots, then spline motion is used. The starting point is the value of the property at the start of the interpolation, the end point is the given value, and the knots are used to control the spline. A quadratic curve is used for a single knot, Bezier is used when there are two and Catmull-Rom is used for three or more knots. In the former two cases, the knot or knots are simply control nodes. For Catmull-Rom, the first and last knot are control nodes (often outside the displayed path) and the other knots are points the path passes through.

  • If the interpolation statement contains a "clockwise" or "counterclockwise" clause, circular motion is used. In that case, Ren'Py will compare the start and end locations (which are set by pos, align, angle and radius, ...) and figure out the polar coordinate center (which is around). Ren'Py will then compute the number of degrees it will take to go from the start angle to the end angle, in the specified direction of rotation. If the circles clause is given, Ren'Py will ensure that the appropriate number of circles will be made.

  • Otherwise, the value is linearly interpolated between the start and end locations.

It is also possible to interpolate a Transform Expression Statement, which should in this case be an ATL transform containing only a single properties statement. The properties from the transform will be processed as if they were written directly in this interpolation.

A warper may be followed by a colon (:). In that case, it may be followed by one or more lines, in an indented block, containing the clauses described above. This lets you break an interpolation of many different things up into several lines.

Pass Statement

atl_pass ::=  "pass"

The pass statement is a simple statement that causes nothing to happen : a no-op. This can be used when there's a desire to separate statements, like when two sets of choice statements (see below) would otherwise be back-to-back. It can also be useful when the syntax requires a block to be created but you need it to be empty, for example to make one of the choice blocks not do anything.

Repeat Statement

The repeat statement is a simple statement that causes the block containing it to resume execution from the beginning.

atl_repeat ::=  "repeat" (simple_expression)?

If the expression is present, then it is evaluated to give an integer number of times the block will execute. (So a block ending with repeat 2 will execute at most twice in total, and repeat 1 does not repeat.)

The repeat statement must be the last statement in a block:

show logo base:
    xalign 0.0
    linear 1.0 xalign 1.0
    linear 1.0 xalign 0.0
    repeat

Block Statement

The block statement simply contains a block of ATL statements.

atl_block_stmt ::=  "block" ":"
                         atl_block

This can be used to group statements that will repeat:

show logo base:
    alpha 0.0 xalign 0.0 yalign 0.0
    linear 1.0 alpha 1.0

    block:
        linear 1.0 xalign 1.0
        linear 1.0 xalign 0.0
        repeat

Parallel Statement

The parallel statement defines a set of ATL blocks to execute in parallel.

atl_parallel ::=  ("parallel" ":"
                     atl_block)+

Parallel statements are greedily grouped into a parallel set when more than one parallel block appears consecutively in a block. The set of all parallel blocks are then executed simultaneously. The parallel statement terminates when the last block terminates.

The blocks within a set should be independent of each other, and manipulate different Transform Properties. When two blocks change the same property, the result is undefined.

show logo base:
    parallel:
        xalign 0.0
        linear 1.3 xalign 1.0
        linear 1.3 xalign 0.0
        repeat
    parallel:
        yalign 0.0
        linear 1.6 yalign 1.0
        linear 1.6 yalign 0.0
        repeat

Choice Statement

The choice statement defines one of a set of potential choices. Ren'Py will pick one of the choices in the set, and execute the ATL block associated with it, and then continue execution after the last choice in the choice set.

atl_choice ::=  ("choice" (simple_expression)? ":"
                    atl_block)+

Choice statements are greedily grouped into a choice set when more than one choice statement appears consecutively in a block. If the simple_expression is supplied, it is a floating-point weight given to that block, otherwise 1.0 is assumed.

image eileen random:
    choice:
        "eileen happy"
    choice:
        "eileen vhappy"
    choice:
        "eileen concerned"

    pause 1.0
    repeat

The pass statement can be useful in order to break several sets of choice blocks into several choice statements, or to make an empty choice block.

Animation Statement

The animation statement must be the first statement in an ATL block, and tells Ren'Py that the block uses the animation timebase.

atl_animation ::=  "animation"

As compared to the normal showing timebase, the animation timebase starts when an image or screen with the same tag is shown. This is generally used to have one image replaced by a second one at the same apparent time. For example:

image eileen happy moving:
    animation
    "eileen happy"
    xalign 0.0
    linear 5.0 xalign 1.0
    repeat

image eileen vhappy moving:
    animation
    "eileen vhappy"
    xalign 0.0
    linear 5.0 xalign 1.0
    repeat

label start:
    show eileen happy moving
    pause
    show eileen vhappy moving
    pause

This example will cause Eileen to change expression when the first pause finishes, but will not cause her position to change, as both animations share the same animation time, and hence will place her sprite in the same place. Without the animation statement, the position would reset when the player clicks.

On Statement

The on statement defines an event handler.

atl_on ::=  "on" name ("," name)* ":"
                 atl_block

on blocks are greedily grouped into a single statement. On statement can handle a single event name, or a comma-separated list of event names.

This statement is used to handle events. When an event is handled, handling of any other event ends and handing of the new event immediately starts. When an event handler ends without another event occurring, the default event is produced (unless the default event is already being handled).

Execution of the on statement will never naturally end. (But it can be ended by the time statement, or an enclosing event handler.)

See the event statement for a way to produce arbitrary events, and see External events for a list of naturally-produced events.

show logo base:
    on show:
        alpha 0.0
        linear .5 alpha 1.0
    on hide:
        linear .5 alpha 0.0

transform pulse_button:
    on hover, idle:
        linear .25 zoom 1.25
        linear .25 zoom 1.0

Transform Expression Statement

This statement includes another ATL transform as part of the current ATL block.

atl_transform_expression ::=  simple_expression

This only applies if the ATL transform has not been supplied a child (see the top of the page for how to do that), otherwise it will be interpreted as a Displayable Statement. The contents of the provided ATL transform are included at the location of this statement.

transform move_right:
    linear 1.0 xalign 1.0

image atl example:
    # Display logo_base.png
    "logo_base.png"

    # Run the move_right transform.
    move_right

Displayable Statement

The displayable statement consists of a simple Python expression evaluating to a displayable, optionally followed by a with clause containing a second simple expression.

atl_displayable ::=  simple_expression ("with" simple_expression)?

It is used to set or replace the child of the transform when the statement executes.

If a with clause is present, the second expression is evaluated as a transition, and the transition is applied between the old child and the new child. Be careful in that not all transitions will work in this situation, notably Dict Transitions and move- and ease- transitions.

image atl example:
    # Displays logo_base.png
    "logo_base.png"

    # Pause for 1.0 seconds.
    1.0

    # Show logo_bw.png, with a dissolve.
    "logo_bw.png" with Dissolve(0.5, alpha=True)

Warning

If passing any child-less transform is pointless as it will make the transform transparent and ineffective, passing child-less ATL transforms may be interpreted as a Transform Expression Statement, which will yield different results.

If the expression evaluates to an ATL transform with a child, the execution of this ATL block will only continue after the includee's ATL code runs.

Contains Block Statement

The contains block, like its inline counterpart, sets the child of the transform but in a different way.

atl_counts ::=  "contains" ":"
                   atl_block

One or more contains blocks will be greedily grouped together inside a single contains statement, wrapped inside a Fixed(), and set as the child of the transform.

Each block should define a displayable to use, otherwise an error will occur. The contains statement executes instantaneously, without waiting for the children to complete.

image test double:
    contains:
        "logo.png"
        xalign 0.0
        linear 1.0 xalign 1.0
        repeat

    contains:
        "logo.png"
        xalign 1.0
        linear 1.0 xalign 0.0
        repeat

Function Statement

The function statement allows ATL to use Python code.

atl_function ::=  "function" simple_expression

The functions have the same signature as those used with Transform():

  • The first argument is a transform object. Transform Properties can be set as attributes on this object.

  • The second argument is the shown timebase, the number of seconds since the function began executing.

  • The third argument is the animation timebase, which is the number of seconds something with the same tag has been on the screen.

  • If the function returns a number, it will be called again after that number of seconds has elapsed. (0 seconds means to call the function as soon as possible.) If the function returns None, control will pass to the next ATL statement.

This function should not have side effects other than changing the transform object in the first argument, and may be called at any time with any value as part of prediction.

Note that function is not a transform property, and that it doesn't have the exact same behavior as Transform()'s function parameter.

init python:
    def slide_vibrate(trans, st, at, /):
        if st > 1.0:
            trans.xalign = 1.0
            trans.yoffset = 0
            return None
        else:
            trans.xalign = st
            trans.yoffset = random.randrange(-10, 11)
            return 0

label start:
    show logo base:
        function slide_vibrate
        pause 1.0
        repeat

Time Statement

The time statement is a control statement.

atl_time ::=  "time" simple_expression

It contains a single expression, which is evaluated to give a time expressed as seconds from the start of execution of the containing block. When the time given in the statement is reached, the following statement begins to execute. This transfer of control occurs even if a previous statement is still executing, and causes any such prior statement to immediately terminate.

Time statements are implicitly preceded by a pause statement with an infinite time. This means that if control would otherwise reach the time statement, it waits until the time statement would take control.

When there are multiple time statements in a block, they must strictly increase in order.

image backgrounds:
    "bg band"
    xoffset 0
    block:
        linear 1 xoffset 10
        linear 1 xoffset 0
        repeat # control would never exit this block

    time 2.0
    xoffset 0
    "bg whitehouse"

    time 4.0
    "bg washington"

Event Statement

The event statement is a simple statement that causes an event with the given name to be produced.

atl_event ::=  "event" name

When an event is produced inside a block, the block is checked to see if an event handler for the given name exists. If it does, control is transferred to the event handler. Otherwise, the event propagates to any containing event handler.

External events

The following events are triggered automatically within an ATL transform:

start

A pseudo-event, triggered on entering an on statement, if no event of higher priority has happened.

show

Triggered when the transform is shown using the show or scene statement, and no image with the given tag exists.

replace

Triggered when transform is shown using the show statement, replacing an image with the given tag.

hide

Triggered when the transform is hidden using the hide statement or its Python equivalent.

Note that this isn't triggered when the transform is eliminated via the Scene Statement or exiting the Contexts it exists in, such as when exiting the game menu.

replaced

Triggered when the transform is replaced by another. The image will not actually hide until the ATL block finishes.

hover, idle, selected_hover, selected_idle, insensitive, selected_insensitive

Triggered when a button containing this transform, or a button contained by this transform, enters the named state.

ATL curry and partial parameter passing

An ATL transform defined using the Transform Statement can have its parameters set at different times. When calling an ATL transform like a function, the resulting value is still a transform, and the parameters that were passed a value are treated as though the value is the new default value of the parameter.

For example:

transform screamer(child, screamee, wait_time=2, flash_time=.1):
    child
    pause wait_time
    screamee
    pause flash_time
    child

# doing this doesn't raise an error (it would if it were a Python function)
define shorter_screamer = screamer(wait_time=1)

define eileen_excited_screamer = screamer(screamee="eileen excited", flash_time=.2)

label start:
    show hhannahh happy at screamer(screamee="hhannahh surprised", wait_time=1.5)
    "Here is one way"

    show eileen vhappy at eileen_excited_screamer
    "Here is another"

    show patricia sad at eileen_excited_screamer(screamee="patricia wow")
    "And you can also do this"

Note that the shorter_screamer transform, just as the screamer transform, cannot be used directly like show eileen at screamer, since their screamee parameters do not have a value.

Note also that, like labels and screens, the default values of the parameters of a transform directly created by the Transform Statement will be evaluated at the time when the transform is called, not at the time when it is defined.

However, the transform resulting from calling another transform (such as shorter_screamer in the example above, or also the transform applied to patricia) has all the default values of its parameters already evaluated, whether they come from the evaluation of the default values in the original transform (such as shorter_screamer's flash_time parameter, or patricia's transform's wait_time parameter), or from values passed to it in a call earlier in the line (such as shorter_screamer's wait_time parameter, or patricia's transform's screamee and flash_time parameters).

Special Child Parameter

If an ATL transform has a parameter named "child" and that parameter receives a value, regardless of the kind of parameter or the way it receives a value (by a positional argument or by keyword, and even if the parameter is positional-only or keyword-only, and defaulted or required), then in parallel the child of the transform is set to the value of the parameter.

Note that the default value of the parameter doesn't count, the parameter has to receive a value from the outside.

Conversely, when that ATL transform is used as a transform, the child= keyword argument will be passed, and so in addition to setting the child, if a parameter is there to receive it (excluding positional-only parameters, since it is passed by keyword), it will have the child's value when the transform executes.

For example, this enables to swap between the supplied child and another displayable:

transform lucy_jump_scare(child):
    # the child is implicitly set as the child of the transform
    pause 5

    # Jump scare
    "lucy mad"
    pause .2

    # Go back to the original child
    child

It can also be used to place the original child inside a contains block:

transform marquee(width, height=1.0, duration=2.0, child=None):
    xcenter 0.5
    ycenter 0.5

    crop (0, 0, 0.5, 500)

    contains:
        child
        xanchor 0.0 xpos 1.0
        linear duration xanchor 1.0 xpos 0.0

The old_widget and new_widget keyword-able parameters (meaning that they should not be positional-only) have a special use as part of ATL Transitions.

Warpers

A warper is a function that can change the amount of time an interpolation statement considers to have elapsed. They are defined as functions from t to t', where t and t' are floating point numbers, with t ranging from 0.0 to 1.0 over the given amount of time. (If the statement has 0 duration, then t is 1.0 when it runs.) t' should start at 0.0 and end at 1.0, but can be greater or less. The following warpers are defined by default.

pause

Pause, then jump to the new value. If t == 1.0, t' = 1.0. Otherwise, t' = 0.0.

linear

Linear interpolation. t' = t

ease

Start slow, speed up, then slow down. t' = .5 - math.cos(math.pi * t) / 2.0

easein

Start fast, then slow down. t' = math.cos((1.0 - t) * math.pi / 2.0)

easeout

Start slow, then speed up. t' = 1.0 - math.cos(t * math.pi / 2.0)

In addition, most of Robert Penner's easing functions are supported. To make the names match those above, the functions have been renamed somewhat. Graphs of these standard functions can be found at http://www.easings.net/.

Ren'Py Name

easings.net Name

ease_back

easeInOut_back

ease_bounce

easeInOut_bounce

ease_circ

easeInOut_circ

ease_cubic

easeInOut_cubic

ease_elastic

easeInOut_elastic

ease_expo

easeInOut_expo

ease_quad

easeInOut_quad

ease_quart

easeInOut_quart

ease_quint

easeInOut_quint

easein_back

easeOut_back

easein_bounce

easeOut_bounce

easein_circ

easeOut_circ

easein_cubic

easeOut_cubic

easein_elastic

easeOut_elastic

easein_expo

easeOut_expo

easein_quad

easeOut_quad

easein_quart

easeOut_quart

easein_quint

easeOut_quint

easeout_back

easeIn_back

easeout_bounce

easeIn_bounce

easeout_circ

easeIn_circ

easeout_cubic

easeIn_cubic

easeout_elastic

easeIn_elastic

easeout_expo

easeIn_expo

easeout_quad

easeIn_quad

easeout_quart

easeIn_quart

easeout_quint

easeIn_quint

These warpers can be accessed in the _warper read-only module, which contains the functions listed above. It is useful for things in Ren'Py which take a time-warping function, such as Dissolve(), which you can use like:

with Dissolve(1, time_warp=_warper.easein_quad)

New warpers can be defined using the renpy.atl_warper decorator, in a python early block. It should be placed in a file that is parsed before any file that uses the warper. This looks like:

python early hide:

    @renpy.atl_warper
    def linear(t):
        return t

Replacing Transforms

When an ATL transform, a built-in transform or a transform defined using the Transform class is replaced by another transform of these categories, the properties of the outgoing transform are inherited by the incoming transform. That inheritance doesn't apply for other kinds of transforms.

When the show statement has multiple transforms in the at list, the transforms are matched from last to first, until one list runs out. For example:

show eileen happy at a, b, c
"Dialogue !"
show eileen happy at d, e

The c transform will be replaced by e, the b transform will be replaced by d, and nothing replaces the a transform.

At the moment of replacement, if both transforms are of suitable kinds, the values of the properties of the old transform are copied to the new transform. If the old transform was animated, the current intermediate value is inherited. For example:

transform bounce:
    linear 3.0 xalign 1.0
    linear 3.0 xalign 0.0
    repeat

transform headright:
    linear 15 xalign 1.0

label example:
    show eileen happy at bounce
    pause
    show eileen happy at headright
    pause

In this example, the image will bounce from left to right and back until the player clicks. When that happens, the xalign property of the bounce transform will be used to initialize the xalign property of the headright transform, and so the image will move from where it was when the player first clicked.

The position properties (xpos, ypos, xanchor, yanchor, and properties setting them such as xalign or radius/ angle) have a special rule for inheritance : a value set in the child will override a value set in the parent. That is because a displayable may have only one position, and a position that is actively set takes precedence.

Finally, when a show statement does not include an at clause, the same transforms are used, so no inheritance is necessary. To reset all transform properties, hide and then show the displayable again. To break the animations applied to a displayable (but keep the position), you can use:

show eileen happy at some_animation
"Wow, so this is what antigravity feels like !"

show eileen:
    pass
"But I'm happy when it settles down."

The Transform Class

One equivalent to to the simplest ATL transforms is the Transform class.

class Transform(child=None, function=None, **properties)

Creates a transform which applies operations such as cropping, rotation, scaling or alpha-blending to its child. A transform object has fields corresponding to the transform properties, which it applies to its child.

child

The child the transform applies to.

function(trans: Transform, st: float, at: float, /) int | None

If not None, this function will be called when the transform is rendered, with three positional arguments:

  • The transform object.

  • The shown timebase, in seconds.

  • The animation timebase, in seconds.

The function should return a delay, in seconds, after which it will be called again, or None to be called again at the start of the next interaction.

This function should not have side effects other than changing the Transform object in the first argument, and may be called at any time with any value as a part of prediction.

Additional keyword arguments are values that transform properties are set to. These particular transform properties will be set each time the transform is drawn, and so may not be changed after the Transform object is created. Fields corresponding to other transform properties, however, can be set and changed afterwards, either within the function passed as the function parameter, or immediately before calling the update() method.

hide_request

This attribute is set to true when the function is called, to indicate that the transform is being hidden.

hide_response

If hide_request is true, this can be set to false to prevent the transform from being hidden.

set_child(child)

Call this method with a new child to change the child of this transform.

update()

This should be called when a transform property field is updated outside of the function passed as the function argument, to ensure that the change takes effect.

Callables as transforms

Finally, simple Python callables can be used as transforms. These callables should take a single displayable as an argument, and return a new Displayable. For example:

init python:

    # this transform uses the right and left transforms
    def right_or_left(d):
        if switch:
            return At(d, right)
        else:
            return At(d, left)

That means that certain builtins which take a displayable and return a displayable, such as Flatten(), are also transforms and can be used as such.