Build a GameCreating Shatterfield

2  Shots

Goal

The Ship flies, and now it learns to shoot. By the end of this chapter, Space fires small darts of cyan light from its nose. Four at most fly at once. Each one is gone in less than a second.

The game as it is at the end of this chapter. Click the game to play.

If your game and this chapter part ways, here is the chapter’s finished project. Open it and compare, or start Chapter 3 from it.

Shatterfield, Chapter 2 finishedZip of a Myth project · 5.0 MB

To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Beside it, OFL.txt is the license of the project’s font, Press Start 2P.

2.1 The Idea

Press Space. A small dart of cyan light leaves the Ship’s nose and streaks off across the dark. Press again, and again. Three darts, four. Press a fifth time, and nothing happens, until the first one disappears on its own.

There’s nothing to hit yet. No rocks, no saucers, only the Ship and its light. That’s all right. This chapter is about the gun, and the gun has rules of its own.

A Shot never flies far. It lives for 0.85 seconds. While it lives, it wraps around the edges the way the Ship does, and it always leaves the Ship at 20 meters a second, however fast the Ship itself is going.

In Chapter 1 there was one Ship. You placed it in the Space Scene yourself. Nobody places a Shot, though, because the game makes each one at the moment you fire. The Shot Object is a pattern, like a cookie cutter, and every dart on the screen is one cookie cut from it. Each copy is an instance, with its own place, its own speed and its own short life.

The gun waits a moment between Shots, too. That wait is a cooldown, a stopwatch with a name, started each time the gun fires. Until it runs out, the gun isn’t ready.

Three pieces of Logic make all of this. Ship Gun fires. Shot Lifetime ends each Shot. Motion Facing comes built in, like the two from Chapter 1, and it turns each dart to point the way it flies.

2.2 The Plan

Here’s how the pieces work together, read from left to right.

  • Space is pressed -> Ship Gun asks two questions. Is the gun ready? Are there fewer than four Shots in the air?
  • Both answers are true -> Ship Gun makes a Shot ahead of the Ship, plays the Fire sound, and starts its cooldown.
  • A Shot is made -> Shot Lifetime starts that Shot’s timer. The timer rings -> the Shot is gone.
  • A Shot flies -> Motion Facing points it along its path, and Wrap Around World brings it back from the edges.

Space belongs to a new Command, Fire.

2.3 Set Up

This chapter needs one picture, one sound and one key. The Shot Sprite and the Fire sound are already in your project, from the Starter Kit.

The key is yours to make. In Chapter 1, the Command arrived by itself, with Rotate-and-thrust Controls, but this one doesn’t. Open the Input Manager and press Add Command. Name it Fire, and leave its Input Type on Button. The new Command comes with one empty key row, which reads No Input Assigned. Click that row and press Space. Now it reads Space. The Set up card passage shows where these are.

A Button is a key that’s either down or up. It has no direction. That’s all a gun needs.

There’s nothing to place this time, and the next part says why.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Shot Sprite
SOUNDS (from the starter kit)
Fire
COMMANDS (Game Settings > Input Manager)
FireButton
KeybindingsSpace

2.4 The Shot

First comes the dart.

Make the Shot the way you made the Ship: Add > Object, a 2D Object, and its Sprite from the Component Toolbox. Its picture is the Shot Sprite, a small white dart with a cyan glow. It points up. Hold on to that, because the last part of this chapter turns on it.

A Shot has to fly past an edge to come back. Stay inside the scene, in its Transform’s Physics section, starts off, as on the Ship, so there’s nothing to set.

Its Collider waits for the rocks. It’s a small circle, and its Interactions list is Collide Only With, like the Ship’s, with nothing on it yet. So for now the Shot touches nothing at all. In Chapter 4, the Asteroids go on it.

Remember the promise in Chapter 1? The Ship’s Draw Order of 20 put it in front, and the Shots would get lower numbers. Here’s the first of them. The Shot’s Draw Order is 15, so it slides under the Ship, never over it.

Make the Shot without its Logic for now. The Logic and the Shared Variable at the bottom of its card come in the parts after this one.

