Skip to content

06 Printing Times

In this chapter, we will print whole-second time labels in the left margin of each panel.

Printing

Although preview already works and displays every note, it is easy for players to get lost while scrolling around.

This is why we left space on each side of a panel. We can use it to print helpful information such as the time and measure number. In later chapters, we will also use this space to print BPM and time scale changes.

Unlike sprite drawing, print_number expects its anchor and dimensions in screen coordinates. The print_at_time function transforms the chart position with the skin transform. It also clamps the anchor away from the top and bottom edges so the text remains visible.

In guide/preview/chart.py, add a utility function using the print_number API:

python
from typing import Literal

from sonolus.script.interval import Interval
from sonolus.script.printing import PrintColor, PrintFormat, print_number
from sonolus.script.runtime import HorizontalAlign, screen, skin_transform
from sonolus.script.vec import Vec2


def print_at_time(
    value: float,
    time: float,
    *,
    fmt: PrintFormat,
    decimal_places: int = -1,
    color: PrintColor,
    side: Literal["left", "right"],
):
    print_number(
        value=value,
        fmt=fmt,
        decimal_places=decimal_places,
        anchor=_get_anchor(
            pos_at_time(time) + Vec2(-1.5 if side == "left" else 1.5, 0)
        ),
        pivot=Vec2(1 if side == "left" else 0, 0.5),
        dimensions=Vec2(screen().h / 10, screen().h / 20),
        color=color,
        horizontal_align=HorizontalAlign.RIGHT
        if side == "left"
        else HorizontalAlign.LEFT,
        background=False,
    )


def _get_anchor(position: Vec2):
    anchor = skin_transform().transform_vec(position)
    anchor.y = Interval(screen().b, screen().t).shrink(screen().h / 40).clamp(anchor.y)
    return anchor

Times

In guide/preview/stage.py, add print_times to PreviewStage and call it from render after render_panels. The loop starts at 1 so a time label does not obstruct the BPM or time scale markers at the beginning of the level:

python
from math import floor

from sonolus.script.printing import PrintColor, PrintFormat

from guide.preview.chart import Chart, print_at_time


class PreviewStage(PreviewArchetype):
    # ...

    def render(self):
        # ...

        self.print_times()

    def print_times(self):
        for time in range(1, floor(Chart.duration) + 1):
            print_at_time(
                time,
                time,
                fmt=PrintFormat.TIME,
                decimal_places=0,
                color=PrintColor.NEUTRAL,
                side="left",
            )

This guide prints time information on the left, beat information on the right, and regular labels in a neutral color.

Checkpoint

Refresh Preview. Neutral time labels should appear in the left margin at 1 second, 2 seconds, and each later whole second through the end of the sample level. There should be no time label at 0.