Skip to content

Tutorials · Cameras and Framing · Chapter 11

The lens

40 minutes Uses: Viewport, in_group, emit_to, heard, set_flag

By the end

  • A town square built inside the castle, east of every room, and a lens on the lens room’s back wall that is the only way to see it
  • WhenVisible: a view that draws only while something on the screen shows it
  • A portrait of the watchman, a Subtree view that is Once: painted as the scene opens, and kept
  • An easel whose script unticks the view’s Drawn, so the portrait is painted again; Never on a copy; and what a view costs
On this page

A square out of sight

A view sees only its own scene, so a lens on the town has to look at a town inside the castle: a set of its square, built far east of every room, where no camera of his and no map ever looks. Every node in this chapter goes in the castle, in this order.

  1. With castle.ron open, select castle, press + Add node and choose Node. Rename it square and set its Position to 3200, 0: east of the beacon room’s far wall, at x 1984, and past all the map can see.
  2. Select square, add a Tilemap and rename it square-street, at 0, 0. In its Tilemap, drag art/street.tilemap onto Tilemap: the town’s own street. Set Sort Layer to Ground, and leave Tile Size at 16, 16 and Collision ticked.
  3. Select square, add a Sprite and rename it square-houses, at 1280, 24. Drag art/night-town-background-town.png onto Texture, and set Size to 512, 99, Anchor to 0.5, 1 and Sort Layer to Town.
  4. Select square, add a Sprite and rename it square-sky, at 1280, -120. Drag art/night-town-background-sky.png onto Texture, set Draw Mode to Tiled, Tile Size to 96, 224, Size to 288, 224 and Sort Layer to Sky, and leave Anchor at 0.5, 0.5.
  5. Press Ctrl+S. Open scenes/town.ron, right-click fog and choose Copy. Open scenes/castle.ron again, right-click square and choose Paste as child. Rename it square-fog and set its Position to 1280, -8. Remove its ParallaxComponent with the bin at the right of its header. In its Particles, set the Shape’s box to 256 by 48 and Rate to 3.
The Sprite’s Draw Mode Tiled, Tile Size
Tiled repeats the picture across the Sprite’s Size, a copy every Tile Size: 288 wide at every 96 is three whole copies of the sky. Simple, as it comes, stretches one copy over the Size.

The street is the town’s own art/street.tilemap, so the castle carries a second copy of its stone, solid too, where nobody walks. The street’s cap hides the houses’ last 24 rows, so they stand on it. The sky is tiled because the square has no plane to repeat it: none of the four has a Parallax. The town’s fog spreads 6 puffs a second over 1800 units; over 256, as many would be about 0.85 a second, a faint wash once the lens draws the square at a quarter of its size, so 3 a second makes a mist round the houses’ feet.

The lens

  1. Select castle, press + Add node and choose Viewport. Rename it lens-eye and set its Position to 4480, -72: the middle of the square, 72 above its street.
  2. In its Viewport, choose New render target for Texture. Set Size to 64, 64, Zoom to 0.25 and Update to WhenVisible. Add four Layers: Sky, Town, Fog and Ground. Set Clear Colour to 0.012, 0.020, 0.040, alpha 1, the camera’s Background.
  3. Select castle, add a Sprite and rename it lens, at 1400, -536, on the lens room’s back wall. Click Texture and choose lens-eye. Set Size to 64, 64 and Sort Layer to Fixtures, and leave Anchor at 0.5, 0.5.
  4. Add another Sprite under castle, lens-bezel, at the same 1400, -536. Drag art/lens-bezel.png, which came with the starter, onto Texture, and set Size to 80, 80 and Sort Layer to Props: a brass ring round a clear window 64 across.
  5. Press Ctrl+S.
The editor with lens-eye selected and framed at 236.7%: the square built in the castle, a row of grey houses with lit windows on the street’s mossy stone, faint fog at their feet, and behind them, in the editor, the castle’s brick wall with a lantern; a pale square frame round the middle of it with a cross at its centre; the Hierarchy shows square with square-street, square-houses, square-sky and square-fog under it, then lens-eye, lens, lens-bezel, sitter with sitter-figure, portrait, portrait-frame and easel; the Inspector shows Position x 4480.0 and y -72.0 and the ViewportComponent: Texture lens-eye, Size 64.0 and 64.0 px, Zoom 0.25, Update WhenVisible, Subtree unticked, Layers 4 items, Sky, Town, Fog and Ground, the Clear Colour a night blue, Drawn unticked
The square, and the frame of what the lens’s eye sees: 256 by 256.
WhenVisible
Draws in every frame in which something on the screen shows the view’s texture, and in no other: here, while the lens is in the picture.

