Skip to content

18 SFX

In this chapter, we will add sound effects (SFX) to Note and Stage.

Define the Effect Clips

Effect clips are the sounds an engine can play. Open guide/lib/effect.py and replace its placeholder Effects class with these standard clips:

python
from sonolus.script.effect import StandardEffect, effects


@effects
class Effects:
    stage: StandardEffect.STAGE
    perfect: StandardEffect.PERFECT
    great: StandardEffect.GREAT
    good: StandardEffect.GOOD

Play Judgment Sounds

In Note.touch in guide/play/note.py, add this match after the existing result assignments. It plays the clip for the recorded judgment. The distance argument prevents the same clip from playing again for the specified number of seconds. A value of 0.02 avoids stacking the same sound during rapid hits. Keep the existing callback decorator unchanged.

python
from sonolus.script.bucket import Judgment

from guide.lib.effect import Effects


class Note(PlayArchetype):
    # ...

    def touch(self):
        # ...

        for touch in touches():
            # ...

            match self.result.judgment:
                case Judgment.PERFECT:
                    Effects.perfect.play(0.02)
                case Judgment.GREAT:
                    Effects.great.play(0.02)
                case Judgment.GOOD:
                    Effects.good.play(0.02)

            # ...

    # ...

Play Sounds for Unclaimed Taps

The judgment line already reacts to taps, so Stage should play a sound for taps that no Note used. Add this callback to Stage in guide/play/stage.py:

python
from sonolus.script.archetype import callback
from sonolus.script.runtime import touches

from guide.lib.effect import Effects
from guide.play.input_manager import touch_is_used


class Stage(PlayArchetype):
    # ...

    @callback(order=2)
    def touch(self):
        for touch in touches():
            if not touch.started:
                continue
            if touch_is_used(touch):
                continue

            Effects.stage.play(0.02)
            return

    # ...

Callback order makes the ownership check reliable: Input Manager clears used touches at order 0, Note claims its touches at order 1, and Stage inspects the remaining touches at order 2. The return limits Stage to one sound per frame.

Checkpoint

Run the level and tap a Note, then tap an empty part of the judgment line. The first tap should play only a judgment sound. The second should play the stage sound.