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) |
完整签名
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()
签名:
Kind kind();行为:返回样式类型。TEXT 走字符特效绘制路径,IMAGE_SEQUENCE 走逐帧贴图绘制路径;两条路径读取的方法不同(见各方法说明),返回类型决定了哪些方法会被调用。
durationMs()
签名:
long durationMs();行为:特效持续时长(毫秒),从特效创建时刻起算,到期后实例被销毁。
同一打击点上可能同时存在多个模组样式(每个 provider 各返回一个),每个样式按自己的 durationMs 独立计算强度:intensity = clamp(1 - 已过毫秒 / durationMs, 0, 1)。整条特效的存活时长取所有样式 durationMs 的最大值。
scale()
签名:
float scale();行为:缩放倍率,1.0 为原始大小。
- 对
IMAGE_SEQUENCE:绘制尺寸 = 源图尺寸(或精灵图单格尺寸)×scale,写入时须为正数; - 对
TEXT:当前宿主实现不使用该值——文字按固定基准字号绘制(等宽字形),scale不会放大文字,也不会改变其位置。
与旧版文档的差异:早期文档称
scale对TEXT影响"文字绘制位置的偏移倍率",但宿主渲染路径并未读取该值。以实际行为为准:TEXT样式的scale无效果。
textForIntensity(float intensity)
签名:
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()
签名:
List<RenderImage> frames();行为:显式帧列表,按顺序播放。仅在 kind() == Kind.IMAGE_SEQUENCE 且 sheetImage() 返回 null 时使用;sheetImage() 非 null 时本方法被忽略(若同时给了帧列表,帧数上限会取精灵图格数与列表长度中的较小值,以便精灵图加载失败时回退)。
列表元素须来自 ResourceManager#loadImage / loadImageAsync(模组不能自行构造 RenderImage)。
frameDurationMs()
签名:
long frameDurationMs();行为:每帧显示时长(毫秒)。仅当 autoFrameDuration() 为 false 时生效;返回 0 或负值时进入自动均分模式。宿主实际使用的值不会小于 1ms。
loop()
签名:
boolean loop();行为:true = 在 durationMs() 内循环播放;false = 单次播放,播完停在最后一帧,直到 durationMs() 到期。
autoFrameDuration()
签名:
default boolean autoFrameDuration();行为:默认实现为 frameDurationMs() <= 0。为 true 时每帧时长 = durationMs() / 帧数(宿主保证至少 1ms),此时无需实现 frameDurationMs()。
sheetImage()
签名:
default RenderImage sheetImage();行为:可选精灵图,默认返回 null。非 null 时按 cellWidth() × cellHeight() 的网格切成帧:列数 = 图宽 / cellWidth、行数 = 图高 / cellHeight(各自至少 1),播放顺序为从左到右、从上到下,不足一格的边角像素忽略。
cellWidth() / cellHeight()
签名:
default int cellWidth();
default int cellHeight();行为:精灵图单格宽/高(像素),默认 0。仅在 sheetImage() 非 null 且两者都大于 0 时按精灵图模式拆帧;否则退回 frames() 列表。
followLaneRotation()
签名:
default boolean followLaneRotation();行为:默认 false。为 true 时,特效以所在轨道的旋转圆心为轴施加与轨道相同的旋转角,从而与轨道上的音符保持同一朝向;为 false 时始终水平绘制。轨道未旋转(角度为 0)时本选项无视觉效果。
示例:直接实现最小自定义样式
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。