15 Input Bucket
In this chapter, we will add an input bucket to Note.
Input Buckets
An input bucket groups timing results from Notes. The result screen shows a judgment graph for each bucket.
While buckets are not required, they are very useful for helping players calibrate their input offset and improve their accuracy.
Define the Note Bucket
Our engine only has one type of note, so one bucket will do.
Each bucket is represented on the result screen by a graphic composed of skin sprites. Use Skin.note so the graphic matches the note's in-game appearance. Use milliseconds as the unit.
Update guide/lib/buckets.py, keeping note_window below Buckets:
python
from sonolus.script.bucket import Bucket, JudgmentWindow, bucket, bucket_sprite, buckets
from sonolus.script.interval import Interval
from sonolus.script.text import StandardText
from guide.lib.skin import Skin
@buckets
class Buckets:
note: Bucket = bucket(
sprites=[
bucket_sprite(
sprite=Skin.note,
x=0,
y=0,
w=2,
h=2,
rotation=0,
),
],
unit=StandardText.MILLISECOND_UNIT,
)
note_window = JudgmentWindow(
perfect=Interval(-0.05, 0.05),
great=Interval(-0.1, 0.1),
good=Interval(-0.2, 0.2),
)The template already passes Buckets to the play mode, so defining the bucket is all the mode setup required.
Reuse the Judgment Windows
The note_window that we added earlier now serves two purposes: Note uses it to judge input, and the result screen uses the same timing boundaries for its judgment graph. Copy those boundaries into the bucket once. Do not put this setup in Note.preprocess, which runs once for every Note entity.
One-Time Setup
Add init_buckets below note_window in guide/lib/buckets.py. The bucket uses milliseconds, so multiply the judgment window by 1000. The @= operator copies every field from the value on its right into the existing value on its left:
python
def init_buckets():
Buckets.note.window @= note_window * 1000Now open guide/play/initialization.py and call init_buckets from Initialization.preprocess:
python
from guide.lib.buckets import init_buckets
class Initialization(PlayArchetype):
def preprocess(self):
# ...
init_buckets()
# ...The bucket setup runs once because the level contains one Initialization entity.
Assign the Bucket to Each Result
Lastly, update Note.touch in guide/play/note.py after setting self.result.accuracy. Assign the Note bucket and store the same timing difference in milliseconds:
python
from guide.lib.buckets import Buckets, note_window
class Note(PlayArchetype):
# ...
@callback(order=1)
def touch(self):
# ...
for touch in touches():
# ...
self.result.bucket @= Buckets.note
self.result.bucket_value = self.result.accuracy * 1000
# ...Checkpoint
After completing a play session, open the Note judgment graph on the result screen. Hit markers should be positioned by their early or late timing in milliseconds.