Skip to content

Tutorials · Animation and Cutscenes · Chapter 13

A gallery of animation

85 minutes Uses: AnimatedSprite, AnimationLayers, PropertyAnimation, Timeline, CameraRig, Camera, Sprite, TileMap, Light, AmbientLight, AudioSource, Signal, anneal_by, emit, heard, hide, play_clip, play_cutscene, play_sound, pulse_by, set_flag, set_text, set_time_scale, shake, show, vec2, wait

By the end

  • Six scenes in scenes, each opened from Assets and played on its own: outfit, slow-motion, rewind, tank, ghost and boss
  • Five new sheets with their clips: outfit, flame, tank, ghost and demon, and a sprite library, dresser.spritelib
  • The six Animated Sprite fields the show did not need: Culling, Tick Mode, Library, Resolver, Category and Label
  • Eight new scripts, each short, and for each case its values in a table and what to change in your own game
On this page

Six cases

The first twelve chapters built one show. This one builds six small things the show did not need, each a case you will meet again in a game of your own: an outfit swap, slow motion, a rewind, a toy tank made of two sprites, a ghost that appears and vanishes, and a boss that drops into an arena. Each is a scene of its own in scenes, opened from Assets and played with F5. The startup scene stays theatre.ron.

Between them they use the six fields of the Animated Sprite that nothing in the show touched: Culling, Tick Mode, Library, Resolver, and the Category and Label inside it. Each is defined where a case first sets it, and so is every other control no earlier chapter has defined: the Camera’s, the Variants band’s, a Sprite’s Flip, and a Light’s and an Ambient Light’s Color and Radius.

CaseWhat it is forWhat carries it
An outfit swapA hero in a new cloak, a scorched variant, a sword found on the way.One set of clips, a sprite library of replacement pictures, a Resolver that names which, and a script that changes the label.
Slow motionA finishing blow, with something that must keep real time beside it, and a crowd that costs nothing while nobody sees it.set_time_scale on a swish, a candle on Tick Mode Unscaled, and Culling on the crowd, brought into view by a panning camera.
A rewindA replay, a time-reversal puzzle, a lamp that goes out as the clock runs back.Speed -1 on a sprite, and on a cutscene that plays again each time it reaches its start.
A tankA character made of parts, each part its own node: a turret on a hull, a rider on a horse.Two Animated Sprites, each cut by node by a layer of its own.
A ghostAn enemy or a stranger that fades in, haunts a place and leaves, and comes back.Clips that play once and chain by the signal each sends when it finishes, Playing off for a beat, and a flicker keyed with Nearest.
A boss entranceA dramatic landing and a shot that swings in on it.A camera rig held off until a signal, a shake of the picture, and a blend with Overshoot.

Every case reads the same way: what it is for, the steps, the script whole where it has one, the new fields defined, its values in a table, who decides the clip, and a note on which field to change in your own game.

The room every case stands in

All six scenes start from the same empty room: a root, a camera that shows 640 by 360 world units, the hall’s wall behind and its floor. You make it once and save it six times.

  1. Choose File → New scene. Click + Add node, choose Node, and rename it gallery.
  2. Select gallery, click + Add node and choose Camera. Rename it camera. Set its Position to 320 and 180, the middle of the view. In its Camera, set Scaling to Letterbox and Background to 0.005, 0.016, 0.022, alpha 1, in the colour picker’s F mode, as chapter 3 showed. Extent stays 360 and Width stays 640.
  3. Select gallery, click + Add node and choose Sprite. Rename it backdrop. Drag old-dark-castle-interior-background.png from Assets onto its Texture, and set Size to 960 and 304, Anchor to 0 and 0, and Sort Layer to -20.
  4. Select gallery, click + Add node and choose Tilemap. Rename it floor. Drag old-dark-castle-interior-tileset.tilemap onto its Tilemap and untick Collision: nothing in these rooms stands on it.
  5. Press Ctrl+S and save the scene in the scenes folder as outfit.ron.
  6. Choose File → Save as… five times, and save the copy each time in scenes, as slow-motion.ron, rewind.ron, tank.ron, ghost.ron and boss.ron. Each save leaves the editor holding the copy, which is why the room is made before any case is.
Scaling
The Camera’s Scaling: how it works out how much of the world to show when the window is any shape. Letterbox shows exactly Width by Extent units whatever the window, and fills what is left of the window with bars of the Background colour. The theatre’s camera is a Letterbox, which is why the picture of every chapter is the same 640 by 360.
Extent
The Camera’s Extent: how tall the view is, in world units, for a Letterbox: 360, as it comes.
Width
The Camera’s Width: how wide the view is when Scaling is Letterbox: 640, as it comes. With the Extent the view is 640 by 360 units, which a 1280 by 720 window draws twice as big.
Background
The Camera’s Background: the colour behind everything it shows, and in the bars of a Letterbox. 0.005, 0.016, 0.022 is a very dark blue-green.
Collision
The Tilemap’s Collision: whether the layers marked solid become collision shapes. Ticked, as it comes, a body would stand on the floor. These rooms hold no body, so it is off.
NodeComponentFieldValue
galleryNodeno other field
cameraPosition320, 180
CameraScalingLetterbox
Background0.005, 0.016, 0.022, alpha 1
backdropSpriteTextureold-dark-castle-interior-background.png
Size960, 304
Anchor0, 0
Sort Layer-20
floorTilemapTilemapold-dark-castle-interior-tileset.tilemap
Collisionunticked

The six scenes now exist, each a copy of the room. From here, every case opens its own scene: double-click it under scenes in Assets.

An outfit swap

What it is for. A hero finds a cape, takes a burn, picks up another sword. None of it changes how he moves: the frames, their timing, their pivots and their events stay as they are. Only the pictures differ. A second copy of every clip for every outfit would mean mending every timing once for each outfit. So the pictures go in a sprite library, a table of replacement pictures, and the Animated Sprite names which to draw.

One sheet for two knights

