Sonolus Wiki

12. 输入管理器

在本章中,我们将介绍并实现输入管理器。

输入管理器

音符的输入逻辑中还缺少最后一件事:如果有两个音符彼此非常接近,当玩家点击时,我们只希望点击只记录在一个音符上,而不是同时记录在两个音符上。

这需要一个作为中介的实体来协调所有的音符:输入管理器。

让我们构建一个输入管理器。由于输入管理器只在游玩模式中出现,因此我们将其定义为一个可生成的原型并让其在初始化中生成。我们可以扩展 SpawnableArchetype({}) 来使其变为可生成原型并且不需要任何数据来生成:

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

我们能在初始化原型中生成输入管理器:

export class Initialization extends Archetype {
    // ...

    updateSequential() {
        archetypes.InputManager.spawn({})
        
        // ...
    }
}
export class Initialization extends Archetype {
    // ...

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

        // ...
    }
}

已经用过的触摸

为了阻塞输入,我们可以用一个集合存储已经使用过的触摸数据,让音符在使用前检查触摸数据是否已经使用过。

那如何共享可变数据呢?我们可以使用关卡内存(Level Memory)块来做到这一点:

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

现在我们可以实现并导出两个函数来与之交互:

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

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

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

最后,我们应该清除每一帧使用过的触摸,这样这个集合就不会无限增长:

export class InputManager extends Archetype {
    // ...

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

    touch() {
        usedTouchIds.clear()
    }
}

阻塞输入

有了输入管理器,我们现在可以将阻塞输入添加到音符的输入逻辑中。

但首先,我们需要确保音符的touch在输入管理器的之后执行,我们可以通过给出更高的顺序来实现:

export class Note extends Archetype {
    // ...

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

    touchOrder = 1
    // ...
}

现在阻塞输入简单地在使用前检查触摸是否已经被使用过,并在使用时将其标记为已使用:

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

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

            markAsUsed(touch)

            // ...
        }
    }

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

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

            markAsUsed(touch)

            // ...
        }
    }

    // ...
}