Dynamic blinking animation

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
User avatar
Posts: 4
Joined: Wed Jul 19, 2023 12:12 pm

Dynamic blinking animation

#1 Post by Starberries »

Long story short, I wanted my characters to have blink animations and got tired of having to copy-paste the animation code block for every character. I also wanted them to have a random duration between blinks to make it look more organic. Partial solution was to adapt this code which uses only eyes open/eyes closed frames. This was a lot of brute force and trial and error from an intermediate coder, so any critiques and suggestions on cleanup are welcome. But I figured this might be helpful to someone else out there.

Code: Select all

init python:
    # The dynamic_blink class is the core code for the animation itself
    class dynamic_blink(renpy.display.layout.DynamicDisplayable):
        def __init__(self, *args, **kwargs):
            self.current_image_index = 0
            self.blink_st = -1.0 # arbitrary number to force normal start

            self.used_images = args
            kwargs.update( {
                '_predict_function' : self.predict_images } )
            super(dynamic_blink, self).__init__( self.get_blink_image, *args, **kwargs )

        def get_blink_image(self, st, at, *args, **kwargs):
            # if time to change image
            if st > self.blink_st:
                self.current_image_index = (self.current_image_index + 1) % len(args)
                # set the next change time based on current image
                if self.current_image_index == 0: # back to open eyes, wait longer
                    self.blink_st = st + 2.0 + ( renpy.python.rng.random() * 5.0 )
                else: # other frames, shorter delay
                    self.blink_st = st + 0.1 
            return args[self.current_image_index], 0

        def predict_images(self):
            return self.used_images

    # The blinking_animation function will take the character's name as a parameter and use it to automatically fill in the file names
    # This assumes your files are organizes and named as they are below. Alter the strings as needed to fit your organization methods
    def blinking_animation(character):
        return dynamic_blink(
            # Define frames for the animation here. It should be able to take more or less frames
            "chars/{}/{}_eyes_open.png".format(character, character),
            "chars/{}/{}_eyes_half.png".format(character, character),
            "chars/{}/{}_eyes_closed.png".format(character, character),
            "chars/{}/{}_eyes_half.png".format(character, character)

image eileen_blinking = blinking_animation("eileen")
Basically, the code works in two parts. The first is the class dynamic_blink, which is the bulk of the code that handles the animation itself and the random time between blinks/animation loops. By default, the timer should be 2~7 seconds unless I'm worse at reading math equations than I thought.

The last Python chunk the blinking_animation function, takes "character", as in probably the character's name, as a parameter and adds it into a template that matches my folder paths and file naming convention, so that I don't have to write out the entire file name repeatedly when defining the animations. As I mentioned in the code comments, you should be able to add or remove frames without issue, you don't have to stick to the same number of frames I use.

As a bonus, I use the following alternate function in the same init python block for blink animations with filenames that are exceptions to my usual format.

Code: Select all

    def blinking_anim_special(character,*frames):
        return dynamic_blink(
            "chars/{}/{}.png".format(character, frames[0]),
            "chars/{}/{}.png".format(character, frames[1]),
            "chars/{}/{}.png".format(character, frames[2]),
            "chars/{}/{}.png".format(character, frames[1]),

image eileen_red_blinking = blinking_anim_special("eileen","eileen_eyes_open_red","eileen_eyes_half_red","eileen_eyes_closed")
Note that this does require the majority of the filename written in the parameters, but I don't mind it in this case since the number of animations that use this function is a pretty tiny handful, it's just my lazy way of handling outliers. Feel free to adapt this stuff however you need for whatever blinks or other animations you have going on.

Post Reply

Who is online

Users browsing this forum: No registered users