The starter came with knight-blue.png, the knight’s idle strip with his cloak recoloured. Make Animation packs it beside the red one, so that both lie on one sheet and a frame of the blue is a rectangle a fixed distance from the same frame of the red.

  1. In Assets, open art, right-click gothic-hero-idle.png, and choose Make Animation…. Tick knight-blue.png under it. Set Name to outfit, leave Frames a second at 12 and Loop ticked, and press Create.
  2. It writes art/outfit.png, a sheet of 512 by 64 with the red strip at its left and the blue one 154 along, and art/outfit.anim, with one clip, outfit, which plays the two strips as two whole frames.
The Make Animation window over the editor, its list of art’s pictures scrolled to the ticked one: gothic-hero-attack.png, crouch, hurt, idle, jump and run, and knight-blue.png in sight, only gothic-hero-idle.png ticked, its Name gothic-hero-idle, Frames a second 12.0 and Loop ticked; behind it the Hierarchy lists gallery with camera, backdrop, floor and dresser, and the Viewport shows the hall with the knight at its left edge
The window as it opens on gothic-hero-idle.png. Tick knight-blue.png in its list, and type the name, before pressing Create.

The dresser

The knight who changes cloak is a node called dresser. It is an Animated Sprite like the knight of chapter 1, on the new sheet.

  1. Double-click scenes/outfit.ron in Assets. Select gallery, click + Add node, choose Animated Sprite and rename the node dresser. Set its Position to 320 and 288.
  2. In its Sprite, drag art/outfit.png onto Texture, set Anchor to 0.5 and 1, and Sort Layer to 5.
  3. In its Animated Sprite, drag art/outfit.anim onto Animations and tick Frame Size. Clip stays idle: the clip the next steps cut.
  4. With dresser selected, double-click art/outfit.png in Assets. The Sprite sheet tab opens on it. Set Cell to 38 and 48, with Margin and Spacing at 0. Drag across the first four cells of the top row, from cell 0, 0 to cell 3, 0: the red knight’s four frames, Selected 4 x 1.
  5. Under Clips, press From selection. Name the clip idle, set its rate to 8 fps and its loop to Loop, and press Generate 1. The clip goes into outfit.anim, which the Animation tab has open: it lists outfit (2) and idle (4). Press Save there. The Sprite sheet tab hangs each frame at the pivot 0.5, 1, the middle of its bottom edge.
ClipFramesRateLoop
outfitMake Animation’s own: cells 152 by 48, two, from 0, 0, spacing 212Loop
idlecells 38 by 48, four, from 0, 0; every frame’s pivot 0.5, 18Loop

The library

A library is a table: for a category, a label and a frame number, it names the rectangle of a sheet to draw. The blue knight’s four frames are the red one’s four, 154 along the sheet.

  1. In Assets, click the scenes folder, so that Create writes into it, then press Create, choose New sprite library, type dresser and press Enter. It writes scenes/dresser.spritelib, beside the gallery’s scenes, with no library in it yet. Drag it onto the dresser’s Animated Sprite, onto Library.
  2. In the Animation tab, in its Frames mode on idle, open the Variants band. Type outfits into New library and press its +: the library is made, and the band offers it.
  3. The category row offers idle, the clip’s own name. Type blue into New variant and press its +: the label is chosen, and the strip below it reads 0 of 4 frames. A label with no pictures cannot be stored, so it begins to exist at the first frame you assign.
  4. Set the row’s frame to 0, press Copy from clip, which fills the boxes with what that frame draws now, x 0, y 0, w 38, h 48, set x to 154 and press Assign. Do the same for frame 1, with x 192, for frame 2 with 230, and for frame 3 with 268. The strip reads 4 of 4 frames.
  5. Press the band’s own Save, which writes the .spritelib, and then the Animation tab’s Save and Ctrl+S.
  6. Select dresser. In its Animated Sprite, set the Resolver’s Category to idle and its Label to blue. The Viewport shows the knight in the blue cloak.
New library
The box in the Variants band’s top row, and the + after it: the name of a library to start in this .spritelib file. A file can hold several, and the list at the row’s left chooses which the band shows.
New variant
The box in the band’s second row, after the lists of categories and labels, and its +: the name of a label to start under the chosen category. It only chooses the label. Nothing is stored until a frame is assigned, since a label with no pictures has nothing to keep.
frame
Which frame of the open clip the row below acts on, 0 for the first. A click on a cell of the strip chooses it too.
Copy from clip
Fills the four boxes after it with the rectangle that frame draws now, so a variant starts as a copy of the picture it replaces and you only move it.
x, y, w and h
The rectangle of the sheet the variant draws on that frame, in pixels: where its left and top are, and its width and height.
Assign
Writes the rectangle into the library for that frame, under the chosen category and label. The frame’s cell in the strip fills in.
The Animation tab on the dresser in its Frames mode: the clips outfit (2) and idle (4), idle chosen, Loop lit at 8.0 fps; under them the open Variants band, with the library outfits, the category idle, the label blue, a strip of four green cells reading 4 of 4 frames, and a row with frame 0, Copy from clip, the boxes x 0, y 0, w 1 and h 1, and Assign
The Variants band: library outfits, category idle, label blue, all four frames replaced. The row under the strip is where each frame is assigned.
The dresser selected, the Inspector narrowed to its Animated Sprite: Animations outfit.anim, Clip idle, Speed 1.00, Playing and Frame Size ticked, and under Swap and culling the Culling AlwaysAnimate, Tick Mode Frame, Library dresser.spritelib, and a Resolver with Category idle and Label blue; the Viewport is framed on the knight, large, in a blue cloak, and the Animation tab lists outfit (2) and idle (4)
Library
The Animated Sprite’s Library: the .spritelib file it can swap pictures from, here dresser.spritelib. A library is a table of replacement pictures: a category, a label, a frame number and the rectangle of a sheet. Empty, as it comes, nothing is swapped. The file is made by Create → New sprite library in Assets and filled in the Variants band.
Resolver
The Animated Sprite’s Resolver: which set of pictures of the library to draw, by two words, its Category and its Label. The clip picks when, the Resolver picks which, and neither knows about the other. Both empty, as they come, draws the clip’s own pictures.
Category
The Animated Sprite’s Category, the first of its Resolver’s two words: where in the library to look. Empty, as it comes, means the name of the clip that is playing, so the library’s idle serves idle. The dresser names it, idle, which says the same thing. Name it when two clips show the same drawings and should share one set of entries.
Label
The Animated Sprite’s Label, the Resolver’s second word: which variant of the category to draw, blue. This is the field a script writes to change the outfit. Empty draws the clip’s own pictures, the red cloak. A label the library lacks, or a frame it does not hold, falls back to the clip’s own picture too, so a half-drawn outfit shows the wrong coat on a character who is still there.

