Skip to content

Tutorials · Ship Evader · Chapter 5

Firing

25 minutes Uses: Sprite, Spawner, translate, destroy_self, held, spawn

By the end

  • A laser saved as an artifact: a node the game makes copies of while it runs
  • A Spawner on the ship, which says what the name laser means
  • A ship that fires about six lasers a second while Space is held, each flying up and removing itself
  • A field the Inspector does not show, for the script’s own counting
On this page

The laser’s script

A laser has to exist many times over, each made as the ship fires it. You build it once and save it to a file, and the ship asks for copies. Start with what every copy does: fly up the field, and go once it is past the top.

  1. In Assets, press Create, choose New script, type laser, and press Enter. The editor writes laser.rgs into the project folder.
  2. Click the Code tab, then laser.rgs in its Scripts list, replace everything in it with the script below, and press Ctrl+S.
rgs laser.rgs
/// A laser: it flies up the field, and removes itself once it is 100 units
/// past the top.
component Laser {
    /// How fast the laser flies.
    #[suffix("u/s")]
    speed: f32 = 900.0,

    fn loop(dt: f32) {
        // Up is toward negative y: the world's y grows downward.
        translate(vec2(0.0, -speed * dt));
        // camera_view().y is the top edge of the field.
        if position().y < camera_view().y - 100.0 {
            destroy_self();
        }
    }
}

translate moves the node by a vector each step: speed * dt is how far the laser flies in one, and it flies up, so toward negative y. destroy_self removes the node, once it is 100 units past the top edge, out of sight.

Make the laser once

  1. Click the Viewport tab, select ship, and drag art/laserBlue01.png from Assets into the Viewport. It lands beside the ship, under Game.
  2. Double-click its row in the Hierarchy, type laser, and press Enter.
  3. Set its Position to 0 and 0. A copy is placed wherever the ship asks for it, so the laser itself sits at the world’s origin.
  4. Leave its Sprite’s Size at the picture’s own 9 by 54, and set Sort Layer to -1, so a laser leaving the ship draws under its nose rather than over it.
  5. Press Add component and choose Laser. Its Speed is 900.

Save it as an artifact

An artifact is a node and its children saved to a file of their own, which a scene or a script can copy. Scenes and nodes says more about them.

  1. Right-click laser in the Hierarchy and choose Save as artifact….
  2. In the save dialog, make a folder called artifacts, open it, and save the file as laser.artifact.
  3. Delete the laser left in the scene: select it, press Delete, and choose Delete. It is a copy of that file now, and the game makes copies of its own.

To change the laser later, double-click laser.artifact in Assets, and the editor opens it as the scene you are editing; if the open scene has edits not on disk, it asks Save the open scene? first. Save the laser with Ctrl+S, then double-click scenes/game.ron to go back to the game.

The laser artifact open in the editor, “laser.artifact” in the title bar: one node, laser, at position 0 and 0, and the Viewport framed on the long blue laser

The ship’s Spawner

A script asks for a copy by a name, "laser", not by a file. The node’s Spawner says which artifact each name means. Making copies with spawn has the whole of it.

  1. Select ship, press Add component, and choose SpawnerComponent.
  2. Press + beside Entries, and open the new [0] row.
  3. Type laser as the new entry’s Name, and drag artifacts/laser.artifact from Assets onto its Artifact.
The ship selected, its Inspector narrowed to the Spawner: one entry, Name laser, Artifact “laser.artifact”

Fire while Space is held

The ship’s script gains two fields and a few lines at the end of its loop. Click the Code tab, then ship.rgs in its Scripts list, replace everything in it with this, and press Ctrl+S:

rgs ship.rgs
/// The player's ship: it flies where `move` points, never leaves the field,
/// and fires lasers while `fire` is held.
component Ship {
    /// How fast the ship flies.
    #[suffix("u/s")]
    speed: f32 = 420.0,
    /// How close the ship's centre may come to an edge of the field.
    #[suffix("u")]
    margin: f32 = 50.0,
    /// Seconds between two lasers while fire is held.
    #[suffix("s")]
    fire_interval: f32 = 0.16,
    /// Seconds until the ship can fire again.
    #[hidden]
    cooldown: f32 = 0.0,

    fn loop(dt: f32) {
        let view = camera_view();
        let next = position() + direction("move").normalized() * (speed * dt);
        move_to(vec2(
            clamp(next.x, view.x + margin, view.z - margin),
            clamp(next.y, view.y + margin, view.w - margin),
        ));

        if cooldown > 0.0 {
            cooldown -= dt;
        }
        if held("fire") && cooldown <= 0.0 {
            // 50 units above the ship's centre: up is toward negative y.
            spawn("laser", position() + vec2(0.0, -50.0));
            cooldown = fire_interval;
        }
    }
}
held
Whether an action’s key is down on this step. held("fire") stays true for as long as Space is held.
spawn
Makes a copy of the artifact the Spawner calls laser, at a point in the world: 50 units above the ship’s centre. The copy is a node of its own at the top of the scene, so it does not follow the ship; its own script flies it.
cooldown
Seconds until the ship may fire again. #[hidden] keeps it out of the Inspector: it is the script’s own counting, not something to tune. Each step takes dt off it, and a laser fires once it has run out and fire is held, setting it to fire_interval again.

So holding Space fires a laser at once and then one every ten steps, about six a second. Click the Viewport tab, then press Ctrl+S: the scene stores the Ship’s new Fire Interval beside its Speed and Margin.

Check it works