ObjectShot
Transform
Bounding Box > SizeX 0.8125, Y 0.8125
Collider
Shape and Transform > ShapeCircle
Shape and Transform > Radius0.156
Tags and Interactions > InteractionsCollide Only With
Sprite
Scene Depth Draw Order15
SpriteShot Sprite
Transform > SizeX 0.8125, Y 0.8125
Logic
Shot Lifetime
Wrap Around World
Motion Facing
Shared Variables
facingOffset-90

Don’t place it in the Space Scene. A Shot sitting in the middle of the screen when the game starts would be a Shot nobody fired, and Ship Gun will make every one of them anyway.

So nothing new shows yet. Run the game, and the Ship flies as it did before. The Shot waits in your Resources until something asks for it.

2.5 A Short Life

Now each Shot gets an end.

2.5.1 Shot Lifetime

A Shot appears, flies, and after 0.85 seconds it’s gone. Nothing shoots it down. It removes itself.

You’ve seen this card before, as the example on Making what a card shows.

Shot Lifetime waits for two moments. The first is the moment the Shot is made. Right then, it starts a timer named shotLife, set to ring after lifeSeconds. Its Ring Once is Yes, so it rings a single time, and after that it’s quiet for good.

The second moment is that ring. When shotLife goes off, Destroy Instance removes the Shot. Only this one. Every Shot started its own timer when it was made, so each one ends on its own time.

Make it with Add > Logic Component, the Starter left on Empty Logic Component, as Thrust Feedback was, and build it from the top of the card down. That puts lifeSeconds first, and here the order matters.

lifeSeconds is a Shared Variable, the first you make yourself. Press the + on the Shared Variables card, at the left of the canvas. The Add Shared Variable window asks for four things. Type the Name, lifeSeconds. Its Kind starts on Changeable, so pick Constant. Leave its Type on Number. Type 0.85 as its Starting Value, the 0.85 seconds a Shot lives, and press Add.

Why first? The timer’s Action reads it, as @self.lifeSeconds. A box that names something the Logic doesn’t have yet turns its block red.

For the first Event, type Create into the Add Event window’s filter, and pick it. It lands with its Mode 1 beside it, as in Chapter 1. Right-click Mode 1, choose Add unattached Action here, and pick Start Recurring Timer. Connect to sequence joins it.

Its Timer Name is one of the boxes that take a name as it is. Type shotLife, bare. Once you’re done, the block draws it between quote marks, as “shotLife”. That’s only how it shows a name. Ring Once is a list that starts on No. Pick Yes.

The second Event takes three small windows. Type Recurring Timer into the filter, pick it, and press OK. Myth Engine asks which timer it should hear. Pick Type a timer name… and press OK, and a last window asks for the Timer Name. Type shotLife there, bare again. Then give that Event its one Action, Destroy Instance, from a right-click on its own Mode 1, and join it the same way.

Myth names the two Events itself, so you won’t type the card’s headings anywhere.

Logic ComponentShot LifetimeAttach to: Shot

A Shot disappears lifeSeconds after it is fired.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
lifeSecondsNumberConstant0.85
When Created
WhenCreate
1Start Recurring Timer
Timer Name
shotLife
Repeat Every (s)
@self.lifeSeconds
Ring Once
Yes
On the Shot Life Timer
WhenRecurring TimerTimer name: shotLife
1Destroy Instance

Attach Shot Lifetime to the Shot. Back in Components, lifeSeconds has joined the Shot’s Variables, at 0.85, and the Shot’s card has no line for it. Every Shot keeps that.

Next comes Wrap Around World, the same one the Ship has. You made it in Chapter 1, and one Logic Component can go on as many Objects as need it. Attach it below Shot Lifetime, so the Shot’s Attached Logic reads like the card from top to bottom. Its seamlessEdges arrives at Yes, as on the Ship. There’s nothing to type.

Still nothing to see. One more part, and the Shots fly from the Ship’s nose at last.

2.6 The Gun

Now the Ship learns to shoot.

2.6.1 Ship Gun

Every press of Space asks the gun for a Shot, and the gun doesn’t always say yes.

This is the chapter’s one hard piece. Take it slowly.

Ship Gun has a single Event, When Fire Is Pressed, and it runs once each time Space goes down. Holding Space doesn’t fire again, so every Shot is one press.

