07 Instructions
In this chapter, we will add instructions to the frozen part of the tap phase.
Declaring
The template's TutorialMode already connects the shared skin, effects, and particles. It also connects the tutorial-specific instructions and instruction icons declared in guide/tutorial/instructions.py. Replace that file with the following declarations for the tap text and hand icon.
The StandardInstruction and StandardInstructionIcon classes provide these built-in resources:
python
from sonolus.script.instruction import (
StandardInstruction,
StandardInstructionIcon,
instruction_icons,
instructions,
)
@instructions
class Instructions:
tap: StandardInstruction.TAP
@instruction_icons
class InstructionIcons:
hand: StandardInstructionIcon.HANDText
In guide/tutorial/note.py, add the Instructions import shown below. Then replace draw_frozen so it shows the tap instruction while the frozen range is active:
python
from guide.tutorial.instructions import Instructions
def draw_frozen(elapsed: float):
Instructions.tap.show()
draw_note(1)Clear any previous instruction at the start of each frame. Replace guide/tutorial/update.py with the following code. The frozen range then shows the tap text, and no instruction remains after that range ends:
python
from sonolus.script.instruction import clear_instruction
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():
clear_instruction()
draw_stage()
if tap_phase(current_phase_time()):
reset_phase()
finish_frame()Icon
Next, replace draw_frozen again with the complete version that includes the hand animation. In guide/tutorial/note.py, add from math import pi at the top, add the runtime import shown below, and replace the existing Instructions import with the InstructionIcons, Instructions import. The existing interval import already provides remap_clamped.
The runtime_ui function provides the player's instruction scale and alpha settings:
python
from math import pi
from sonolus.script.runtime import runtime_ui, skin_transform
from guide.tutorial.instructions import InstructionIcons, Instructions
def draw_frozen(elapsed: float):
Instructions.tap.show()
draw_note(1)
cycle = elapsed % 1
angle = remap_clamped(0.25, 0.75, pi / 6, pi / 3, cycle)
alpha = remap_clamped(0.5, 0.25, 0, 1, abs(cycle - 0.5))
config = runtime_ui().instruction_config
judgment_position = skin_transform().transform_vec(Vec2(0, 1))
position = Vec2(0, -1).rotate(pi / 3) * (0.25 * config.scale) + judgment_position
InstructionIcons.hand.paint(
position=Vec2(0, 1).rotate(angle) * (0.25 * config.scale) + position,
size=0.25 * config.scale,
rotation=(180 * angle) / pi,
z=0,
a=alpha * config.alpha,
)elapsed % 1 creates a one-second animation cycle. During each cycle, angle sweeps the hand through the tap motion, and alpha fades it at both ends.
The hand position crosses two coordinate systems:
Vec2(0, 1)is the judgment-line position in the engine coordinate system from chapter 03.- Sprites receive the skin transform automatically, but
InstructionIcons.hand.paintexpects screen coordinates. skin_transform().transform_vec(Vec2(0, 1))converts the judgment-line position to screen coordinates.- The remaining vectors offset and animate the hand around that screen position using the player's instruction scale.
Vec2.rotate uses radians, while paint receives its rotation in degrees.
The tap text and hand animation now appear only while the note is frozen.