Move Your Sprites! - October 2025 Devlog


Duhop here, back again with another devlog for October!

I’ve recently been working on putting some of our new sprite assets to use, so this month I want to talk about a surprisingly simple part of the process that really brings the characters to life.

Also because seeing the before and after of this step really makes me happy :D

A lot of this might seem obvious to an experienced developer, but I think the most useful way for me to write devlogs on topics like this is probably just to explain everything I wish I knew when I first went into it myself.

So! Here’s a quick look at a short, unfinished scene that I recently did the character expressions for:

What sticks out to you at first glance?

If you’re anything like me, probably the fact that Jenna’s sprite doesn’t move an inch throughout the entire scene. Obviously it looks a whole lot better in real time, but that utter lack of movement is still played out to the viewer, subconsciously boring the visual portions of their brains half to death as the scene remains static, expressions grow repetitive, and the dialogue box becomes their only source of engagement.

It is supposed to be a visual novel, after all.

Well, it’s easy enough to identify lack of movement as a problem in a scene, but how do you fix it? Interspersing CGs (ideally with dynamic camera movement) or swapping to alternate backgrounds angles are a couple visually fantastic ways to alleviate it, but those are expensive and don’t actually address the base problem of not doing enough with your sprites.

Thankfully, the brain-stimulating movement that VN players crave is only ever a few lines of code away! Ren’Py’s transforms are an incredibly powerful, easy to use, and often underutilized tool for adding much-needed liveliness to your scenes. You might be using them for some things already, but I’m here to tell you that your creativity is the limit when it comes to making visuals more dynamic with transforms, and you should probably use them more.

Step 1: Visualize.

Read through your scene again, and focus on the portions most lacking in on-screen movement. Are there any moments where movement is implied or described through writing/dialogue, but hasn’t been depicted visually? If not, are there any simple movements you think it would be natural for the characters to make, even if they aren’t implied in the writing at all?

Do you have a movement in mind? Cool, keep it there! For the purposes of this devlog, I’m just gonna pretend it’s a character getting bopped on the top of her head.

Now, picture how the movement would look like in reality, and try your best to imagine how that movement would simplify down to a static 2D sprite. It might not come easily if you haven’t done much of this before, but I’ll have plenty of examples in this devlog that will hopefully help you get into the flow of it.

The main thing to remember for this step is that a gross simplification of the movement is still far better than nothing, even if it doesn’t capture every last nuance of how the character would move in reality. In our head-bopping example, I imagine the character would probably flinch down with just her head, bringing it closer to her shoulders, but we are unable and don’t really need to capture that nuance anyway.

All we need to do is quickly have her sprite move down and back up again, like this:

At this point, you should have a idea of how you want the movement to look in-game.

Step 2: Create a transform.

Here’s the transform I used in the scene above:

transform chop:
    ease 0.15 yoffset 15
    ease 0.15 yoffset 0

Adding ease—or any other kind of transition—to a transform is what allows you to have it change the position of a sprite in a fashion other than just instantly. In this case, Flora is taking 0.15 seconds to ease 15 pixels down, then 0.15 seconds to ease back up.

Ease uses a cosine-based curve to slow down the start and end of the movement, where the simpler transition move would create linear movement that’s usually much too jarring and mechanical. I pretty much only use ease when making transform animations for sprites, since most other transitions are not really intended for this use case and move looks so unnatural. (Perhaps move could look good if used with a robot/android character?)

If you want diagonal or other types of combined movement like with zoom, you can change multiple variables at once with a transition like this:

ease 0.5 yoffset 15 xoffset 15 zoom 1.5

You might be wondering how to come up with the right values to make an animation look good, but truthfully, it’s nothing but experience and professional guesstimation. At first, just put in some random values that seem reasonable.

Step 3: Apply the transform and iterate.

Apply the animation transform to a sprite’s show statement the exact same way you would a regular transform:

show flora at chop

OR you can define the transform directly inside the show statement if you don’t intend to reuse it:

show flora:
    ease 0.15 yoffset 15
    ease 0.15 yoffset 0

Chances are it’s not going to look good right away.

If something seems particularly haywire, check the last transform you used on the sprite and make sure you really are starting with the values you think you are. (Personally I always try to keep the x and y offsets at 0 in non-animation transforms so I can always just assume they’re starting at 0 when making animations.)

Otherwise, just slowly adjust the variables and test until it does look good. You’ll get a whole lot better at guessing and zeroing in variables as you go, and you’ll quickly build up a library of transforms that can potentially be reused for similar movements in other situations as well.

Step 4: Don’t forget to continue using poses and expressions at the same time.

So after fiddling with the variables for a while, you now have your animation looking as you imagined it. However in our case, the full effect of the head chop in the gif isn’t finished yet.

As you can probably guess, transform animations aren’t at their best unless used together in harmony with the rest of your toolbox for character movement and expressiveness as well.

In this case, combining the transform together with a change of pose and expression, like this:

show flora body_armdown eyes_smilesquint mouth_bigsmile at chop

—is what really brings it to the next level and creates the full illusion of movement seen in the gif.

Step 5: Learn new techniques.

