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

NoteRenderContext


想先跑起来?

这一页是签名与行为参考。渲染期的轨道布局坐标与扩展数据。直接做一遍:教程 · 添加新音符类型。

音符渲染上下文,每帧 render 传入。坐标是未叠加轨道变换的布局(世界)坐标,而画布缝本身已带轨道变换——两者配合即"默认跟随轨道几何"。

方法速览

方法用途
x() / y()音符中心坐标(世界坐标)
width()轨道绘制宽度(像素)
musicTimeMs()当前音乐时间(毫秒,用于动画)
lane()音符所属轨道
isHoldEnd()是否为长音符结尾(宿主当前恒传 false)
judgeLineY()当前轨道有效判定线 y
scrollSpeed()滚动速度(像素/秒)
endTime()长音符结束时间(非长音符 null)
extensionData()运行时扩展数据只读透传
skinImage()解析好的音符皮肤图(实验性)

x()

签名:

java
double x();

行为:音符中心的 x 坐标(世界坐标,即轨道中心线;未叠加轨道旋转/缩放/裁剪变换)。

示例:

java
double cx = ctx.x();

y()

签名:

java
double y();

行为:音符中心的 y 坐标,已包含音乐时间偏移(即当前帧的下落位置);同样未叠加轨道变换。

示例:

java
double cy = ctx.y();

width()

签名:

java
double width();

行为:轨道绘制宽度(像素)。用它决定音符横向尺寸可自动适配不同轨道宽度。

示例:

java
double w = Math.min(ctx.width(), 48);

musicTimeMs()

签名:

java
long musicTimeMs();

行为:当前音乐时间(毫秒),用于逐帧动画(脉冲、闪烁等)。倒带重演窗口内非单调,纯视觉动画不受影响。

示例:

java
double pulse = 0.5 + 0.5 * Math.sin(ctx.musicTimeMs() / 180.0);

lane()

签名:

java
int lane();

行为:音符所属轨道索引(0-based),可用于按轨道做差异化绘制。

示例:

java
if (ctx.lane() % 2 == 0) { /* 偶数轨样式 */ }

isHoldEnd()

签名:

java
boolean isHoldEnd();

行为:声明"本次绘制的是否为长音符结尾"。宿主当前在音符渲染分派中恒传 false(长音符尾部由类型自己按 endTime() + scrollSpeed() 绘制),字段保留以兼容未来/其它宿主实现。

示例:

java
if (ctx.isHoldEnd()) { /* 理论上不会进入 */ }

judgeLineY()

签名:

java
double judgeLineY();

行为:当前轨道有效判定线的 y 坐标(世界坐标,已计入轨道高度/透视等有效值)。可直接用 judgeLineY() - y() 得到音符到判定线的剩余距离。

示例:

java
double remainPx = Math.max(0.0, ctx.judgeLineY() - ctx.y());

scrollSpeed()

签名:

java
double scrollSpeed();

行为:当前滚动速度(像素/秒),与全局流速、轨道流速倍率一致,满足 位移 = 时间差(ms) * scrollSpeed / 1000。用于把时间长度换算成画面长度(如长音符尾部)。

示例:

java
double pxPerMs = ctx.scrollSpeed() / 1000.0;
double tailPx = (ctx.endTime() == null ? 0 : 300) * pxPerMs;   // 300ms 示例时长

endTime()

签名:

java
Long endTime();

行为:长音符结束时间(毫秒);非长音符为 null,绘制尾部前必须判空。

示例:

java
Long end = ctx.endTime();

extensionData()

签名:

java
default Object extensionData();

行为:NoteData.setExtensionData 写入值的只读透传;与判定回调经 NoteView.extensionData() 取到的是同一份数据。内置类型返回 null。默认返回 null。

示例:

java
AimState state = ctx.extensionData() instanceof AimState s ? s : null;

skinImage()

签名:

java
default RenderImage skinImage();

行为(源码标注为实验性):宿主为本类型解析好的皮肤图。解析链为「当前皮肤的本类型专属槽位(玩家皮肤优先)→ 定义的 defaultImage()」,两者都不可用返回 null,由模组自行绘制几何兜底。位置与方向由模组自定(宿主不会替你画)。默认返回 null。

示例:

java
RenderImage img = ctx.skinImage();
if (img != null) {
    double w = ctx.width();
    double h = w * img.height() / img.width();
    canvas.drawImage(img, ctx.x() - w / 2, ctx.y() - h / 2, w, h);
}

完整签名

java
public interface NoteRenderContext {

    double x();

    double y();

    double width();

    long musicTimeMs();

    int lane();

    boolean isHoldEnd();

    double judgeLineY();

    double scrollSpeed();

    Long endTime();

    default Object extensionData();

    default RenderImage skinImage();
}

线程:源码未声明额外线程约束;宿主在游玩渲染线程同步调用。

生命周期阶段:运行期每帧渲染回调内使用。

稳定性:2.0 起冻结:2.x 内只增不破(禁止删签名/改签名/改语义)。skinImage() 在源码中标注为实验性。