Written tutorials

Animating An Image In Ren’Py “Automatically”

June 22, 2022

Sometimes when I have time, I like to go through the Ren’Py sub-reddit section to see what people need help with and answer one or two of them if I can. Recently, someone was asking a question I’m sure many have had at one point in their visual novel game-development, especially if you deal with animations a lot.

The question was more or less: Can I animate an image without having to manually type out each image path that should be used?

You can read the reddit post here: https://www.reddit.com/r/RenPy/comments/vhgn1h/creating_an_animation_using_a_for_loop/.

So one of the common ways of animating an image is by using an ATL block together with an image statement or show statement, like in the example below.

image my_animation:
    "frame-1"
    pause 1.0
    "frame-2"
    pause 1.0
    ...

This is easy to do when you have fewer images to work with, but once you start working with a larger number of frames, having to type them out one after the other can be quite tiresome.

So one solution, which I also gave in the reddit post, is to use a transform function and a custom variable. Have a look at the example below.

init python:
    def next_frame(t, st, at):
        global animation_frame
        if animation_frame < 4:
            animation_frame += 1
        else:
            animation_frame = 1
image cursor:
    xalign 0.5
    yalign 0.5
    "cursor-[animation_frame].png"
    pause 1.0
    function next_frame
    repeat
label start:
    $animation_frame = 1
    show cursor
    "example animation"
    return

PSST! You can download this complete test-project to test yourself.

In the above example, I have 4 different cursor images that should be used in an animation. I want these frames to be switched between every second. So let’s go through the code bit by bit to see how this works.

label start:
    $animation_frame = 1
    show cursor
    ...

First we have the start label containing a variable we call “animation_frame“. This variable keeps track of the current frame that the animation should be displaying. As the animation should start at the first frame, we set this to the initial value of 1. Then we show the cursor image that we have defined above the start label.

image cursor:
    xalign 0.5
    yalign 0.5
    "cursor-[animation_frame].png"
    pause 1.0
    function next_frame
    repeat

The image statement contains an ATL block as we can see, and inside this ATL block, we have defined only one image instead of all 4. The path to this image uses a placeholder variable, namely the “animation_frame” variable. This makes sure that the image being used is dependent on what the animation_frame variable’s value is set to. Since we start with 1, the first frame of the animation is used (cursor-1.png).

After the image path has been defined, we add a pause statement which pauses the animation for 1 second, then we call a transform function that is called “next_frame“.

def next_frame(t, st, at):
        global animation_frame
        if animation_frame < 4:
            animation_frame += 1
        else:
            animation_frame = 1

The next_frame function’s only purpose is to add 1 to the animation_frame variable to make the animation switch to the next frame. So we’re not really using it the way it was intended to be used (which is to add custom python code to transform the image in some way), but it works for our purpose.

if animation_frame < 4:
    animation_frame += 1

We only add 1 to the variable as long as the variables value is less than 4, because we only have 4 frames. Thus we have an if statement that check if the animation_frame variable is less than 4, and then increment the variables value.

else:
    animation_frame = 1

If it’s not less than 4 anymore, then it means it must be 4 now, the last frame. In such a case, the else statement runs instead and simply makes sure that the variable is set to 1 again. The next time the ATL block runs (which it will because we added the repeat statement), the image is set back to the first frame, and the entire cycle begins again.

Therefore, if you don’t want the animation to loop, you simply remove or comment out the else statement in the transform function. This will cause the animation to stop at 4 and not switch back to the first one. Do note that the repeat statement should still be present as it’s responsibility, in this specific case, is simply to make sure the transform function keeps running to switch to another frame. It does not cause the animation in itself to loop in this case, even though it keeps the ATL block running indefinitely.

Got questions or want to say thanks for the tutorial? Leave a comment below! 😁

    Leave a Reply