Before it fires, Ship Gun asks two questions. First, is the gun ready? Check Cooldown Ready looks at the stopwatch named fireCooldown and keeps its answer as isGunReady. Second, is there room for one more? Check Instance Count counts the Shots in the game, compares them with maxShots, and keeps that answer as hasRoomForShot.

One If reads both answers, and the gun fires only when both are true. Asking first and acting after keeps the If small. It’s one block that checks two answers, with all the work inside it.

The firing starts with Create Instance, the Action that cuts a new Shot from the pattern. Which pattern? Ship Gun has none built in. Its Shared Variable shot holds an Object, and the Ship hands it the Shot, on the Ship’s own card.

The Shot’s starting place is measured from Self. Self always means the Object a Logic sits on, so in Ship Gun it’s the Ship, wherever the word turns up. Measured from the Ship, up isn’t the top of the screen. It’s the way the Ship’s picture points, toward its nose, however the Ship has turned. That’s why muzzleDistance goes in Y. The Shot starts that far out from the Ship’s middle, right at the nose.

How it moves comes next. The Launch Angle of 90 is straight up, and Angle Relative To adds the Ship’s own turn to it. So the Shot leaves straight out of the nose. That list has no Self. Its two choices are World, which is the screen, and Creator, whatever made the Shot. Creator is the Ship again, under another name.

It looks as if a Shot fired from a moving Ship should carry the Ship’s speed along. It doesn’t. Every Shot leaves at shotSpeed, 20 meters a second, and the Ship’s own speed is never added.

Then the Fire sound plays. Control Sound’s Operation starts on Play, and the card leaves it there. Last, Start Cooldown sets fireCooldown running for 0.11 seconds.

Now build it. Make Ship Gun the way you made Shot Lifetime, and follow its card from the top, one step at a time. The passages on Events, Actions and If have the windows. Skip what Ship Gun doesn’t use yet: a second Mode, Custom Events, the round handles, Change Variable, Else and Repeat.

SHARED VARIABLES first, each with the + on that card, as lifeSeconds was. The first, shot, holds an Object. In the window’s Type list, pick Object. It’s far down, among the kinds of thing a project holds, after Material. Its Starting Value turns into a button that reads None. Leave it there, because the Ship fills it in. The four numbers after it are each a Number, Constant, with the Starting Value the card gives it.

The When bar is Command Pressed. Pick it in the Add Event window, and Myth Engine asks for its Command in a second one. Pick Fire. Every Action of this Event goes in from a right-click on its Mode 1, or on the last block of its line, as in Shot Lifetime.

Steps 1 and 2 each end with a box after the word as. That’s the Action Name. Type isGunReady and hasRoomForShot over the names Myth put there. Cooldown Key takes its name bare, like a Timer Name: type fireCooldown, and the block draws its own quotes. Check Instance Count’s Object is a list. It offers @self.shot, the Shared Variable you made, so pick it there. Its Value is a box. Type @self.maxShots.

Step 3 is the If, in the Action List’s Logic group. Join it like the others. Its condition is the one box on the If. Click it, type the card’s line whole, && and all, and press Enter.

Steps 4 to 6 sit in the card’s Then panel. On the canvas, that panel is the line marked True, running down from the If. Right-click the If and choose Add inside Then. Pick Create Instance in the window that opens, and press OK. It lands under the If, not joined yet, so join it with Connect to sequence. It joins the True line.

Create Instance is the biggest block yet. Its Object to Create is a list like Check Instance Count’s, so pick @self.shot there too. Its Position section holds Position Relative To, and that list starts on World. Pick Self. Y takes @self.muzzleDistance. Its Created As box names the Shot it made: type newShot. No Action on this card reads that name, but you’ll see it again in a moment.

Steps 5 and 6 go in the same way. Right-click the If, choose Add inside Then, and pick Control Sound, then Start Cooldown. Connect to sequence joins each one to the True line, after Create Instance. Control Sound’s Sound is a list. Pick Fire.