lens-eye sees 256 by 256 round its own place, x 4352 to 4608 and y -200 to 56: the square’s sky, houses, fog and street, drawn into a 64-pixel texture and shown by lens under its bezel. Its Layers leave out Buildings, so the castle’s wall, which the editor draws behind the square, is not in it.

The game in the lens room, two brass rings on dark riveted plates on the back wall: at the left the lens, the town square at night, grey houses with lit windows over a strip of mossy stone, fog at their feet; at the right the portrait, the watchman standing on a brown ground, and before its lower left corner the watchman himself, on the floor; the map in the top right corner
The lens, and beside it the portrait.
The lens close up: the square at night in its brass ring, grey houses with lit windows over the mossy stone, puffs of fog drifting slowly along the houses’ feet
The lens is a live view. Watch the fog along the houses’ feet drift: the square is drawn again in every frame the lens is on the screen.

The square is built where no camera of his and no map ever shows it, and the lens is the only way to see it. In the great hall only the map draws; in the lens room the lens draws too, a second pass; back in the hall, one again. The lens’s texture is kept after he leaves: the view stops drawing, and the texture is let go only when the scene changes.

The lens draws only the layers it is given. Here it is as made, and on a copy whose square-houses is on Props, a layer its Layers leave out:

The lens close up, as made: grey houses with lit windows over the mossy stone, fog at their feet, a dark night sky above
As made: Sky, Town, Fog and Ground.
The lens close up on the copy: the same sky, fog and stone, and no houses
The houses on Props: the lens leaves them out.

A portrait, painted once

Beside the lens hangs a portrait of the watchman. It is a view too, of a figure of him standing far out of sight, but of the figure alone, and painted a single time.

  1. Select castle, add a Viewport and rename it sitter, at 2600, -800. In its Node fold, type sitter into the Groups box and press Enter.
  2. In its Viewport, choose New render target for Texture, set Size to 64, 64, leave Zoom at 1, set Update to Once, tick Subtree, and leave Layers empty. Set Clear Colour to 0.18, 0.10, 0.06, alpha 1: a brown ground.
  3. Select sitter, press + Add node and choose Animated Sprite. Rename it sitter-figure, at 0, 24. In its Sprite, drag art/watchman.png onto Texture, and set Size to 38, 48, Anchor to 0.5, 1 and Sort Layer to Figures. In its Animated Sprite, drag art/watchman.anim onto Animations, set Clip to idle and tick Frame Size.
  4. Select castle, add a Sprite, portrait, at 1512, -536: Texture sitter, Size 64, 64, Sort Layer Fixtures. Add another, portrait-frame, at the same place: Texture art/lens-bezel.png, Size 80, 80, Sort Layer Props.
  5. Press Ctrl+S.
The Inspector of sitter: Name sitter, Groups sitter with a cross beside it, Position 2600.0 and -800.0; the Portrait script; its ViewportComponent: Texture sitter, Size 64.0 and 64.0 px, Zoom 1.00, Update Once, Subtree ticked, Layers 0 items, Clear Colour brown, Drawn unticked
The sitter, with the script the easel needs, made below.
Once
Draws when its Drawn is unticked, once, and ticks it. Saved unticked, as it comes, it draws on the first frame of its scene and keeps that picture.

With Subtree ticked, the view draws only the nodes under sitter: the figure, on its brown ground, and nothing of the room round it. The first frame of the castle paints the portrait, so it is painted the moment the scene opens, in the great hall too, and its texture is kept from then on. Far away, the figure goes on through its idle clip, and the portrait does not change.

The map keeps the two frames, which are on Props, and leaves out what they frame, on Fixtures: on the map they are two rings with empty windows.

The map in its brass frame, close up, taken in the lens room: on the slate, the room’s floor along the bottom and a gallery above, two small rings side by side with the slate showing through them, and him, a red speck, by the rings
The lens room on the map: two empty rings.

Sitting again

To paint the portrait again, the view must be told it has not drawn: its Drawn, which a script can untick. The easel on the floor at the portrait’s lower right is where he stands to ask.

  1. Select castle, press + Add node and choose Area. Rename it easel and set its Position to 1556, -480. In its Area’s Shape, set Size to 48, 48 and Offset to 0, -24: x 1532 to 1580, on the floor.