The swap group of the Animated Sprite has one button, Clear Swap. It empties Library, Category and Label together, and the clip’s own pictures draw again.

The script

  1. In Assets, press Create, choose New script, type dresser and press Enter. In the Code tab, replace everything in dresser.rgs with the script below, and press Ctrl+S.
  2. Select dresser, click Add component and choose Dresser.
rgs dresser.rgs
/// Changes the knight's cloak every second and a half: blue, then his own
/// red, and blue again.
component Dresser {
    /// Seconds since the last change.
    #[hidden]
    clock: f32 = 0.0,
    /// Whether he wears the blue cloak now.
    #[hidden]
    blue: bool = true,

    fn loop() {
        clock += dt;
        if clock >= 1.5 {
            clock = 0.0;
            blue = !blue;
            if blue {
                set_text("AnimatedSpriteComponent", "resolver.label", "blue");
            } else {
                set_text("AnimatedSpriteComponent", "resolver.label", "");
            }
        }
    }
}
clock
The script’s own count of seconds since the last change. clock += dt adds the step’s length to it on every step, and past 1.5 it starts again at 0. #[hidden] keeps it out of the Inspector, where it is a number nobody sets.
set_text
Writes text onto another component on this node. Here the component is the Animated Sprite, and the property resolver.label reaches inside its Resolver. The word blue puts the cloak on and the empty word "" takes it off, so the clip’s own pictures draw. It acts on the next step.
Four pictures of the dresser’s knight side by side, each cut close round him: in a blue cloak, in his own red one, in the blue again, and in the red again
The dresser at steps 45, 135, 225 and 315, 0.75, 2.25, 3.75 and 5.25 seconds: blue, red, blue, red. The script flips the label every 1.5 seconds. The pose is the same in each: all four steps fall on the idle’s third frame, and only the label changes.
NodeComponentFieldValue
dresserPosition320, 288
SpriteTextureoutfit.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationsoutfit.anim
Frame Sizeticked
Librarydresser.spritelib
Categoryidle
Labelblue
Dresserclock and blue, hidden: the script’s own0 and on
LibraryCategoryLabelFrames
outfitsidlebluecells 38 by 48, four, from 154, 0

Slow motion

What it is for. A finishing blow that slows the whole game for a moment, with something beside it that must not slow: a flame, a ribbon on a pause menu, a damage number. And a crowd in the background that costs nothing while nobody can see it. The scene has a knight who swings, a candle, and three knights standing off to the right, beyond the edge of the view, which a camera pans across to meet.

The candle

  1. Double-click scenes/slow-motion.ron in Assets. In art, right-click candle.png, which came with the starter, choose Make Animation…, set Name to flame and press Create. It writes art/flame.png, 64 by 32, and art/flame.anim with one clip, flame, the candle’s four flames as one whole frame of 32 by 16.
  2. Select gallery, click + Add node, choose Animated Sprite and rename it candle. Set its Position to 380 and 288. In its Sprite, drag art/flame.png onto Texture, set Anchor to 0.5 and 1 and Sort Layer to 5. In its Animated Sprite, drag art/flame.anim onto Animations, set Clip to flicker, tick Frame Size, and set Tick Mode to Unscaled.
  3. With candle selected, double-click art/flame.png. In the Sprite sheet tab set Cell to 8 and 16, and drag across the first four cells of the top row: Selected 4 x 1. Press From selection, name the clip flicker, set it to 8 fps and Ping-pong, and press Generate 1. Press Save in the Animation tab.
ClipFramesRateLoop
flameMake Animation’s own: cells 32 by 16, one, from 0, 012Loop
flickercells 8 by 16, four, from 0, 0; every frame’s pivot 0.5, 18Ping-pong

The finisher and the crowd

  1. Select gallery, click + Add node, choose Animated Sprite and rename it finisher. Set its Position to 260 and 288. In its Sprite, drag art/knight.png, the knight’s sheet from chapter 1, onto Texture, and set Anchor to 0.5 and 1 and Sort Layer to 5. In its Animated Sprite, drag art/knight.anim onto Animations, set Clip to slash and tick Frame Size.
  2. Select gallery, click + Add node, choose Node and rename it crowd. Select crowd, click + Add node, choose Animated Sprite and rename it fan_1. Set its Position to 760 and 288: past the view’s right edge, which is at 640. Give it the knight’s sheet as the finisher has it: Texture art/knight.png, Anchor 0.5 and 1, Sort Layer 5, Animations art/knight.anim and Frame Size ticked. Clip stays idle. Under the Sprite’s Flip, tick X, so the fans face the knight. In the Animated Sprite, set Culling to PauseWhenOffscreen.
  3. With fan_1 selected, press Ctrl+D, rename the copy fan_2 and set its Position to 800 and 288. Press Ctrl+D again, rename the copy fan_3, set its Position to 840 and 288, and set its Culling to ResetWhenOffscreen.
