Skip to content

12 Input Manager

In this chapter, we will introduce and implement Input Manager.

Input Manager

There's is one last thing missing in Note's input logic: when two notes are very close to each other and player taps, we only want the tap to be registered on one note not both.

This requires a central entity to coordinate all notes: Input Manager.

Let's set up Input Manager. Because Input Manager is only needed in play mode, we will make it a spawnable archetype and spawn it in Initialization. We can extend SpawnableArchetype({}) to make it a spawnable archetype that does not need any data for spawning:

TypeScript
export class InputManager extends SpawnableArchetype({}) {}
JavaScript
export class InputManager extends SpawnableArchetype({}) {}
TypeScript
export const archetypes = defineArchetypes({
    // ...
    InputManager,
    // ...
})
JavaScript
export const archetypes = defineArchetypes({
    // ...
    InputManager,
    // ...
})

We can spawn Input Manager in Initialization:

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

    updateSequential() {
        archetypes.InputManager.spawn({})

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

    updateSequential() {
        archetypes.InputManager.spawn({})

        // ...
    }
}

Used Touches

For input blocking, we can have a collection storing touches that are already used, and let notes check if a touch is already used before using.

How do we share mutable data? We can do so using the Level Memory block:

TypeScript
const usedTouchIds = levelMemory(Collection(16, TouchId))
JavaScript
const usedTouchIds = levelMemory(Collection(16, TouchId))

Now we can implement and export two functions to interact with it:

TypeScript
export const isUsed = (touch: Touch) => usedTouchIds.has(touch.id)

export const markAsUsed = (touch: Touch) => usedTouchIds.add(touch.id)
JavaScript
export const isUsed = (touch) => usedTouchIds.has(touch.id)

export const markAsUsed = (touch) => usedTouchIds.add(touch.id)

Lastly, we should clear used touches every frame, so that the collection won't grow indefinitely:

TypeScript
export class InputManager extends Archetype {
    // ...

    touch() {
        usedTouchIds.clear()
    }
}
JavaScript
export class InputManager extends Archetype {
    // ...

    touch() {
        usedTouchIds.clear()
    }
}

Input Blocking

With Input Manager, we can now add input blocking to Note's input logic.

But first, we need to make sure Note's touch executes after Input Manager's, we can do so by giving a higher order:

TypeScript
export class Note extends Archetype {
    // ...

    touchOrder = 1
    // ...
}
JavaScript
export class Note extends Archetype {
    // ...

    touchOrder = 1
    // ...
}

Now input blocking is simply checking if a touch has already been used before using, and marking it as used when using:

TypeScript
export class Note extends Archetype {
    // ...
    touch() {
        // ...

        for (const touch of touches) {
            // ...
            if (isUsed(touch)) continue

            markAsUsed(touch)

            // ...
        }
    }

    // ...
}
JavaScript
export class Note extends Archetype {
    // ...
    touch() {
        // ...

        for (const touch of touches) {
            // ...
            if (isUsed(touch)) continue

            markAsUsed(touch)

            // ...
        }
    }

    // ...
}