客户端动画 API
按客户端 tick 推进的序列帧动画。全部 @SideOnly(Side.CLIENT),由 ClientTickHandler 统一驱动。
基本信息
| 属性 | 值 |
|---|---|
| 基类 | wanion.lib.client.animation.Animation(abstract) |
| 具体子类 | SimpleAnimation、SimpleHalfAnimation(final)、ComplexAnimation、ComplexHalfAnimation(final) |
| 驱动者 | wanion.lib.client.ClientTickHandler(final) |
| 帧资源 | net.minecraft.util.ResourceLocation[] |
| 挂载事件 | TickEvent.ClientTickEvent(注册在 FML 总线 FMLCommonHandler.instance().bus()) |
| 步进粒度 | 简单类 1 tick / 帧,半速类 2 tick / 帧 |
四个变体对比
| 属性 | SimpleAnimation |
SimpleHalfAnimation |
ComplexAnimation |
ComplexHalfAnimation |
|---|---|---|---|---|
| 修饰 | class(可继承) |
final class |
class(可继承) |
final class |
| 父类 | Animation |
SimpleAnimation |
Animation |
ComplexAnimation |
| 构造签名 | (ResourceLocation[] frames) |
(ResourceLocation[] frames) |
(ResourceLocation[] frames, int[] animationStages) |
(ResourceLocation[] frames, int[] stages) |
| 步进逻辑 | if (++currentFrame > lastFrameIndex) currentFrame = 0; |
if (half ^= true) super.updateAnimation(); |
if (++currentStage > lastStageIndex) currentStage = 0; 然后 currentFrame = animationStages[currentStage]; |
if (half ^= true) super.updateAnimation(); |
| 每 tick 走几帧 | 1 | 0 或 1(隔帧) | 1 | 0 或 1(隔帧) |
| 能否跳帧 | 否,只能顺序 | 同左 | 能,animationStages 显式指定每步的目标帧 |
能 |
| 内部状态 | currentFrame |
currentFrame + private boolean half |
currentFrame + currentStage + lastStageIndex |
同左 + private boolean half |
| 循环 | 回到帧 0 | 同左 | 回到 stage 0 并取 animationStages[0] |
同左 |
half ^= true的语义:boolean上^=每次取反,条件为真时才调super。第一次updateAnimation()就会推进(false ^= true→true),之后隔一次才推进 —— 实际 2 tick 一帧。四个类都不在构造时推进,所以注册后的第一个 tick 就会动。
Animation 基类成员
| 成员 | 签名 | 说明 |
|---|---|---|
| 帧数组 | private final ResourceLocation[] frames |
|
| 末帧下标 | protected final int lastFrameIndex |
构造时 = frames.length |
| 当前帧 | protected int currentFrame |
初值 0 |
| 构造 | public Animation(@Nonnull ResourceLocation[] frames) |
lastFrameIndex - 1 == -1(即空数组)时 throw new RuntimeException("What is an Animation with no frames?") |
| 步进 | public abstract void updateAnimation() |
抽象 |
| 取当前帧 | public final ResourceLocation getCurrentFrame() |
frames[currentFrame] |
ClientTickHandler
@SideOnly(Side.CLIENT)
public final class ClientTickHandler implements WanionLib.IDependency {
private final List<Animation> registeredAnimations = new ArrayList<>();
private ClientTickHandler() { FMLCommonHandler.instance().bus().register(this); }
public void registerAnimation(@Nonnull Animation animation) { registeredAnimations.add(animation); }
@SubscribeEvent
public void tickEvent(final TickEvent.ClientTickEvent event) { registeredAnimations.forEach(Animation::updateAnimation); }
}
| 成员 | 说明 |
|---|---|
| 构造私有 | 单例经 依赖容器 的反射无参构造产出 |
| 总线 | 注册在 FML 总线(FMLCommonHandler.instance().bus()),非 MinecraftForge.EVENT_BUS |
registerAnimation(Animation) |
无去重,重复注册同一实例会被推进多次 |
tickEvent(TickEvent.ClientTickEvent) |
每个客户端 tick 遍历 registeredAnimations 调 updateAnimation();不区分 phase,故一 tick 内 START/END 两阶段各调一次 → 实际 2 tick 一帧 |
⚠️ 步进速率由这两层叠加决定:
TickEvent.ClientTickEvent在一个 tick 内触发两次(START 与 END),tickEvent不做phase过滤,所以「1 tick 一帧」实际上是半速。想要真实 1 tick 一帧需消费方自行按event.phase去重。
⚠️
registeredAnimations是ArrayList,在客户端 tick 回调里遍历时若回调内调registerAnimation会抛ConcurrentModificationException。
取得方式
WanionLib.getClientTickHandler()(@SideOnly(Side.CLIENT))内部走 instance.dependencies.get(ClientTickHandler.class),即由反射无参构造惰性产出并注册到 FML 总线。
⚠️ 见 主类入口与依赖注入 的注意点:
WanionLib.loadComplete把dependencies置null,FMLLoadCompleteEvent之后不可再调getClientTickHandler()。