Tick Mode
The Animated Sprite’s Tick Mode: which clock its clip runs on. Frame, as it comes, is the game’s own, so the clip slows when the game does. Unscaled is real time: the sprite keeps its pace while a script has slowed the game. The candle is the one thing in the scene on real time.
Culling
The Animated Sprite’s Culling: what its clip does while the camera cannot see the sprite. AlwaysAnimate, as it comes, goes on. PauseWhenOffscreen holds the frame it was on and carries on from it when it comes back. ResetWhenOffscreen holds, and starts the clip over when it comes back into view. Fans one and two are on Pause, fan three on Reset.
Flip
The Sprite’s Flip: X mirrors the picture left to right, about the node, and Y top to bottom. Both are off as they come. The fans have X ticked, so they face the knight.
The candle selected and framed in the Viewport, large: a cream candle stub with a flame of a few pale yellow pixels and an orange tip; the Inspector narrowed to its Animated Sprite shows Animations flame.anim, Clip flicker, Speed 1.00, Playing and Frame Size ticked and, under Swap and culling, Culling AlwaysAnimate and Tick Mode Unscaled; the Animation tab lists the clips flame (1) and flicker (4), with flicker lit and Ping-pong lit at 8.0 fps
fan_3 selected in the Hierarchy under crowd, with fan_1 and fan_2 above it; the Viewport is framed on the crowd’s knights, two of them in sight, each a pale-haired knight in a red cloak facing left, the selected one outlined in blue; the Inspector narrowed to its Animated Sprite shows Animations knight.anim, Clip idle, Speed 1.00 and, under Swap and culling, Culling ResetWhenOffscreen and Tick Mode Frame; the Animation tab lists the knight’s clips

The scripts

  1. In Assets, press Create, choose New script, type finisher and press Enter. In the Code tab, replace everything in finisher.rgs with the first script below, and press Ctrl+S. Select finisher, click Add component and choose Finisher.
  2. Do the same for pan.rgs, the second script. Select camera, click Add component and choose Pan.
rgs finisher.rgs
/// A finishing blow: the whole game slows to a fifth as the blade comes
/// round, runs at full speed again once the slash is over, and a second
/// later he swings again.
component Finisher {
    fn on_signal() {
        if heard("frame:swish") {
            set_time_scale(0.2);
        }
        if heard("finished:slash") {
            set_time_scale(1.0);
            await wait(1.0);
            play_clip("slash");
        }
    }
}
rgs pan.rgs
/// Pans the camera across to the crowd and back, for good.
component Pan {
    fn on_create() {
        pulse_by(vec2(320.0, 0.0), 2.0, .InOutSine);
    }
}
set_time_scale
Runs the whole game at scale of its speed from the next step, between 0.05 and 1, until the scene changes. 0.2 is a fifth: a second of the game takes five seconds on the wall. A sprite on Tick Mode Unscaled keeps real time.
frame:swish
The signal a clip’s Trigger track sends the scripts of its node when its frame is reached. The knight’s slash has a Trigger called swish on its frame 2, from chapter 4, which played a sound there. Here nothing sounds and the signal is all the scene wants, and heard("frame:swish") is true on the step it fires.
finished:slash
The signal an Animated Sprite sends when a clip that plays once reaches its end, as chapter 5’s finished:hurt does: heard("finished:slash") is true on that step. The slash ends after its last frame, which lasts 0.3 seconds, so the game stays slow through the whole of the swing, that frame included, and then returns to full speed.
play_clip
Plays one of this node’s Animated Sprite clips from its first frame, even the one already playing, and sets the sprite playing. The slash is a clip that plays once, so a script starts it again.
Four pictures of the finisher’s slash side by side, each with the candle at the right: the blade’s white arc as the swish is reached, the sword thrust straight out, the blade drawn back, and the knight crouched at the end of the swing
Steps 8, 11, 14 and 17 of the slash, 0.13, 0.18, 0.23 and 0.28 seconds in. A picture is one step of the game, and the swish is heard on step 8: from step 9 the game runs at a fifth, so each of its steps takes five times as long. These nine steps, 0.15 seconds of the game, take about 0.75 seconds to play.
NodeComponentFieldValue
finisherPosition260, 288
SpriteTextureknight.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationsknight.anim
Clipslash
Frame Sizeticked
Finisherthe script
candlePosition380, 288
SpriteTextureflame.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationsflame.anim
Clipflicker
Frame Sizeticked
Tick ModeUnscaled
crowdNodeno other field
fan_1, fan_2, fan_3Position760, 800 and 840, each with 288
SpriteTextureknight.png
Anchor0.5, 1
Sort Layer5
Flip Xticked
Animated SpriteAnimationsknight.anim
Frame Sizeticked
fan_1, fan_2CullingPauseWhenOffscreen
fan_3CullingResetWhenOffscreen
cameraPanthe script

A rewind

What it is for. A replay, a time-reversal puzzle, a clock that runs back and takes the lights with it. A negative Speed plays a clip from its last frame to its first. The same on a cutscene plays it from its end. This scene has both: a knight who runs backwards, and three lamps that a cutscene lights going forward and puts out going back, then plays again.

The runner

  1. Double-click scenes/rewind.ron in Assets. Select gallery, click + Add node, choose Ambient Light and rename it ambient. Set its Color to 0.62, 0.58, 0.66, alpha 1.
  2. Select gallery, click + Add node, choose Animated Sprite and rename it runner. Set its Position to 220 and 288. In its Sprite, drag art/knight.png onto Texture, set Anchor to 0.5 and 1 and Sort Layer to 5. In its Animated Sprite, drag art/knight.anim onto Animations, set Clip to run, Speed to -1, and tick Frame Size.
The runner selected, the Inspector narrowed to its Animated Sprite: Animations knight.anim, Clip run, Speed -1.00, Playing and Frame Size ticked and Tick Mode Frame; the Hierarchy lists gallery with camera, backdrop, floor, ambient, runner and lamps with lamp_1, lamp_2 and lamp_3; the Viewport is framed on the knight mid-stride, large, his red cape streaming behind him; the Animation tab has run (12) lit at 20.0 fps
Six pictures of the runner side by side, each cut close round him: six different poses of one stride, his cape streaming back at different lengths
Speed -1, steps 1, 4, 7, 10, 13 and 16, 0.02, 0.07, 0.12, 0.17, 0.22 and 0.27 seconds in: the run’s last frame, 11, and the five before it, 10 down to 6. The run is twelve frames at 20 a second, three steps a frame.
Six pictures of the runner side by side, each cut close round him: six poses of the stride, the last of them, at the right, the same as the first of the strip above
For comparison, Speed 1, steps 19, 22, 25, 28, 31 and 34, 0.32, 0.37, 0.42, 0.47, 0.52 and 0.57 seconds in: frames 6 to 11. Read from the right, they are the six drawings above, one for one.

The lamps

