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 time | Behavior |
|---|---|
| 0 to 1 second | Intro |
| 1 to 3 seconds | Fall |
| 3 to 7 seconds | Frozen |
| Crossing 7 seconds | Hit |
| 7 to 8 seconds | Pause |
| First frame at or after 8 seconds | Restart |
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:
| Expression | Meaning |
|---|---|
phase_time.first(duration) | The first range, from 0 up to duration |
intro.next(duration) | A new range that starts where intro ends |
if intro | The phase clock is in intro, including its start but excluding its end |
intro.elapsed | Seconds since intro started |
intro.end | The boundary at the end of intro |
pause.is_done | The 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_doneThe 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:
| Helper | Use |
|---|---|
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.