One block needs a second look. A new Action placed after a Create Instance points at whatever that Create Instance made. Control Sound has no Applies To at all, because a sound plays for the whole game, so it has nothing to change. Start Cooldown does. It arrives with its Applies To on newShot. But the cooldown belongs to the gun, not to a Shot, and a card with no Applies To line means Self. Open that list and pick Self, under Attached Object.

Logic ComponentShip GunAttach to: Ship

Fire launches a Shot from the Ship's nose, with a cooldown and a limit on Shots in flight.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
shotObjectConstantNone
shotSpeedNumberConstant20
muzzleDistanceNumberConstant1.188
fireCooldownSecondsNumberConstant0.11
maxShotsNumberConstant4
When Fire Is Pressed
WhenCommand PressedCommand: Fire
1Check Cooldown Ready
Action Name
isGunReady
Cooldown Key
fireCooldown
2Check Instance Count
Action Name
hasRoomForShot
Object
@self.shot
Operator
<
Value
@self.maxShots
3IfisGunReady.value == true && hasRoomForShot.value == true
Then
4Create Instance
Object to Create
@self.shot
Position Relative To
Self
Y
@self.muzzleDistance
Launch Speed
@self.shotSpeed
Launch Angle
90
Angle Relative To
Creator
Created As
newShot
5Control Sound
Sound
"Fire"
6Start Cooldown
Cooldown Key
fireCooldown
Seconds
@self.fireCooldownSeconds

Now give the Ship its gun. Its card this time is a Changes card, the first in the book, and the green lines are new. Attach Ship Gun to the Ship, fourth.

Switch back to Components, and Ship Gun’s five Shared Variables have joined the Ship’s, under Variables. The Value of shot is a button that reads None. Press it. Pick Shot in the list that opens. The four numbers below it arrive at the gun’s own values, 20 and the rest. The card has no lines for them, so leave them as they are.

ObjectShipAddDelete
Transform
Bounding Box > SizeX 2.4375, Y 3.0625
Collider
Shape and Transform > ShapeCircle
Shape and Transform > Radius0.594
Tags and Interactions > InteractionsCollide Only With
Sprite
Scene Depth Draw Order20
SpriteShip Sprite
Transform > SizeX 2.4375, Y 3.0625
Logic
Rotate-and-thrust Controls
Wrap Around World
Thrust Feedback
Ship Gun
Shared Variables
topSpeed13.75
turnSpeed290
thrust13.125
coastDrag1.719
shotShot

Run the game, and press Space.

A dart leaves the nose with the Fire sound and streaks away. Tap Space fast. One, two, three, four, and the gun goes quiet until the first Shot is gone.

Turn the Ship a little now, and fire. The dart flies the right way, off to the side, but its nose still points straight up. That’s the last piece.

2.7 Pointing the Way It Flies

Now every Shot turns its nose to its path.

2.7.1 Motion Facing

Fire to the right, and the dart points right. Turn the Ship toward the bottom of the screen and fire, and the dart points down. Its picture always faces the way it’s going.

This one comes built in, like the two from Chapter 1. Make it with Add > Logic Component, and pick Motion Facing from the Starter list, in its Movement group. A Logic Component that comes built in shows that window.

Its one Shared Variable is facingOffset. Motion Facing expects a picture that faces right, and the Shot Sprite faces up. Turn an up-facing dart a quarter turn clockwise, and it faces right. That turn is facingOffset, -90 on the Shot’s card. With it, Motion Facing starts from the dart it expects, and the nose leads, whichever way the Shot flies.

Logic ComponentMotion FacingAttach to: Shot

The Object turns to point the way it is moving.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
facingOffsetNumberConstant0
Built in: this Logic Component comes whole from the engine's template.
You add it and set its Variables on the Object; you do not build its Events.

Attach it to the Shot, third. Back in Components, type -90 into facingOffset’s Value, under the Shot’s Variables.

Run the game, and fire in every direction.

Play it!

