Overview of the demo’s structure

In this lesson, we’ll look at how we’ll structure the project and why.

There are three important parts to the demo:

  1. The game itself, which is made up of the Synchronizer and HitSpawner.
  2. The UI canvas layer that contains the user interface.
  3. The pattern editor we’ll use to create patterns that determine where HitBeats and HitRollers appear.

We have a separate gameplay and user interface layer to prevent coupling.

If a component in the gameplay layer needs to interact with the UI layer (such as a metronome that pulses with the beat), we’ll use the observer pattern to communicate between them.

We make sure components such as the Synchronizer and HitSpawner have distinct roles to allow for easy modification and extension.

Gameplay code diagram

The game’s broken down into small components (scenes) that get used and instantiated by larger systems.

Here’s a simplified diagram of how the various components interact, excluding the user interface.

The gameplay layer

When the demo first loads, the HitSpawner parses the patterns from Patterns before removing them from memory.

When the player selects a music track to play, the HitSpawner loads the corresponding pattern.

The Synchronizer compares the playtime of a track against its beats per minute to determine whether there’s a half-beat at that point in the track.

When the HitSpawner receives the signal that the track has reached a new half-beat, it instances the next interactable scene in the queue so the player can tap or interact with it.

We use signals to communicate changes between the gameplay layer and the UI layer.

For example, when the player clicks a HitBeat with the right timing, we need to update the score.

The user interface

We also have three user interface scenes in the UI layer. When they’re not visible, they no longer take input from the player.

The pattern editor

We use the Godot editor to create track patterns. These patterns are scenes that dictate where the HitBeats and HitRollers spawn and when.

To avoid having lots of clutter on the screen, each pattern has children which group HitBeats and HitRollers together in chunks.

The HitSpawner parses the selected pattern at run-time and instances the next HitBeat or HitRoller so the player can interact with it. We design a unique pattern for each track.

In much the same way as a level designer in a platformer game might place enemies, we decide where the interactable objects go.

These objects are dedicated placer scenes with the tool keyword to update their sprite while working in the editor.

They’re later parsed and “swapped out” with their run-time counterparts, which the player interacts with.

In this example, each child of the Cephalopod pattern represents a chunk of music.

Each chunk has placer scenes as children, which add up to 8 whole-beats. Each placer scene has a number to represent the order it’ll appear. How many half-beats one these scenes represents is shown by its sprite.

We’ll look at this in more detail when we implement the pattern editor!

We’ll use both whole and half beats

Songs are made up of bars of music. Many songs have four beats per bar.

For example, take the following bar (a common time unit in music theory) of four whole-beats.

In the picture below, the notation 4/4 is the song’s time signature. It means that each bar in the song is split into four bears (the upper number) and each beat lasts one quarter note (a common time-unit).

In electronic music, this typically corresponds to when the kick sounds. That low hit sound you hear at regular time intervals and that marks the song’s rhythm.

We could have our rhythm game only detect whole-beats. Instead, we’ll detect half-beats because it allows us to create more varied rhythmic patterns.

The more divisions of beats we have to work with, the more varied and interesting patterns we can create!

It would get boring very quickly if the player only tapped on the whole-beat of the song! We could add rests wherever we want;

But it still would not be very engaging and only allows for 2^4 (16) possible patterns for each bar. By splitting the whole-beats into half-beats;

We can add half and whole-beat rests to break up rhythmic patterns. This addition gives us 2^8 (256) possible combinations.

We’ll talk more about beats and half-beats when creating the pattern editor, including supporting songs with a different number of beats per bar.

Closing words

This concludes the overview of the final project’s code structure.

Moving forward, we’ll implement the Synchronizer and a simplified HitSpawner to show how they interact and build on them later.

We’ll make sure we can reliably spawn HitBeat scenes in time with a track before working on building our patterns.

With the ability to create multiple patterns, we’ll add support for multiple tracks along with the track selector user interface.

Towards the end of the course, we’ll add the HitRoller before polishing the demo.

See you in the next lesson where we’ll start on the Synchronizer.