rgs easel.rgs
/// The easel before the portrait. The watchman stepping up to it tells the
/// sitter, which is in the group `sitter`, to sit again.
component Easel : Area {
    fn on_enter(other: NodeId) {
        if in_group(other, "watchman") {
            emit_to("sitter", "sit");
        }
    }
}
rgs portrait.rgs
/// The painter of the portrait, on the view that draws it. The view is Once:
/// it draws one time and keeps what it drew. When the easel says `sit`, this
/// unticks the view's Drawn, so the view counts as not yet drawn and paints
/// the sitter again, as he is now.
component Portrait {
    fn on_signal() {
        if heard("sit") {
            set_flag("ViewportComponent", "drawn", false);
        }
    }
}
  1. Make easel.rgs and portrait.rgs as in chapter 7: in Assets, click Assets in the path bar, then Create, New script, the name and Enter, and the script above in the Code tab, then Ctrl+S.
  2. Select easel, press Add component and choose Easel. Select sitter, press Add component and choose Portrait. Press Ctrl+S.
set_flag(component, field, value)
set_flag("ViewportComponent", "drawn", false) unticks the Drawn of the view on this node, as a click in the Inspector would. A Once view whose Drawn is unticked draws on the next frame.

His body enters the easel’s reach at x 1526, and at its middle, x 1556, he stands beside the portrait’s lower right, the whole figure clear in its window. The easel tells the group sitter, the sitter unticks Drawn, and the view paints the figure again, on whatever picture of its clip it has reached, and keeps that. The camera is held against the room’s east wall there, so neither frame moves on the screen.

Never is for a view that waits to be asked; its real case is chapter 13’s security monitor. Here it is on a copy whose sitter is Never, its Drawn ticked: the portrait shows only its brown Clear Colour, until he steps up to the easel and the script unticks Drawn.

Never
Draws only when its Drawn is unticked, once, and ticks it. Saved with Drawn ticked, it draws nothing and shows its Clear Colour until a script unticks it. Saved unticked, it draws on its scene’s first frame, as Once does.
The portrait close up on the copy: the brass ring round a plain brown disc, nothing painted in it; the watchman’s head at the lower left, in the room
Never, Drawn ticked: the Clear Colour alone.
The same portrait after he has walked to the easel: the watchman standing in the brown disc; at the right edge, part of him in the room
At the easel: Drawn unticked, and the view paints the figure.

What a view costs

Each view that draws is a pass of its own: the scene walked again for the view’s camera, and drawn into its texture. In the great hall the map’s is the one view pass a frame; in the lens room the lens’s makes two; back in the hall there is one again. A copy with the lens at Always draws it in the hall too: two. Each texture is held at 8 bytes a pixel: in the hall, the map’s 96 square and the portrait’s 64 square, 106,496 bytes; from the lens room on, 139,264, with the lens’s kept.

Who moves the camera

Nothing new. A view’s camera is its own node: lens-eye and sitter stand where they were put, and nothing moves them, so the lens always shows the same stretch of the square. In the lens room the castle’s camera is held against the room’s east wall, with both frames in the picture.

The square, the lens and the portrait

Node, under castle unless it saysComponentFieldValue
squareNodePosition3200, 0
square-street, under squareTilemapTilemap, Sort Layerart/street.tilemap; Ground
square-houses, under squareNodePosition1280, 24
SpriteTexture, Size, Anchor, Sort Layerart/night-town-background-town.png; 512, 99; 0.5, 1; Town
square-sky, under squareNodePosition1280, -120
SpriteTexture, Draw Mode, Tile Sizeart/night-town-background-sky.png; Tiled; 96, 224
Size, Sort Layer288, 224; Sky
square-fog, under squareNodePosition1280, -8
ParticlesShape, RateBox 256 by 48; 3; the rest the town’s fog’s, no Parallax
lens-eyeNodePosition4480, -72
ViewportTexture, Size, ZoomNew render target; 64, 64; 0.25
Update, LayersWhenVisible; Sky, Town, Fog, Ground
Clear Colour0.012, 0.020, 0.040, 1
lensNodePosition1400, -536
SpriteTexture, Size, Sort Layerlens-eye; 64, 64; Fixtures
lens-bezelNodePosition1400, -536
SpriteTexture, Size, Sort Layerart/lens-bezel.png; 80, 80; Props
sitterNodePosition, Groups2600, -800; sitter
ViewportTexture, Size, ZoomNew render target; 64, 64; 1
Update, Subtree, LayersOnce; ticked; none
Clear Colour0.18, 0.10, 0.06, 1
Portraitthe script
sitter-figure, under sitterNodePosition0, 24
SpriteTexture, Size, Anchor, Sort Layerart/watchman.png; 38, 48; 0.5, 1; Figures
Animated SpriteAnimations, Clip, Frame Sizeart/watchman.anim, idle, ticked
portraitNodePosition1512, -536
SpriteTexture, Size, Sort Layersitter; 64, 64; Fixtures
portrait-frameNodePosition1512, -536
SpriteTexture, Size, Sort Layerart/lens-bezel.png; 80, 80; Props
easelNodePosition1556, -480
AreaSize, Offset48, 48; 0, -24
Easelthe script

Check it works