Three lamps hang from a node that carries a cutscene. The cutscene lights them one after another, a second apart: lamp_1 from 1 second, lamp_2 from 2 and lamp_3 from 3, each staying lit to the end of 4 seconds.

  1. Select gallery, click + Add node, choose Cutscene and rename it lamps. Set its Position to 0 and 0.
  2. Select lamps, click + Add node, choose Sprite and rename it lamp_1. Set its Position to 380 and 288. Drag art/footlight.png onto Texture, set Size to 12 and 8, Anchor to 0.5 and 1 and Sort Layer to 10. Click Add component, choose LightComponent, and set Radius to 80 and Color to 1, 0.82, 0.56, alpha 1.
  3. With lamp_1 selected, press Ctrl+D, rename the copy lamp_2 and set its Position to 440 and 288. Press Ctrl+D again, rename the copy lamp_3 and set its Position to 500 and 288.
  4. Select lamps and click the Cutscene tab. Press New cutscene: it writes scenes/lamps.timeline and fills the node’s Timeline. In the bar, set the length, the box that reads 0.00 s, to 4. Press Add track, choose Activation, press Pick a node and choose lamp_1; add lamp_2’s and lamp_3’s the same way. On lamp_1’s lane, click the ruler at 1 and then the lane on the red line, right-click the bar and tick Open end; do the same on lamp_2’s at 2 and lamp_3’s at 3. Press the bar’s Save.
  5. In the Inspector, tick Playing on lamps’s Timeline and set its Speed to -1.
  6. In Assets, create a new script, rewind, and replace everything in rewind.rgs with the script below. Select lamps, click Add component and choose Rewind.
rgs rewind.rgs
/// Plays the lamps' cutscene backwards again each time it reaches its start.
component Rewind {
    fn on_signal() {
        if heard("cutscene:ended") {
            play_cutscene();
        }
    }
}
Radius
The Light’s Radius: how far its light reaches, in world units. A new Light’s is 240; the lamps’ is 80, a small pool round each one.
Color
The Light’s Color is the colour of its light, a warm 1, 0.82, 0.56 for the lamps. The Ambient Light’s Color is what the room is left at where no light reaches, alpha ignored: 0.62, 0.58, 0.66, a cool grey a little under full brightness.
cutscene:ended
The signal a node’s cutscene sends when it reaches its end, or its start when it plays backwards, as in chapter 12: heard("cutscene:ended") is true on that step.
play_cutscene
Plays this node’s cutscene from its start, or from its end at a negative Speed. The lamps’ Speed is -1, so each time the script calls it they go out again, right to left, from the end.
The editor with the node lamps selected: the Hierarchy lists runner and lamps with lamp_1, lamp_2 and lamp_3, lamp_3 greyed with the hidden mark at the right of its row; the Viewport is framed on two small gold lamps on the boards; the Inspector shows a TimelineComponent with Timeline lamps.timeline, Playing ticked and Speed -1.00; the Cutscene tab below shows the cutscene lamps, 4.00 s, the playhead at 2.50 s of 4.00, and three tracks, lamp_1 - Activation, lamp_2 - Activation and lamp_3 - Activation, each a green bar from a later second to the end
The cutscene at 2.5 seconds, scrubbed in the editor: lamp_3, which is lit from 3, is out, so its row in the Hierarchy is greyed and marked hidden.
Four pictures of the boards side by side, each with the lamps along them as small gold marks: three lamps, then two, then one, then none
Steps 30, 90, 150 and 210, at 0.5, 1.5, 2.5 and 3.5 seconds: three lamps, two, one, none. The cutscene plays from its end, so lamp_3 goes out first. It then plays again, from step 240, and all three are lit once more.
NodeComponentFieldValue
ambientAmbient LightColor0.62, 0.58, 0.66, alpha 1
runnerPosition220, 288
SpriteTextureknight.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationsknight.anim
Cliprun
Speed-1
Frame Sizeticked
lampsTimelineTimelinelamps.timeline
Playingticked
Speed-1
Rewindthe script
lamp_1, lamp_2, lamp_3Position380, 440 and 500, each with 288
SpriteTexturefootlight.png
Size12, 8
Anchor0.5, 1
Sort Layer10
LightRadius80
Color1, 0.82, 0.56, alpha 1
TrackNodeLit fromUntil
Activationlamp_11the end, 4
Activationlamp_22the end
Activationlamp_33the end

A tank of two sprites

What it is for. A character made of parts that each move on their own: a turret on a hull, a rider on a horse, a robot whose arm works while its legs walk. Each part is a node with an Animated Sprite of its own, and each plays the clip of a layer that is cut down to that node by name. The hull rolls on one layer and the turret fires on another, so neither clip knows about the other.

One sheet for two strips

  1. Double-click scenes/tank.ron in Assets. In art, right-click tank-hull.png, which came with the starter, choose Make Animation…, and tick tank-turret.png under it. Set Name to tank and press Create.
  2. It writes art/tank.png, a sheet of 256 by 16, and art/tank.anim, with one clip, tank: two whole frames, the hull’s strip of 80 by 14 at the left and the turret’s of 48 by 12 at 82.

The tank and its two parts

  1. Select gallery, click + Add node, choose Animation Layers and rename it tank. Set its Position to 240 and 288.
  2. Select tank, click + Add node, choose Animated Sprite and rename it hull. Leave its Position at 0 and 0. In its Sprite, drag art/tank.png onto Texture and set Anchor to 0.5 and 1 and Sort Layer to 5. In its Animated Sprite, drag art/tank.anim onto Animations and tick Frame Size. Clip stays idle.
  3. Select tank again, click + Add node, choose Animated Sprite and rename it turret. Set its Position to -4 and -13: on top of the hull. Give it the same Texture, Anchor 0.5 and 1, Animations and Frame Size, set its Sort Layer to 6, one above the hull’s, and its Clip to aim.
  4. With hull selected, double-click art/tank.png. In the Sprite sheet tab set Cell to 40 and 14, with Margin 0 and 0. Select the first cell and press From selection: name the clip idle, set it to 1 fps and Loop, and press Generate 1 and Clear. Select both cells, press From selection: roll, 8 fps, Loop, Generate 1, Clear.
  5. Set Cell to 24 and 12 and Margin to 82 and 0: the turret’s strip. Select the first cell: aim, 1 fps, Loop, Generate 1 and Clear. Select both cells: fire, 6 fps, Loop, Generate 1.
  6. In the Animation tab, choose aim and set the frame’s pivot x to 0.4. Do the same on each of the two frames of fire. Press Save. The turret’s cell is 24 wide with its dome toward the left and the barrel running right, and the pivot hangs the dome, not the middle of the cell, from the turret’s node.
