Train Movement Animation




In this tutorial I'll be discussing how to program an animation for the Ren'Py engine. When working for Afterthought Studios,  I made the following animation for the game "When Our Journey Ends." This tutorial is based on the video below. This tutorial will focus on the interior of a train , with gentle screen shaking and looping scenery passing by.

!!Warning!!
The following code was taken from an older Ren'Py project. It might not work perfectly "out of the box" and may require updating the syntax to match more recent Ren'Py updates

I hope to provide such an update at a future date but haven't had the time to. Thank you for being patient.

Step One

To  get a basic understanding of how this works, I encourage you to read the following:

This post really helped me out when I was first starting to work with Ren'Py to make animated BGs. The author, Helia, was also incredibly gracious and answered the questions I had. It also helped that we were both working with the same fantastic artist for the background art, which made things much easier for me. 


This animation relies on the 3D Camera for Ren'Py, which was written by Akakryoryu. You can download it from their homepage. Be sure to follow the instructions to add it to your project.

See a demonstration of the 3D camera plugin in the following video:

(Coming soon)

To make things easy, we should start by having you download the assets I prepared ahead of time. 

The zip includes the following files:

  • interior2_exterior_loop.png
  • interior2_train.png
  • interior2_handles1.png
  • interior2_handles2.png

Step Two

The way I organize my game's background files is as follows:

[YourGame]/game/images/BGS/[FolderName]/[FileName].png

where "Folder Name" is the name of the background. In this example, it would be "Train_Interior2".

I recommend using the script.rpy file to keep the definitions of your characters, animations, background images, transitions, etc. And use a additional script files for your story. (script2.rpy, script3.rpy, etc.) This should help avoid having to scroll through huge amounts of definitions every time you open the file.

script.rpy:

####TRAIN INTERIOR 2####
image interior2_exterior_loop = "images/BGS/Train_Interior/interior2_exterior_loop.png"
image interior2_train = "images/BGS/Train_Interior/interior2_train.png"
image interior2_handles1 = "images/BGS/Train_Interior/interior2_handles1.png"
image interior2_handles2 = "images/BGS/Train_Interior/interior2_handles2.png"
image interior2_handles:
    contains:
        "interior2_handles1"
        pause 0.45
        "interior2_handles2"
        pause 0.30
        "interior2_handles1"
        pause 0.50
        "interior2_handles2"
        pause 0.45
        repeat
image interior2_exterior_move:
    contains:
        "interior2_exterior_loop"
        xalign 1.0
        linear 25.0 xalign 0.0
        repeat
image train_interior2:
    subpixel True
    contains:
        xalign 1000
        yalign 564
        "interior2_exterior_move"
    contains:
        "snow2"
    contains:
        xalign 1000
        yalign 564
        "interior2_train"
    contains:
        xalign 1000
        yalign 564
        "interior2_handles"

options.rpy:

init python:
    
    #################################################################
    # Here we use random module for some random stuffs (since we don't
    # want Ren'Py saving the random number's we'll generate.
    import random
    
    # initialize random numbers
    random.seed()

options.rpy:

########SCREEN SHAKE########
init python:
    class Wiggle(object):
        def __init__(self, freq, amp, octaves=1, ampMulti=.5, time=0):
            from random import random
            self.randoms = [(1+random(), 1+random()) for i in range(0, 100)]
            self.freq = freq
            self.amp = amp
            self.octaves = octaves
            self.ampMulti = ampMulti
            self.time = time
        def __call__(self, time, at):
            from math import sin, pi, ceil
            r1, r2 = self.randoms[int(ceil(time*self.freq)%100)]
            return r1*self.amp*sin(2*pi*self.freq*time) + r2*self.amp*self.ampMulti*sin(2*pi*self.freq*2*self.octaves*(time+self.time))

            

            

 To use this background and animation in a scene (script.rpy):

    scene train_interior2
    $camera_move(0, 0, 100, 0, duration=2.0)
    $ all_moves(x_express=Wiggle(freq=.5, amp=10), y_express=Wiggle(freq=.6, amp=10), z_express=Wiggle(freq=.4, amp=1))
    with Dissolve(time=2.0,alpha=False,time_warp=None)

Leave a comment

Log in with itch.io to leave a comment.