Sonolus Wiki

03. Initialization

In this chapter, we will go over the concept of initialization pattern how it's used in engines.

Initialization Pattern

It is common for a level to have some global values that need to be calculated and made available to other entities to use.

An example could be the stage area may change depending on screen aspect ratio, and rather than calculate it every time we need it, we can calculate it once and store it in a global block such as Level Data.

The initialization pattern is made for achieving such a goal: an entity with the Initialization archetype will always be the first and only entity to spawn and does initialization work for however long it sees fit. Other entities will only spawn once the initialization entity has despawned.

The project template already comes with initialization pattern implemented in Initialization, let's take a look.

Spawning Logic

The spawning logic for our initialization entity is rather simple: we just need to ensure it's the first to spawn and spawn right away.

To make it the first to spawn, we simply need to make its spawnOrder callback to return a value that's lower than any other entities' values.

In this case, 0 is used.

export class Initialization extends Archetype {
    // ...

    spawnOrder() {
        return 0
    }

    // ...
}
export class Initialization extends Archetype {
    // ...

    spawnOrder() {
        return 0
    }

    // ...
}

Initialization Workload

Initialization workload can be put anywhere in the lifecycle of the entity, typically in preprocess and updateSequential callback.

For our engine in this guide, we don't have any initialization workload to perform in updateSequential.

preprocess will be used to perform workload before level starts, typically setting up transform, gameplay UI, scoring, life, etc. The project template comes with code that sets up menu UI so we can exit the dev level.

export class Initialization extends Archetype {
    preprocess() {
        ui.menu.set({
            anchor: screen.rect.lt.add(new Vec(0.05, -0.05)),
            pivot: { x: 0, y: 1 },
            size: new Vec(0.15, 0.15).mul(ui.configuration.menu.scale),
            rotation: 0,
            alpha: ui.configuration.menu.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: true,
        })
    }

    // ...
}
export class Initialization extends Archetype {
    preprocess() {
        ui.menu.set({
            anchor: screen.rect.lt.add(new Vec(0.05, -0.05)),
            pivot: { x: 0, y: 1 },
            size: new Vec(0.15, 0.15).mul(ui.configuration.menu.scale),
            rotation: 0,
            alpha: ui.configuration.menu.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: true,
        })
    }

    // ...
}

Despawning

Despawning the initialization entity is the same as despawning any other entity: by setting this.despawn to true, the entity will be scheduled to despawn at the end of current frame.

In this case it is done in updateSequential.

export class Initialization extends Archetype {
    // ...

    updateSequential() {
        this.despawn = true
    }
}
export class Initialization extends Archetype {
    // ...

    updateSequential() {
        this.despawn = true
    }
}