ClipFramesRateLoop
tankMake Animation’s own: the hull’s strip, 80 by 14 from 0, 0, and the turret’s, 48 by 12 from 82, 012Loop
idlecells 40 by 14, one, from 0, 0; pivot 0.5, 11Loop
rollcells 40 by 14, two, from 0, 0; every frame’s pivot 0.5, 18Loop
aimcells 24 by 12, one, from 82, 0; pivot 0.4, 11Loop
firecells 24 by 12, two, from 82, 0; every frame’s pivot 0.4, 16Loop

The layers

  1. Select tank. In its Animation Layers, press the + on Layers twice. In the first row set Name to drive, Clip to roll, Weight to 1 and Blend to Override, and press the + on its Mask Nodes and type hull. In the second row set Name to gun, Clip to fire, Weight to 1 and Blend to Override, and put turret in its Mask Nodes.
  2. In Assets, create a new script, tank, and replace everything in tank.rgs with the script below. Select tank, click Add component and choose Tank.
The tank selected, Hierarchy listing gallery with camera, backdrop, floor and tank, which holds hull and turret; the Viewport is framed on the toy tank, large: an olive hull on dark treads with a turret and a grey barrel on top; the Inspector narrowed to its Animation Layers lists two rows open, 1 drive with Name drive, Clip roll, Graph none, Weight 1.00, Blend Override and its Mask list holding hull, and 2 gun with Name gun, Clip fire, Graph none, Weight 1.00, Blend Override and its Mask list holding turret
rgs tank.rgs
/// Drives the toy tank to and fro along the boards.
component Tank {
    fn on_create() {
        pulse_by(vec2(160.0, 0.0), 3.0, .InOutSine);
    }
}

pulse_by moves the tank node 160 units along and back, forever, 3 seconds each way, softly at both ends. The two sprites are its children, so they go with it, each playing the clip of its own layer.

Four pictures of the toy tank side by side, each cut close round it: the barrel plain, a bright flash at the end of the barrel, the barrel plain again, and the flash once more
Steps 4, 12, 24 and 34, 0.07, 0.2, 0.4 and 0.57 seconds in, close round the tank as it sets off. The flash is the second frame of fire, which turns over at 6 a second. roll turns the hull’s tread over at 8. They are two clocks on two layers, so the flash comes and goes on its own beat and not the tread’s.
NodeComponentFieldValue
tankPosition240, 288
Animation LayersName, Clip, Weight, Blend, Mask Nodesdrive, roll, 1, Override, hull
Name, Clip, Weight, Blend, Mask Nodesgun, fire, 1, Override, turret
Tankthe script
hullSpriteTexturetank.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationstank.anim
Frame Sizeticked; Clip idle, as it comes
turretPosition-4, -13
SpriteTexturetank.png
Anchor0.5, 1
Sort Layer6
Animated SpriteAnimationstank.anim
Clipaim
Frame Sizeticked

A ghost that appears and vanishes

What it is for. An enemy that fades in out of nothing, haunts a place, and leaves; a stranger on a path; a lamp spirit. Its three moments are three clips that play once, each started by the finish of the one before. The ghost holds its first frame, a wisp, for a beat before it begins, and its colour flickers all the while, from a property clip that has nothing to do with which picture is drawn.

One sheet for three strips

The Gothic Ghost has three strips in the same Gothicvania collection as the knight. Copy them in.

  1. Copy the three files below into art, each from where the list says, and press Refresh in the Assets panel.
  1. Double-click scenes/ghost.ron in Assets. In art, right-click ghost-appears.png, choose Make Animation…, tick ghost-idle.png and ghost-vanish.png, set Name to ghost and press Create. It writes art/ghost.png, a sheet of 2048 by 128, and art/ghost.anim, with one clip, ghost, whose three whole frames are the strips: 384 by 48 from 900, 448 by 80 from 0 and 448 by 64 from 450. Make Animation lays the tallest strip first.
  2. Select gallery, click + Add node, choose Animated Sprite and rename it ghost. Set its Position to 100 and 200: on the lit brick under the left lantern, where every stage of it can be seen. In its Sprite, drag art/ghost.png onto Texture, set Anchor to 0.5 and 1, and Sort Layer to 6. In its Animated Sprite, drag art/ghost.anim onto Animations, set Clip to appear, untick Playing and tick Frame Size.

Three clips and a pivot for each

  1. With ghost selected, double-click art/ghost.png. Appear: set Cell to 64 and 48 and Margin to 900 and 0. Drag across the first six cells, Selected 6 x 1, press From selection: appear, 10 fps, Once, Generate 1, Clear.
  2. Float: set Cell to 64 and 80 and Margin to 0 and 0. Select the first seven cells: float, 8 fps, Loop, Generate 1, Clear.
  3. Vanish: set Cell to 64 and 64 and Margin to 450 and 0. Select the first seven cells: vanish, 10 fps, Once, Generate 1. Press Save in the Animation tab.
  4. In the Animation tab, set each frame’s pivot y, with x at 0.5, one frame after another: 0.521 on the six frames of appear, 0.5 on the seven of float, and 0.641 on the seven of vanish. Press Save.
ClipFramesRateLoopEvery frame’s pivot
ghostMake Animation’s own: three whole frames, 384 by 48 from 900, 0, 448 by 80 from 0, 0 and 448 by 64 from 450, 012Loop0.5, 0.5
appearcells 64 by 48, six, from 900, 010Once0.5, 0.521
floatcells 64 by 80, seven, from 0, 08Loop0.5, 0.5
vanishcells 64 by 64, seven, from 450, 010Once0.5, 0.641

