Theseus 模组文档
API 参考 / 打击特效样式(render 包)

HitEffectStyle


想先跑起来?

这一页是签名与行为参考。描述「这次打击长什么样」。直接做一遍:教程 · 自定义打击特效。

用途:描述一次打击特效的视觉表现。通常不自己实现,而是用工厂类 HitEffectStyles 构造;只有需要完全自定义(例如从配置计算帧列表)时才直接实现本接口。

方法速览

方法用途
kind()特效类型:TEXT 或 IMAGE_SEQUENCE
durationMs()特效持续时长(毫秒)
scale()缩放倍率(图片序列有效)
textForIntensity(float)按当前强度返回要显示的文字(仅 TEXT)
frames()显式帧列表(仅 IMAGE_SEQUENCE,且 sheetImage() 为 null 时)
frameDurationMs()每帧时长(autoFrameDuration() 为 false 时生效)
loop()是否在持续时长内循环播放
autoFrameDuration()帧时长是否由总时长自动均分(默认实现:frameDurationMs() <= 0)
sheetImage()精灵图(可选,非 null 时忽略 frames())
cellWidth() / cellHeight()精灵图单格尺寸(默认 0)
followLaneRotation()是否随所在轨道旋转(默认 false)

完整签名

java
public interface HitEffectStyle {

    enum Kind {
        TEXT,
        IMAGE_SEQUENCE
    }

    Kind kind();

    long durationMs();

    float scale();

    String textForIntensity(float intensity);

    List<RenderImage> frames();

    long frameDurationMs();

    boolean loop();

    default boolean autoFrameDuration();

    default RenderImage sheetImage();

    default int cellWidth();

    default int cellHeight();

    default boolean followLaneRotation();
}

kind()

签名:

java
Kind kind();

行为:返回样式类型。TEXT 走字符特效绘制路径,IMAGE_SEQUENCE 走逐帧贴图绘制路径;两条路径读取的方法不同(见各方法说明),返回类型决定了哪些方法会被调用。

durationMs()

签名:

java
long durationMs();

行为:特效持续时长(毫秒),从特效创建时刻起算,到期后实例被销毁。

同一打击点上可能同时存在多个模组样式(每个 provider 各返回一个),每个样式按自己的 durationMs 独立计算强度:intensity = clamp(1 - 已过毫秒 / durationMs, 0, 1)。整条特效的存活时长取所有样式 durationMs 的最大值。

scale()

签名:

java
float scale();

行为:缩放倍率,1.0 为原始大小。

与旧版文档的差异:早期文档称 scale 对 TEXT 影响"文字绘制位置的偏移倍率",但宿主渲染路径并未读取该值。以实际行为为准:TEXT 样式的 scale 无效果。

textForIntensity(float intensity)

签名:

java
String textForIntensity(float intensity);

行为:仅在 kind() == Kind.TEXT 时每帧调用。intensity 为 0~1 的强度(1 = 刚触发,0 = 即将消失),随 durationMs 线性衰减。

返回的文字绘制在轨道中心水平居中、判定线上方约 0.6 个字符高处;文字颜色由宿主固定(暖黄色调),不做按样式自定义。返回 null 或空串时不绘制任何文字(但特效实例仍然存在,直到 durationMs 到期)。

用 HitEffectStyles#textByIntensity 构造时,文字会随强度在数组内切换:texts[0] 对应强度 1,texts[length-1] 对应强度 0。

frames()

签名:

java
List<RenderImage> frames();

行为:显式帧列表,按顺序播放。仅在 kind() == Kind.IMAGE_SEQUENCE 且 sheetImage() 返回 null 时使用;sheetImage() 非 null 时本方法被忽略(若同时给了帧列表,帧数上限会取精灵图格数与列表长度中的较小值,以便精灵图加载失败时回退)。

列表元素须来自 ResourceManager#loadImage / loadImageAsync(模组不能自行构造 RenderImage)。

frameDurationMs()

签名:

java
long frameDurationMs();

行为:每帧显示时长(毫秒)。仅当 autoFrameDuration() 为 false 时生效;返回 0 或负值时进入自动均分模式。宿主实际使用的值不会小于 1ms。

loop()

签名:

java
boolean loop();

行为:true = 在 durationMs() 内循环播放;false = 单次播放,播完停在最后一帧,直到 durationMs() 到期。

autoFrameDuration()

签名:

java
default boolean autoFrameDuration();

行为:默认实现为 frameDurationMs() <= 0。为 true 时每帧时长 = durationMs() / 帧数(宿主保证至少 1ms),此时无需实现 frameDurationMs()。

sheetImage()

签名:

java
default RenderImage sheetImage();

行为:可选精灵图,默认返回 null。非 null 时按 cellWidth() × cellHeight() 的网格切成帧:列数 = 图宽 / cellWidth、行数 = 图高 / cellHeight(各自至少 1),播放顺序为从左到右、从上到下,不足一格的边角像素忽略。

cellWidth() / cellHeight()

签名:

java
default int cellWidth();

default int cellHeight();

行为:精灵图单格宽/高(像素),默认 0。仅在 sheetImage() 非 null 且两者都大于 0 时按精灵图模式拆帧;否则退回 frames() 列表。

followLaneRotation()

签名:

java
default boolean followLaneRotation();

行为:默认 false。为 true 时,特效以所在轨道的旋转圆心为轴施加与轨道相同的旋转角,从而与轨道上的音符保持同一朝向;为 false 时始终水平绘制。轨道未旋转(角度为 0)时本选项无视觉效果。

示例:直接实现最小自定义样式

java
public final class BlinkStyle implements HitEffectStyle {

    @Override
    public Kind kind() {
        return Kind.TEXT;
    }

    @Override
    public long durationMs() {
        return 400L;
    }

    @Override
    public float scale() {
        return 1.0f;
    }

    @Override
    public String textForIntensity(float intensity) {
        // 强度高时闪烁,低时只留一个点
        return intensity > 0.5f ? "" : "·";
    }

    @Override
    public java.util.List<RenderImage> frames() {
        return java.util.List.of();
    }

    @Override
    public long frameDurationMs() {
        return 0L;
    }

    @Override
    public boolean loop() {
        return false;
    }
}

一般场景直接用工厂更省事,见 HitEffectStyles。