Theseus 模组文档
API 参考 / spi 包:音符与判定 SPI

最小自定义音符类型骨架


一个可编译的最小实现:自定义 NoteData 子类 + 定义对象(解析 / 判定等级钩子 / 渲染)。注册在 onPreInit(示例省略 Mod 外壳,完整模组骨架见 开发指南 3.3 节)。

java
import com.google.gson.JsonObject;
import com.seecmd.mod.api.render.RenderCanvas;
import com.seecmd.mod.api.spi.JudgeInput;
import com.seecmd.mod.api.spi.JudgeSink;
import com.seecmd.mod.api.spi.NoteData;
import com.seecmd.mod.api.spi.NoteParseContext;
import com.seecmd.mod.api.spi.NoteRenderContext;
import com.seecmd.mod.api.spi.NoteTypeDefinition;

import java.awt.Color;

public final class StardustNoteType implements NoteTypeDefinition {

    @Override
    public String displayName() {
        return "星尘";
    }

    @Override
    public String serializationKey() {
        return "stardust";
    }

    @Override
    public NoteData deserialize(JsonObject json, NoteParseContext ctx) {
        StardustNote note = new StardustNote();
        note.setTime(json.get("time").getAsLong());
        note.setLane(json.get("lane").getAsInt());
        if (json.has("endTime") && !json.get("endTime").isJsonNull()) {
            note.setEndTime(json.get("endTime").getAsLong());
        }
        note.setTypeKey(serializationKey());
        note.setExtensionData(Double.valueOf(ctx.contentScale()));
        return note;
    }

    @Override
    public void onJudge(JudgeInput input, JudgeSink sink) {
        // 不产出等级 = 使用宿主内置判定等级;命中与否仍由宿主时间窗口决定
    }

    @Override
    public void render(RenderCanvas canvas, NoteRenderContext ctx) {
        double size = Math.min(ctx.width(), 48);
        canvas.save();
        canvas.setColor(new Color(160, 120, 255));
        canvas.fillRect(ctx.x() - size / 2, ctx.y() - size / 2, size, size);
        canvas.restore();
    }

    /** api 侧最小运行时音符。 */
    static final class StardustNote extends NoteData {
        private long time;
        private int lane;
        private String typeKey;
        private Long endTime;
        private boolean judged;
        private Object extensionData;

        @Override public long time() { return time; }
        @Override public int lane() { return lane; }
        @Override public String typeKey() { return typeKey; }
        @Override public Long endTime() { return endTime; }
        @Override public boolean isJudged() { return judged; }
        @Override public Object extensionData() { return extensionData; }

        @Override public void setTime(long time) { this.time = time; }
        @Override public void setLane(int lane) { this.lane = lane; }
        @Override public void setEndTime(Long endTime) { this.endTime = endTime; }
        @Override public void setJudged(boolean judged) { this.judged = judged; }
        @Override public void setExtensionData(Object data) { this.extensionData = data; }

        void setTypeKey(String typeKey) { this.typeKey = typeKey; }
    }
}