The flicker

  1. With ghost selected, click Add component and choose PropertyAnimationComponent. In the Animation tab, click Properties, press New property clips: it writes scenes/ghost.propanim and fills the component’s Clips. Press the + at the right of the tabs, type flicker and press Enter.
  2. Press Key a property and choose color. A row color appears, and its first dropdown reads Linear: set it to Nearest. Double-click the row’s lane at 0.00, the left end of the ruler: a first key, holding what the Sprite holds, white, 1, 1, 1, alpha 1.
  3. Turn Record on. Put the playhead at 0.2: click the time box once, 0.00 s, beside the Play and Stop buttons, so that its number is selected, type 0.2 and press Enter. In the Inspector click the Sprite’s Color, press U8 at the top left of the picker that opens so that it reads F, as in chapter 3, and set A to 0.55. The edit is the key: a diamond appears on the lane at 0.20, and nothing is written to the scene. Do the same at 0.35 with A 0.9, at 0.6 with 0.6, and at 0.8 with 1. The clip is 0.8 seconds long.
  4. Turn Record off. The color row is amber, which says the neutral pose does not hold it yet. Press Capture RESET: it writes the Sprite’s own white, alpha 1, at whatever time the playhead is. Press Save.
  5. In the Property Animation set Clip to flicker, tick Playing, and set Looping to Loop. Clips is ghost.propanim, and Speed stays 1.
Nearest
An interpolation, with Step, Linear and the others: between two keys the property takes the value of whichever key is nearer in time, so it jumps halfway between them and is never in between. The ghost’s alpha is only ever one of its five keys, which is a flicker and not a fade. Step would change on the key itself.
The ghost selected: the Viewport is framed on its box, a stretch of brick wall with the ghost’s faint first frame in it; the Inspector shows Position 100.0 and 200.0, an Animated Sprite with Animations ghost.anim, Clip appear, Speed 1.00, Playing unticked and Frame Size ticked, a Ghost component, and a Property Animation with Clips ghost.propanim, Clip flicker, Playing ticked, Speed 1.00 and Looping Loop; the Animation tab is on Properties, with the clips flicker, chosen, and RESET, 0.20 s, and one row, color, set to Nearest, with five keys along a ruler running from 0.00 to 0.90, and under it a line saying to double-click a lane to key what the property holds, or to turn Record on and edit it in the Inspector
The ghost, and its flicker at 0.2 seconds: one row, color, Nearest, five keys. Playing on its Animated Sprite is off.

The script

  1. In Assets, create a new script, ghost, and replace everything in ghost.rgs with the script below. Select ghost, click Add component and choose Ghost.
rgs ghost.rgs
/// The ghost waits half a second on its first frame, then appears, floats a
/// while and vanishes, and a second later does it all again: each clip that
/// plays once says when it has finished.
component Ghost {
    fn on_create() {
        await wait(0.5);
        play_clip("appear");
    }

    fn on_signal() {
        if heard("finished:appear") {
            play_clip("float");
            await wait(2.0);
            play_clip("vanish");
        }
        if heard("finished:vanish") {
            hide();
            await wait(1.0);
            show();
            play_clip("appear");
        }
    }
}
finished:appear
The signal the Animated Sprite sends when appear, a clip that plays once, reaches its end: heard("finished:appear") is true on that step. The script answers by starting float.
finished:vanish
The same for vanish. The script answers by hiding the ghost, waiting a second and showing it again, then starting appear from its first frame.
hide and show
hide() makes this node invisible and show() makes it visible again. The ghost is out of the scene between vanish and the next appear.
wait
await wait(2.0) suspends the function for two of the game’s seconds, then carries on from the next line: the ghost floats for two seconds before it vanishes. Without await in front it would not wait.
Six pictures of the ghost on the brick wall under a lantern, side by side: a speck of dark at its middle, a curl of dark smoke, a hooded figure with a blue skull and orange hands, the same figure faint and see-through, a thick swirl of dark smoke, and a ring of cyan sparks
Steps 20, 48, 100, 130, 195 and 220, 0.33, 0.8, 1.67, 2.17, 3.25 and 3.67 seconds in: the wisp it holds for half a second, the smoke it appears out of, floating, a low of its flicker, the smoke it goes into and the sparks it leaves.
NodeComponentFieldValue
ghostPosition100, 200
SpriteTextureghost.png
Anchor0.5, 1
Sort Layer6
Animated SpriteAnimationsghost.anim
Clipappear
Playingunticked
Frame Sizeticked
Property AnimationClipsghost.propanim
Clipflicker
Playingticked
LoopingLoop
Ghostthe script
Property clipLengthTrackKeys, in seconds
flicker0.8 scolor, Nearest0: 1, 1, 1, alpha 1; 0.2: alpha 0.55; 0.35: alpha 0.9; 0.6: alpha 0.6; 0.8: alpha 1
RESET0 scolor, Linear0: 1, 1, 1, alpha 1

A boss entrance

What it is for. A boss that drops into an empty arena. The camera holds the arena until it lands. Then the picture shakes, the camera comes on, and a shot swings in on the boss, tilted, a little closer, past where it will rest and back. The camera is held by a rig that is switched off, and a signal switches it on.

The demon’s sheet

  1. Copy the file below into art, from where the list says, and press Refresh in the Assets panel.
  1. Double-click scenes/boss.ron in Assets. In art, right-click demon-idle.png, choose Make Animation…, set Name to demon and press Create. It writes art/demon.png, a sheet of 1024 by 256, and art/demon.anim, with one clip, demon, the whole strip as one frame, 960 by 144.
  2. Select gallery, click + Add node, choose Node and rename it arena_mark. Set its Position to 320 and 180: the middle of the view, which the rig’s first shot will hold.
  3. Select gallery, click + Add node, choose Animated Sprite and rename it demon. Set its Position to 420 and -10: above the top of the view, with its feet, which are its node, off the screen. In its Sprite, drag art/demon.png onto Texture, set Anchor to 0.5 and 1 and Sort Layer to 5. In its Animated Sprite, drag art/demon.anim onto Animations and tick Frame Size. Clip stays idle.
  4. With demon selected, double-click art/demon.png. In the Sprite sheet tab set Cell to 160 and 144. Drag across the first six cells, Selected 6 x 1, press From selection: idle, 8 fps, Loop, Generate 1. In the Animation tab set pivot x to 0.501 and y to 0.889 on each of its six frames, and press Save. They are the middle of the body across and the row under its lowest drawn row down, measured on the sheet.
