Sonolus Wiki

06. Printing Times

In this chapter, we will implementing printing times.

Printing

Although preview already works and we can see all notes, it's very easy for players to get lost while scrolling around.

This is why we left some padding on each side of panel, so we can print helpful information such as time and measure. In later chapters, we will also use this space to print BPMs and time scales.

Let's make a utility function for easier printing:

export const print = (
    value: number,
    time: number,
    format: PrintFormat,
    decimalPlaces: number | 'auto',
    color: PrintColor,
    side: 'left' | 'right',
) =>
    canvas.print({
        value,
        format,
        decimalPlaces,
        anchor: getAnchor(panel.getPos(time).translate(side === 'left' ? -1.5 : 1.5, 0)),
        pivot: { x: side === 'left' ? 1 : 0, y: 0.5 },
        size: { x: screen.h / 10, y: screen.h / 20 },
        rotation: 0,
        color,
        alpha: 1,
        horizontalAlign: side === 'left' ? HorizontalAlign.Right : HorizontalAlign.Left,
        background: false,
    })

const getAnchor = (pos: Vec) => {
    const anchor = pos.transform(skin.transform)
    anchor.y = Math.clamp(anchor.y, screen.b + screen.h / 40, screen.t - screen.h / 40)

    return anchor
}
export const print = (value, time, format, decimalPlaces, color, side) =>
    canvas.print({
        value,
        format,
        decimalPlaces,
        anchor: getAnchor(panel.getPos(time).translate(side === 'left' ? -1.5 : 1.5, 0)),
        pivot: { x: side === 'left' ? 1 : 0, y: 0.5 },
        size: { x: screen.h / 10, y: screen.h / 20 },
        rotation: 0,
        color,
        alpha: 1,
        horizontalAlign: side === 'left' ? HorizontalAlign.Right : HorizontalAlign.Left,
        background: false,
    })

const getAnchor = (pos) => {
    const anchor = pos.transform(skin.transform)
    anchor.y = Math.clamp(anchor.y, screen.b + screen.h / 40, screen.t - screen.h / 40)

    return anchor
}

The utility function simply prints a value at a certain time with some options. Additionally it also makes sure the printed text is always visible when it's too close to top and bottom for the screen.

Times

Now we can loop over the duration, and print every second:

export class Stage extends Archetype {
    // ...

    render() {
        // ...

        this.printTimes()
    }

    // ...

    printTimes() {
        for (let i = 1; i <= Math.floor(chart.duration); i++) {
            print(i, i, PrintFormat.Time, 0, PrintColor.Neutral, 'left')
        }
    }
}
export class Stage extends Archetype {
    // ...

    render() {
        // ...

        this.printTimes()
    }

    // ...

    printTimes() {
        for (let i = 1; i <= Math.floor(chart.duration); i++) {
            print(i, i, PrintFormat.Time, 0, PrintColor.Neutral, 'left')
        }
    }
}

Note that we are starting at 1 rather than 0, this is because often start of the level will have BPM change and time scale change so we don't want to obstruct them.

Although it may vary by engine, usually we print time related information on the left side, while beat related information on the right side. Times are usually printed in the neutral color to not draw too much attention.