Watch for each of these.

  • Press Space. A small cyan dart leaves the Ship’s nose, and the Fire sound plays.
  • Hold Space down. One Shot comes out, and no more, because the gun fires once for each press.
  • Tap Space as fast as you can. Four Shots, never five.
  • Fire straight up from the middle of the screen. The dart leaves at the top, comes back at the bottom, and disappears before it reaches the Ship again.
  • Every Shot is gone in under a second, whether it’s near anything or not.
  • Turn and fire. Each dart points the way it flies.
  • Fly at top speed and fire straight ahead. The Shot pulls away from the Ship slowly, since it doesn’t take the Ship’s speed.
  • The cooldown is too short to feel, so stretch it. In the Ship’s Variables, type 1 into the Value of fireCooldownSeconds. Now tap Space fast. One Shot a second, however fast you tap. Then type 0.11 back.
How it works

Each of the four pieces in this chapter does one job. Ship Gun decides when a Shot may leave. Shot Lifetime ends it. Motion Facing points it, and Wrap Around World carries it across the edges, as it carries the Ship.

Underneath them is one idea. The Shot is a pattern, and the game cuts a new instance from it each time you fire. Each instance carries the whole Shot with it: its picture, its Logic and its own timer. That’s why four Shots can each end at their own moment.

2.8 If It Didn’t Work

Something may be a little off the first time, and that’s normal. Fire, watch what the game does, and think it through one piece at a time.

Space does nothing at all? Perhaps the Command isn’t there. Open the Input Manager and look for Fire, a Button, with Space bound to it. If it’s there, open the Ship and switch to Logic, where Ship Gun should be in the Attached Logic. In Components, the Value of shot should read Shot.

Still nothing? Look on Ship Gun’s canvas for a block standing apart, with no arrow into it. That block does nothing yet. Right-click it, and Connect to sequence puts it to work.

The Fire sound plays, but no dart appears? Then the If let the gun fire, and Create Instance is the step to look at. Is it standing apart from the True line? Join it with Connect to sequence, and look at its Object to Create, which should be @self.shot. And on the Ship, the Value of shot should read Shot, not None.

The If has a red edge? A name in its box isn’t one Ship Gun knows. Check the spelling against the card, letter by letter, with .value after both answers.

Check Instance Count’s list has no @self.shot? Then shot’s Type isn’t Object. On the Shared Variables card, the bin beside shot deletes it, and you add it again, with Object picked in its Type list.

After four Shots, the gun goes quiet for good? The Shots aren’t ending. They’re still out there, wrapping round and round. Open Shot Lifetime. The second Event should name the timer shotLife. That’s the Timer Name in the first Event’s Action, spelled the same, because a timer and its Event find each other by that name. If they differ, type the Timer Name again to match. Under that Event, Destroy Instance should sit joined to the line.

Shots never end, though the names match? Perhaps Destroy Instance went onto the first Event’s line, from the wrong Mode 1. Each Event has its own. Right-click the stray block, choose Delete, and add it again from the second Event’s Mode 1.

With fireCooldownSeconds at 1, the gun still fires on every tap? Look at the two cooldown Actions in Ship Gun, Check Cooldown Ready and Start Cooldown. Both need the Cooldown Key fireCooldown, spelled the same. And Start Cooldown’s Applies To should be Self, not newShot, the Shot it made a moment before.

Shots come out of the Ship’s side, or its tail? Look at Create Instance. Position Relative To is Self, and Angle Relative To is Creator, with a Launch Angle of 90. And muzzleDistance belongs in Y, not X.

Shots fly the right way but point backward? The facingOffset may be 90, without the minus, and that puts each dart half a turn off. Make it -90, and they turn around.

Each of these is a small fix. Make it, fire again, and you’re back with the chapter.

The catch-up download in the Goal is this chapter, finished, and you can hold yours up against it. Save your project first. Then unzip the download, as its button says, and open the .mythproj file inside with File > Open Project…. It takes your project’s place for a while. File > Open Recent brings yours back.

Challenge

Ship Gun’s numbers are the whole character of the gun, and they’re yours to try. They’re in the Ship’s Variables, and any number you type into a Value takes the gun’s place. Allow one Shot at a time, and every press counts. Let maxShots climb while you shorten the cooldown, and the Ship sprays light across the dark. Or lower shotSpeed and watch your darts drift. Chapter 3 starts from the card’s numbers, so write down any you’d like to keep, and type the gun’s own numbers back: 20, 1.188, 0.11 and 4.

Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).

A Ship that flies, and a gun that answers. In Chapter 3, the Ship finds a way out.