Sonolus Wiki

14. 输入判定

本章我们将为音符添加输入判定。

输入原型

您可能已经注意到,尽管我们的音符功能齐全,但 Sonolus 并没有按照正常的处理音符的方式处理它们:您可以立即跳过整个关卡,并且在结果界面上,显示所有统计数据为 0 个音符。

为了让一个实体被 Sonolus 视为可播放的音符,它的原型必须有输入。

export class Note extends Archetype {
    hasInput = true

    // ...
}
export class Note extends Archetype {
    hasInput = true

    // ...
}

有了这个设置后:

  • Sonolus 将知道并计算所有具有输入原型的实体。
  • 只有当所有输入实体都消失时,玩家才会允许跳过剩余的关卡。
  • 输入实体可以访问实体输入块(Entity Input block),该代码可用于告诉 Sonolus 玩家的表现。
  • Sonolus 将根据输入结果自动计算得分、连击数、Perfect 计数等统计数据。
  • 当出现新的输入结果时,相关的 UI 也会更新和播放动画效果。
  • 指定桶(bucket)的输入结果,能够在结果界面上得到判定图表。

输入结果

要告诉 Sonolus 玩家在某个音符上的表现如何,我们只需修改this.result即可。

对于this.result.judgment ,我们可以手动分配Judgment.MissJudgment.PerfectJudgment.GreatJudgment.Good 。然而,更好的方法是直接使用input.judge辅助函数。

对于this.result.accuracy ,我们应该以秒为单位提供时间差。

export class Note extends Archetype {
    // ...

    initialize() {
        // ...

        this.result.accuracy = windows.good.max
    }

    // ...
    touch() {
        // ...

        for (const touch of touches) {
            // ...

            this.result.judgment = input.judge(touch.startTime, this.targetTime, windows)
            this.result.accuracy = touch.startTime - this.targetTime

            // ...
        }
    }

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

    initialize() {
        // ...

        this.result.accuracy = windows.good.max
    }

    // ...
    touch() {
        // ...

        for (const touch of touches) {
            // ...

            this.result.judgment = input.judge(touch.startTime, this.targetTime, windows)
            this.result.accuracy = touch.startTime - this.targetTime

            // ...
        }
    }

    // ...
}

判定和连击 UI

在玩家玩游戏时提供判定和连击 UI,为玩家提供即时反馈也很重要。

我们在初始化原型中设置它们:

export class Initialization extends Archetype {
    preprocess() {
        // ...

        ui.judgment.set({
            anchor: { x: 0, y: -0.4 },
            pivot: { x: 0.5, y: 0 },
            size: new Vec(0, 0.15).mul(ui.configuration.judgment.scale),
            rotation: 0,
            alpha: ui.configuration.judgment.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })

        ui.combo.value.set({
            anchor: { x: screen.r * 0.7, y: 0 },
            pivot: { x: 0.5, y: 0 },
            size: new Vec(0, 0.2).mul(ui.configuration.combo.scale),
            rotation: 0,
            alpha: ui.configuration.combo.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })
        ui.combo.text.set({
            anchor: { x: screen.r * 0.7, y: 0 },
            pivot: { x: 0.5, y: 1 },
            size: new Vec(0, 0.12).mul(ui.configuration.combo.scale),
            rotation: 0,
            alpha: ui.configuration.combo.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })
    }

    // ...
}
export class Initialization extends Archetype {
    preprocess() {
        // ...

        ui.judgment.set({
            anchor: { x: 0, y: -0.4 },
            pivot: { x: 0.5, y: 0 },
            size: new Vec(0, 0.15).mul(ui.configuration.judgment.scale),
            rotation: 0,
            alpha: ui.configuration.judgment.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })

        ui.combo.value.set({
            anchor: { x: screen.r * 0.7, y: 0 },
            pivot: { x: 0.5, y: 0 },
            size: new Vec(0, 0.2).mul(ui.configuration.combo.scale),
            rotation: 0,
            alpha: ui.configuration.combo.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })
        ui.combo.text.set({
            anchor: { x: screen.r * 0.7, y: 0 },
            pivot: { x: 0.5, y: 1 },
            size: new Vec(0, 0.12).mul(ui.configuration.combo.scale),
            rotation: 0,
            alpha: ui.configuration.combo.alpha,
            horizontalAlign: HorizontalAlign.Center,
            background: false,
        })
    }

    // ...
}

最后,让我们也给我们的判定和连击 UI 一些动画效果,让它们更生动:

export const ui: EngineConfigurationUI = {
    // ...
    judgmentAnimation: {
        // ...
        alpha: {
            from: 1,
            to: 0,
            duration: 0.2,
            ease: 'outCubic',
        },
    },
    comboAnimation: {
        scale: {
            from: 1.2,
            to: 1,
            duration: 0.2,
            ease: 'inCubic',
        },
        // ...
    },
    // ...
}
export const ui = {
    // ...
    judgmentAnimation: {
        // ...
        alpha: {
            from: 1,
            to: 0,
            duration: 0.2,
            ease: 'outCubic',
        },
    },
    comboAnimation: {
        scale: {
            from: 1.2,
            to: 1,
            duration: 0.2,
            ease: 'inCubic',
        },
        // ...
    },
    // ...
}