Skip to content

06 Note Phase

In this chapter, we will implement the tap phase and connect it to the tutorial update callback.

Timeline

The tap phase follows this timeline:

Phase elapsed timeBehavior
0 to 1 secondIntro
1 to 3 secondsFall
3 to 7 secondsFrozen
Crossing 7 secondsHit
7 to 8 secondsPause
First frame at or after 8 secondsRestart

The template already provides the phase clock in guide/tutorial/navigate.py. You do not need to edit this file.

PhaseTime holds the phase clock at the current and previous frames. Its methods and range properties mean:

ExpressionMeaning
phase_time.first(duration)The first range, from 0 up to duration
intro.next(duration)A new range that starts where intro ends
if introThe phase clock is in intro, including its start but excluding its end
intro.elapsedSeconds since intro started
intro.endThe boundary at the end of intro
pause.is_doneThe phase clock has reached or passed the end of pause
phase_time.crossed(timing)The phase clock crossed the boundary since the previous frame

Calling first or next describes a range. It does not advance the clock. The crossed check compares two frames, so a one-time event still runs if one long frame skips past its exact boundary.

Tap Phase

In guide/tutorial/note.py, add the PhaseTime import shown below with the other guide imports. Then add tap_phase after play_note_hit_effects:

python
from guide.tutorial.navigate import PhaseTime


def tap_phase(phase_time: PhaseTime) -> bool:
    intro = phase_time.first(INTRO_DURATION)
    fall = intro.next(FALL_DURATION)
    frozen = fall.next(FROZEN_DURATION)
    hit_time = frozen.end
    pause = frozen.next(PAUSE_DURATION)

    if intro:
        draw_intro(intro.elapsed)
    if fall:
        draw_fall(fall.elapsed)
    if frozen:
        draw_frozen(frozen.elapsed)
    if phase_time.crossed(hit_time):
        play_note_hit_effects()

    return pause.is_done

The drawing ranges do not overlap, so at most one drawing check succeeds. The hit is a boundary rather than a range: it runs once when the clock crosses 7 seconds. The pause draws nothing, and pause.is_done becomes true when the full eight-second phase has finished.

Updating

The remaining project helpers control the clock around each update:

HelperUse
current_phase_time()Read the phase clock for the current and previous frames
reset_phase()Start the phase clock over at zero
finish_frame()Record this frame's time for the next crossed() check

Keep finish_frame() at the end of update.

Replace guide/tutorial/update.py with the following code. It draws the stage, runs the tap phase, and restarts the phase when tap_phase returns True:

python
from guide.tutorial.navigate import current_phase_time, finish_frame, reset_phase
from guide.tutorial.note import tap_phase
from guide.tutorial.stage import draw_stage


def update():
    draw_stage()
    if tap_phase(current_phase_time()):
        reset_phase()
    finish_frame()

The tutorial now loops through the intro, fall, frozen note, and one-second blank pause. The hit boundary has no visible or audible effect yet.