Skip to content

05 Note Display

In this chapter, we will implement the note drawing functions.

Existing Sprite

The shared Skin already declares the note sprite from play mode. This code is for reference. Do not edit guide/lib/skin.py:

python
@skin
class Skin:
    # ...
    note: StandardSprite.NOTE_HEAD_CYAN

Note Drawing Function

Create guide/tutorial/note.py with a draw_note function. It draws the note at a given height, scale, and alpha using Rect and Vec2:

python
from sonolus.script.quad import Rect
from sonolus.script.vec import Vec2

from guide.lib.layout import Config
from guide.lib.skin import Skin


def draw_note(y: float, scale: float = 1, alpha: float = 1):
    size = 2 * Config.note_radius * scale
    layout = Rect.from_center(Vec2(0, y), Vec2(size, -size))
    Skin.note.draw(layout, z=1, a=alpha)

The note uses layer 1, so it appears in front of the judgment line in layer 0. The negative height in Vec2(size, -size) accounts for the transformed vertical axis.

Timing

In guide/tutorial/note.py, add these durations after the imports:

python
INTRO_DURATION = 1
FALL_DURATION = 2
FROZEN_DURATION = 4
PAUSE_DURATION = 1

The next chapter will chain these durations into a timeline. Each drawing function receives the time elapsed within its part of the phase. PAUSE_DURATION creates a blank delay before the phase repeats.

Drawing

In guide/tutorial/note.py, add the imports from sonolus.script.interval, then add the three drawing functions after draw_note.

The intro draws an enlarged note near the center of the screen and fades it during the final quarter. The remap_clamped function keeps the fade within its specified range:

python
from sonolus.script.interval import remap, remap_clamped


def draw_intro(elapsed: float):
    alpha = remap_clamped(0.75 * INTRO_DURATION, INTRO_DURATION, 1, 0, elapsed)
    draw_note(0.5, scale=2, alpha=alpha)

The fall uses remap to map its elapsed time to the note's vertical position:

python
def draw_fall(elapsed: float):
    y = remap(0, FALL_DURATION, 0, 1, elapsed)
    draw_note(y)

The frozen part draws the note at the judgment line. We will add its instructions in a later chapter:

python
def draw_frozen(elapsed: float):
    draw_note(1)

Finally, add this placeholder after the drawing functions. Later chapters will replace its body with the hit effects:

python
def play_note_hit_effects():
    pass

The note is not visible yet because these drawing functions are not scheduled until the next chapter.