Hopefully, you can already see the value of what simple animations like my first example bring to the table. In my opinion, the movement they bring to a scene is absolutely critical, especially if it had nothing but unmoving sprites cycling through expressions to offer beforehand.

And thankfully, Ren’Py’s transforms aren’t limited to mono-directional or finite movements. Creativity really is the limit, I’ve still yet to find a single movement in writing that can’t at least be emphasized with a transform.

Here’s a slightly more complicated example from our demo, where the animation is looped instead of finite:

In this case, all you need to do is add repeat to the transform. Simple!

transform ruffled:
    ease 0.125 xoffset -4
    ease 0.125 xoffset 0
    repeat

And that’s just the beginning. Once you start chaining multiple animations and expression changes, you might just start to go mad with power.

Look at how she’s moving up and down and around all over the screen, with changes of expression mixed in! She’s even moving… forward?! It’s starting to feel like anything is possible!

For this particular sequence, my code looks like this:

"I grab our drinks from the counter, and Flora leads me over to a table for two."


show flora eyes_neutral at f_center
with ease


pause 0.75


show flora at f_sit1
with ease


pause 0.2


show flora eyes_happy:
    ease 0.3 zoom 1.2 xoffset 60 yoffset -100


"She immediately looks across at me after we sit down, ignoring the latte I placed in front of her."

P.S. If you want to quickly animate a movement between two existing, purely positional transforms, you can just directly apply an ease to the show statement the same way you would any other transition, as seen here.

As an aside, the reason the xoffset variable changes in that final transform—despite it appearing to have no horizontal movement—is because Flora’s sprite is technically centered on the canvas but asymmetric enough to look otherwise, and needs to be aligned slightly off-center horizontally to appear like it’s centered. Zooming in or out on a misaligned displayable can very quickly throw off the corrective alignment and make it look off-center again or appear to move horizontally. It took some rather annoying trial and error to counteract that effect and get that final zoom-in to keep Flora looking centered, while—since this is far from the only issue that misalignment causes—I probably should’ve just bit the bullet and re-exported the images to fix the source of the problem instead XD

Yoffset also changes for a similar reason as above, but in this case it couldn’t be fixed by realigning the base sprite images since it’s visually necessary to center the image vertically closer to her face rather than the true center of her body. Whenever you want to zoom in on a sprite anywhere other than dead center, this is unfortunately just a fact of life as far as I’m aware.

You will inevitably run into this and other various Ren’Py quirks and problems as you try out more and more ideas with transforms, so just remember not to back down and give up on those ideas too easily. The Ren’Py docs and—if you’re really stuck—the Ren’Py Discord are your friends if you can’t figure it out on your own!

Anyway, let’s wrap back around to the scene I showed you at the start of this month’s devlog.

Alex and Jenna don’t really have much in the way of interesting, clearly described physical movements and interactions like the ones from my examples to work off here. That’s partly my own fault for not considering how to make the scene more visually interesting as I was writing it, but even on second review my mind was void of any reasonable ideas for such additions. Sometimes it’s just inevitable that you’ll have to reach a bit more to get things moving.

As I went through the scene, I primarily used small, non-specific movements that weren’t described in the text to break up the stillness. My biggest go-to was moving Jenna slightly up and down as her confidence or mood rose and fell, adding a simple representation of body language that meshed surprisingly well with her expression changes.

Example:

show jenna:
    ease 0.5 yoffset 5
with charDissolve
   
jq "So..."


show jenna eyes_worriedclosed mouth_neutral:
    ease 0.5 yoffset 10
with { 'master' : charDissolve } # this is a dict transition so it doesn't interrupt the text being extended on the same line afterward


extend " umm..."


show jenna eyes_worried mouth_neutralspeak:
    ease 0.5 yoffset 5
with charDissolve
   
jq "Do you think you could pass on a message for me?"

There were also a few interspersed moments of small movements described in writing, which I obviously jumped at the opportunity to animate.

Example:

show jenna eyes_worriedclosed:
    pause 0.1
    ease 0.5 yoffset 3
    pause 0.1
    ease 0.5 yoffset 13
with charDissolve

And one moment where I did come up with an idea to add some movement in the writing:

j "But..."


show jenna eyes_worriedclosed mouth_neutral
with charDissolve


pause 0.5


show jenna mouth_surprised:
    ease 0.6 yoffset 3
with charDissolve
   
pause 1.0
       
show jenna mouth_surprised:
    ease 0.6 yoffset 13


pause 1.0


show jenna eyes_neutral mouth_neutral
with charDissolve


"She straightens herself up after taking a deep breath in, resolve clear as her eyes meet mine."

Now, this is where I would show you an after comparison of that gif at the start of the devlog, but it’s not actually all that satisfying to look at when all the animations are sped up and mushed together like that. The important part is that Jenna actually moves around now, please just judge the value of the animations in real time 😭

To wrap this up, I don’t think I’ve anywhere near tried out the full breadth of possibilities that transform animations have to offer yet, but I’m super excited to keep experimenting with them as I continue on with this stage of development!

Once again, thank you so much for reading to the end. If you’re a fellow developer, I hope this gave you some cool new ideas for your games!

-Duhop

Get Alex's Journey to the Grave

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.