ClipFramesRateLoop
demonMake Animation’s own: the strip, 960 by 144 from 0, 0, one frame12Loop
idlecells 160 by 144, six, from 0, 0; every frame’s pivot 0.501, 0.8898Loop

The landing

  1. With demon selected, click Add component and choose AudioSourceComponent. Press + beside Triggers, open the new row, set Name to thud and drag impactSoft_heavy_000.ogg from Assets onto its Clip: the heavy landing thud that chapter 4 copied in.
  2. Click Add component and choose SignalComponent. Add one connection with the + on Connections: Signal landed and To camera. When the demon says landed, the camera hears it.
  3. In Assets, create a new script, boss, and replace everything in boss.rgs with the first script below. Select demon, click Add component and choose Boss.
rgs boss.rgs
/// The demon drops into the arena, lands with a thud, and tells the camera.
component Boss {
    fn on_create() {
        await wait(0.8);
        await anneal_by(vec2(0.0, 298.0), 0.5, .InQuad);
        play_sound("thud");
        emit("landed");
    }
}

The demon waits 0.8 seconds in an empty arena, then falls 298 units, from -10 to the boards at 288, in half a second, and the thud sounds as it lands.

The shot

  1. Select camera, click Add component and choose CameraRigComponent. Untick Enabled: the rig is switched off, and the camera is its node, at 320 and 180.
  2. Press the + on Framings twice and set the two rows from the table below: arena, which follows arena_mark and holds it still, and boss, which follows demon, is not Active and has the higher Priority.
  3. In Assets, create a new script, arena, and replace everything in arena.rgs with the second script below. Select camera, click Add component and choose Arena.
Fieldarenaboss
Namearenaboss
Subjectarena_markdemon
Activetickedunticked
Priority01
Zoom11.25
Rotation0°-3.44°
Duration0.50.9
EaseEaseInOutOvershoot
Dead Zone0, 00, 0
Soft Zone0, 00, 0
Offset0, 00, 40
Damping0, 00.25, 0.5
Time00
Smoothing0.20.2
Limit00
rgs arena.rgs
/// The camera holds the empty arena until the demon lands; then the picture
/// shakes, the rig comes on, and the shot swings in on the demon.
component Arena {
    fn on_signal() {
        if heard("landed") {
            set_flag("CameraRigComponent", "enabled", true);
            set_flag("CameraRigComponent", "framings.boss.active", true);
            shake(10.0, 0.6);
        }
    }
}
The camera selected: the Viewport looking through the game camera at the empty arena, a hall with a stained-glass window and a lantern at each side, the floor below; the Hierarchy lists gallery with camera, backdrop, floor, arena_mark and demon; the Inspector narrowed to the Camera Rig shows Enabled unticked and two framings open: 1 arena with Name arena, Subject arena_mark, Active ticked, Priority 0, Zoom 1, Rotation 0 degrees, Duration 0.50 s, Ease EaseInOut, and 2 boss with Name boss, Subject demon, Active unticked, Priority 1, Zoom 1.25, Rotation -3.44 degrees, Duration 0.90 s, Ease Overshoot, Offset 0.0 and 40.0 u and Damping 0.25 and 0.5 s
The rig as the scene saves it: Enabled off, the arena shot live and the boss shot waiting.
landed
The demon’s own signal, named nowhere else: emit("landed") sends it to the node its SignalComponent names, the camera, and the camera’s script hears it on the next step.
set_flag
Writes a true or false onto another component on this node. The first call writes the Camera Rig’s enabled, which switches the rig on. The second reaches into its list of framings by name: framings.boss.active makes the shot called boss live.
shake
Shakes the game’s camera by up to strength world units for seconds: shake(10.0, 0.6), ten units for six tenths of a second. A weaker shake during a stronger one changes nothing.
anneal_by
Moves this node by a vector over some seconds along a curve: await anneal_by(vec2(0.0, 298.0), 0.5, .InQuad) drops the demon 298 units in half a second. With await in front the script waits for it to arrive, and the thud sounds the step after. .InQuad starts slowly and speeds up: a fall.
Four pictures of the arena side by side: the demon, a blue-skulled figure in a dark cloak with red wings, high in the picture and falling; the demon on the floor at the right of the window with its wings out; the picture drawn in toward it and beginning to tilt, wings spread wide; and a closer, tilted picture with the demon big in the middle and the floor sloping
Steps 70, 80, 95 and 150, 1.17, 1.33, 1.58 and 2.5 seconds in: falling, landed, the shot swinging in and the shot at rest, a quarter closer and leaning a little more than three degrees.
NodeComponentFieldValue
arena_markPosition320, 180
demonPosition420, -10
SpriteTexturedemon.png
Anchor0.5, 1
Sort Layer5
Animated SpriteAnimationsdemon.anim
Frame Sizeticked
Audio SourceName, Clipthud, impactSoft_heavy_000.ogg
SignalSignal, Tolanded, camera
Bossthe script
cameraCamera RigEnabledunticked
Framingsarena and boss, from the table above
Arenathe script

Check it works

Five of the six scenes as the game draws them, each at the step its picture names. The sixth is under the list.

The hall with the knight in the middle, under the window, in a blue cloak, standing on the floor
The outfit at step 40: blue.
The hall panned to the right: a small candle on the boards at the left and, at the right edge, three red-cloaked knights in a row, facing left
Slow motion at step 110: the crowd in view, the finisher left behind the edge.
The hall a little dim, with a knight running at the left and three small gold lamps along the boards
The rewind at step 40: three lamps lit, the runner mid-stride.
The hall with a small olive tank on the boards, a little left of the middle
The tank at step 70, on its way out.
The hall with a small dark hooded figure, a blue skull in its hood, floating on the brick wall under the left lantern
The ghost at step 100, at full strength.