Skip to content

09 Note Spawning

In this chapter, we will implement the spawning logic for Note.

Target Time

We first need to calculate a note's target time. Sonolus.py provides beat_to_time to convert a beat directly to time using the timing information provided by Sonolus.

Calculated Entity Data

In chapter 07, the level supplied imported fields in Entity Data for each Note. An archetype can also calculate Entity Data during preprocess instead of loading it from the level. A field declared with entity_data can be written during preprocess and is read-only afterward. Other entities can also read it.

Target time does not change after preprocessing, so store it in Entity Data:

python
from sonolus.script.archetype import PlayArchetype, StandardImport, entity_data
from sonolus.script.timing import beat_to_time


class Note(PlayArchetype):
    beat: StandardImport.BEAT

    target_time: float = entity_data()

    def preprocess(self):
        self.target_time = beat_to_time(self.beat)

Spawn Time

Set each Note's spawn time to 1 second before its target time. This gives the player time to see it fall from above.

Spawn time also does not change for a note entity, so calculate it during preprocess and store it in Entity Data:

python
class Note(PlayArchetype):
    # ...

    spawn_time: float = entity_data()

    def preprocess(self):
        # ...

        self.spawn_time = self.target_time - 1

Spawning Logic

Replace the temporary spawn_order callback from chapter 07 so Notes are spawned in order of their spawn time, after the setup entities.

Initialization and Stage use the negative spawn orders -10 and -9. In this guide's chart, every Note spawns after those setup entities and can use its spawn time directly:

python
class Note(PlayArchetype):
    # ...

    def spawn_order(self) -> float:
        return self.spawn_time

Then add should_spawn, which allows each note to spawn when the current time reaches its spawn time:

python
from sonolus.script.runtime import time


class Note(PlayArchetype):
    # ...

    def should_spawn(self) -> bool:
        return time() >= self.spawn_time