当前位置: 首页 > news >正文

我的世界1.20.1forge模组开发进阶教程——Geckolib动画实体(3)

注意:本章涉及大量的geckolib底层代码,补充讲解了上一节没讲的,如果看不懂请去学习Java

GeoEntity

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package software.bernie.geckolib.animatable;

import javax.annotation.Nullable;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.network.PacketDistributor;
import software.bernie.geckolib.core.animatable.GeoAnimatable;
import software.bernie.geckolib.network.GeckoLibNetwork;
import software.bernie.geckolib.network.SerializableDataTicket;
import software.bernie.geckolib.network.packet.EntityAnimDataSyncPacket;
import software.bernie.geckolib.network.packet.EntityAnimTriggerPacket;

public interface GeoEntity extends GeoAnimatable {
   
    @Nullable
    default <D> D getAnimData(SerializableDataTicket<D> dataTicket) {
   
        return (D)this.getAnimatableInstanceCache().getManagerForId((long)((Entity)this).getId()).getData(dataTicket);
    }

    default <D> void setAnimData(SerializableDataTicket<D> dataTicket, D data) {
   
        Entity entity = (Entity)this;
        if (entity.level().isClientSide()) {
   
            this.getAnimatableInstanceCache().getManagerForId((long)entity.getId()).setData(dataTicket, data);
        } else {
   
            GeckoLibNetwork.send(new EntityAnimDataSyncPacket(entity.getId(), dataTicket, data), PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> entity));
        }

    }

    default void triggerAnim(@Nullable String controllerName, String animName) {
   
        Entity entity = (Entity)this;
        if (entity.level().isClientSide()) {
   
            this.getAnimatableInstanceCache().getManagerForId((long)entity.getId()).tryTriggerAnimation(controllerName, animName);
        } else {
   
            GeckoLibNetwork.send(new EntityAnimTriggerPacket(entity.getId(), controllerName, animName), PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> entity));
        }

    }

    default double getTick(Object entity) {
   
        return (double)((Entity)entity).tickCount;
    }
}

这段代码是 GeckoLib 库中用于处理实体动画的核心接口 GeoEntity,它为 Minecraft 实体(如生物、玩家等)提供了动画控制、数据同步和网络通信的能力。以下是对代码的逐层解析及其在 GeckoLib 中的作用分析:


代码结构概览

  1. 接口定义
    GeoEntity 继承自 GeoAnimatable,表明这是一个面向实体(Entity)的动画接口。
  2. 核心方法
    包含 getAnimDatasetAnimDatatriggerAnimgetTick 四个方法,分别用于动画数据操作和生命周期管理。

核心方法解析

1. getAnimData(SerializableDataTicket<D> dataTicket)

作用
通过 SerializableDataTicket 从实体中获取特定类型的动画数据。
实现逻辑
• 通过 getAnimatableInstanceCache() 获取实体动画缓存。
• 使用 (long) ((Entity) this).getId() 获取实体唯一ID,定位动画管理器。
• 调用管理器中的 getData(dataTicket) 获取数据。
用途
用于客户端或服务端读取实体的动态动画数据(如自定义动画参数)。

2. setAnimData(SerializableDataTicket<D> dataTicket, D data)

作用
设置实体的动画数据,并自动处理网络同步。
实现逻辑
客户端:直接更新本地动画管理器的数据。
服务端:通过 GeckoLibNetwork 发送 EntityAnimDataSyncPacket 数据包,确保所有客户端同步更新。
用途
跨端(客户端-服务端)同步动画数据,例如同步实体的血量变化触发的动画参数。

3. triggerAnim(@Nullable String controllerName, String animName)

作用
触发实体播放特定名称的动画。
实现逻辑
客户端:直接调用本地动画管理器的 tryTriggerAnimation
服务端:发送 EntityAnimTriggerPacket 数据包通知客户端播放动画。
用途
在多人游戏中,服务端控制的实体(如 Boss 攻击动作)触发动画时,客户端能正确响应。

4. getTick(Object entity)

作用
返回实体的当前游戏刻(tickCount),用于动画时间计算。
用途
为动画系统提供时间基准,确保动画更新与游戏逻辑同步。


在 GeckoLib 中的核心作用

  1. 动画数据管理
    • 通过 SerializableDataTicket 实现类型安全的动画数据存取,支持自定义数据类型(如浮点数、布尔值等)。
    • 自动处理客户端与服务端之间的数据同步,避免手动编写网络代码。

  2. 网络通信
    • 使用 GeckoLibNetwork 发送标准化的动画数据包(EntityAnimDataSyncPacketEntityAnimTriggerPacket)。
    PacketDistributor.TRACKING_ENTITY_AND_SELF 确保动画数据仅同步给关注该实体的客户端。

  3. 实体动画生命周期
    • 依赖 getTick 提供的时间基准,驱动动画逐帧更新。
    • 通过 GeoAnimatable 接口与 GeckoLib 动画核心(如动画控制器、状态机)交互。


设计模式与协作

客户端-服务端解耦
通过条件判断 entity.level().isClientSide() 分离本地操作与网络通信,确保单机和多人模式的一致性。
数据驱动动画
SerializableDataTicket 允许开发者定义自定义动画参数(如 ATTACKINGMOVING),实现数据驱动的动画逻辑。
AnimatableInstanceCache 协作
动画管理器通过实体 ID 唯一标识,支持同一实体类型多实例的独立动画控制。


典型使用场景

  1. 同步 Boss 攻击动画
    服务端触发 triggerAnim("controller", "attack"),所有客户端播放攻击动画。
  2. 动态血量条动画
    实体血量变化时,通过 setAnimData 同步血量百分比到客户端,驱动材质动画。
  3. 客户端粒子效果触发
    客户端通过 getAnimData 读取 SHOULD_PLAY_PARTICLES 参数,决定是否生成粒子。

总结

这段代码是 GeckoLib 实体动画系统的核心枢纽,实现了动画数据的存储、同步、触发和时间管理。通过标准化接口和网络通信机制,开发者可以专注于动画逻辑本身,而无需处理底层细节,极大简化了复杂实体动画的实现流程。

GeoAnimatable接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package software.bernie.geckolib.core.animatable;

import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animation.AnimatableManager;

public interface GeoAnimatable {
   
    void registerControllers(AnimatableManager.ControllerRegistrar var1);

    AnimatableInstanceCache getAnimatableInstanceCache();

    default double getBoneResetTime() {
   
        return (double)1.0F;
    }

    default boolean shouldPlayAnimsWhileGamePaused() {
   
        return false;
    }

    double getTick(Object var1);

    default AnimatableInstanceCache animatableCacheOverride() {
   
        return null;
    }
}


代码解释与协作分析

GeoAnimatable 接口是 GeckoLib 动画系统的核心,定义了所有可动画对象(如实体、方块实体)的基础行为。以下是其方法解析及与其他类的协作关系:


1. registerControllers(AnimatableManager.ControllerRegistrar)

作用
注册动画控制器(AnimationController),管理动画逻辑(如状态切换、条件触发)。
协作类
AnimatableManager:通过 ControllerRegistrar 添加控制器,最终由 AnimatableManager 统一管理所有控制器的更新。
AnimationController:用户自定义控制器,处理动画状态机、过渡逻辑。
示例

// 实体类实现
public class MyEntity implements GeoAnimatable {
   
    @Override
    public void registerControllers(ControllerRegistrar registrar) {
   
        registrar.add(new AnimationController<>(this, "walk_controller", state -> state.setAndContinue(WALK_ANIM)));
    }
}

2. getAnimatableInstanceCache()

作用
提供动画实例缓存,管理动画数据(如实体ID到动画状态的映射)。
协作类
AnimatableInstanceCache:默认实现为 SingletonAnimatableInstanceCacheInstancedAnimatableInstanceCache,用于高效复用动画实例。
GeoEntity/GeoBlockEntity:具体类通过此方法返回缓存,例如实体使用实体ID作为缓存键。
示例

// GeoEntity 实现
public class MyEntity implements GeoEntity {
   
    private final AnimatableInstanceCache cache = new SingletonAnimatableInstanceCache(this);

    @Override
    public AnimatableInstanceCache getAnimatableInstanceCache() {
   
        return cache;
    }
}

3. default double getBoneResetTime()

作用
定义骨骼复位时间(动画停止后骨骼恢复原位的时间)。
协作类
GeoBone:在动画停止时,骨骼根据此时间插值回初始位置。
AnimationProcessor:处理骨骼变换时参考此值。
覆盖场景
若需快速复位(如机械部件),可返回更小值(如 0.5)。


4. default boolean shouldPlayAnimsWhileGamePaused()

作用
控制游戏暂停时是否继续播放动画(如过场动画需持续播放)。
协作类
AnimatableManager:在游戏每帧更新时检查此标志,决定是否更新动画。
示例

// 过场动画实体
@Override
public boolean shouldPlayAnimsWhileGamePaused() {
   
    return true; // 游戏暂停时继续播放
}

5. double getTick(Object)

作用
获取当前时间刻(驱动动画进度的核心参数)。
协作类
GeoModel:渲染时调用此方法获取时间,计算动画帧。
AnimationController:根据 tick 值推进动画进度,处理关键帧事件。
实现示例

// 实体实现
@Override
public double getTick(Object entity) {
   
    return ((Entity) entity).tickCount;
}

6. default AnimatableInstanceCache animatableCacheOverride()

作用
允许覆盖默认缓存实现(高级用途)。
协作类
InstancedAnimatableInstanceCache:若需为每个实例独立缓存(如动态生成的实体),可返回自定义缓存。
示例

// 自定义缓存策略
@Override
public AnimatableInstanceCache animatableCacheOverride() {
   
    return new MyCustomCache(this);
}

协作流程图

渲染循环 (Game Render Loop)
    ├── 调用 GeoModel.render() 
    │    ├── 从 GeoAnimatable.getTick() 获取时间
    │    └── 根据时间计算动画进度
    │
    ├── AnimatableManager.updateAnimations()
    │    ├── 检查 shouldPlayAnimsWhileGamePaused()
    │    ├── 遍历所有 AnimationController
    │    └── 调用控制器逻辑(触发动画、状态切换)
    │
    └── 网络同步 (GeckoLibNetwork)
         ├── 服务端发送 EntityAnimTriggerPacket
         └── 客户端通过 AnimatableInstanceCache 更新动画状态

总结

GeoAnimatable 是 GeckoLib 动画系统的核心接口,通过定义动画控制器注册、缓存管理、时间获取等行为,与以下关键类协作:
AnimatableManager:管理动画控制器生命周期。
AnimatableInstanceCache:优化动画实例复用。
AnimationController:实现具体动画逻辑。
GeoModel:驱动渲染流程。
网络包:同步客户端与服务端动画状态。

开发者通过实现此接口,可将自定义实体、方块实体等无缝接入 GeckoLib 的动画框架,无需关注底层细节,专注于动画逻辑设计。


AnimatableManager类

AnimatableManager 类在 GeckoLib 中的作用解析

AnimatableManager 是 GeckoLib 动画系统的核心管理类,负责协调 动画控制器(AnimationController)骨骼状态(Bone Snapshot)动画数据(Extra Data),确保动画逻辑的更新、同步与渲染。以下是其核心功能及在库中的协作关系:


1. 动画控制器的生命周期管理

作用
通过 ControllerRegistrar 收集并管理所有注册的动画控制器。
协作流程
注册阶段:当实体(如 GeoEntity)实现 GeoAnimatable 接口时,需在 registerControllers 方法中调用 registrar.add(...) 添加控制器(例如移动、攻击控制器)。
初始化阶段AnimatableManager 构造函数调用 animatable.registerControllers(registrar),将控制器存入 animationControllers Map。
运行时操作:通过 addController/removeController 动态增删控制器,支持热更新或条件化动画逻辑。
示例代码

// 实体类注册控制器
public class MyEntity implements GeoAnimatable {
   
    @Override
    public void registerControllers(ControllerRegistrar registrar) {
   
        registrar.add(new AnimationController<>(this, "walk", this::handleWalk));
        registrar.add(new AnimationController<>(this, "attack", this::handleAttack));
    }
}

2. 骨骼快照(Bone Snapshot)管理

作用
保存骨骼的当前变换状态(位置、旋转、缩放),用于动画混合(Blending)或插值(Interpolation)。
协作流程
渲染前GeoModel 调用 getBoneSnapshotCollection() 获取上一帧骨骼状态。
动画更新:动画控制器修改骨骼状态后,新状态会被缓存至 boneSnapshotCollection
平滑过渡:通过对比新旧快照,计算插值以实现平滑动画过渡。
关键字段

private final Map<String, BoneSnapshot> boneSnapshotCollection = new Object2ObjectOpenHashMap();

3. 动画数据(Extra Data)传递

作用
提供类型安全的键值对存储(DataTicket),用于跨控制器或动画状态共享数据。
协作流程
存储数据:通过 setData(ticket, value) 存储自定义数据(如 IS_MOVING 布尔值)。
读取数据:控制器在 predicatestate 中调用 getData(ticket) 读取数据,决定动画逻辑。
示例

// 存储移动状态
animatableManager.setData(DataTicket.BOOLEAN, "IS_MOVING", true);
// 在控制器中读取
if (animatableManager.getData(DataTicket.BOOLEAN, "IS_MOVING")) {
   
    state.setAnimation(WALK_ANIM);
}

4. 动画时间管理

作用
跟踪动画更新时间(lastUpdateTime)和首次更新标志(isFirstTick),确保动画进度与游戏时间同步。
协作流程
更新触发:游戏每帧调用 AnimatableManager.updateAnimations(),根据 getTick() 计算时间差。
首帧处理isFirstTick 标记首次更新,初始化时间基准(firstTickTime)。
时间驱动:控制器通过 getLastUpdateTime() 获取时间差,推进动画状态机。
关键方法

public void updatedAt(double updateTime) {
   
    this.lastUpdateTime = updateTime;
}

5. 动画触发与协调

作用
通过 tryTriggerAnimation 方法触发特定动画,支持全局或指定控制器触发。
协作流程
外部调用:如实体受伤时调用 triggerAnim("attack_controller", "hit")
内部路由tryTriggerAnimation 遍历控制器,找到匹配名称的控制器并触发动画。
网络同步:若在服务端触发,通过 GeckoLibNetwork 发送数据包同步至客户端。
代码逻辑

public void tryTriggerAnimation(String controllerName, String animName) {
   
    AnimationController<?> controller = this.animationControllers.get(controllerName);
    if (controller != null) {
   
        controller.tryTriggerAnimation(animName);
    }
}

协作关系总览

游戏主循环 (Game Loop)
    │
    ├── 实体更新 (Entity Update)
    │    └── 调用 AnimatableManager.updateAnimations()
    │         ├── 更新 lastUpdateTime
    │         ├── 遍历所有 AnimationController
    │         │    ├── 调用控制器逻辑(状态机、过渡)
    │         │    └── 修改骨骼状态(BoneSnapshot)
    │         └── 更新 extraData
    │
    ├── 渲染流程 (Rendering)
    │    └── GeoModel 渲染器
    │         ├── 读取 boneSnapshotCollection
    │         └── 计算骨骼变换(插值/混合)
    │
    └── 网络事件 (Network Events)
         ├── 服务端触发动画(EntityAnimTriggerPacket)
         └── 客户端接收后调用 tryTriggerAnimation()

设计亮点

  1. 集中化管理
    所有动画相关状态(控制器、骨骼、数据)统一由 AnimatableManager 管理,降低耦合度。
  2. 扩展性
    ControllerRegistrar 允许动态注册控制器,支持复杂动画逻辑的组合。
  3. 性能优化
    Object2ObjectOpenHashMap 提供高效的数据存取。
    • 骨骼快照缓存减少重复计算,提升渲染性能。
  4. 跨线程安全
    时间戳和首次更新标志确保动画在客户端和服务端的一致性。

总结

AnimatableManager 是 GeckoLib 动画系统的“指挥中心”,负责整合控制器、骨骼状态、时间与数据,实现高效、可扩展的动画管理。它通过标准化的接口与 GeoAnimatableAnimationControllerGeoModel 等组件协作,使开发者能够专注于动画逻辑设计,而无需处理底层细节。

CoreGeoBone接口

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package software.bernie.geckolib.core.animatable.model;

import java.util.List;
import software.bernie.geckolib.core.state.BoneSnapshot;

public interface CoreGeoBone {
   
    String getName();

    CoreGeoBone getParent();

    float getRotX();

    float getRotY();

    float getRotZ();

    float getPosX();

    float getPosY();

    float getPosZ();

    float getScaleX();

    float getScaleY();

    float getScaleZ();

    void setRotX(float var1);

    void setRotY(float var1);

    void setRotZ(float var1);

    default void updateRotation(float xRot, float yRot, float zRot) {
   
        this.setRotX(xRot);
        this.setRotY(yRot);
        this.setRotZ(zRot);
    }

    void setPosX(float var1);

    void setPosY(float var1);

    void setPosZ(float var1);

    default void updatePosition(float posX, float posY, float posZ) {
   
        this.setPosX(posX);
        this.setPosY(posY);
        this.setPosZ(posZ);
    }

    void setScaleX(float var1);

    void setScaleY(float var1);

    void setScaleZ(float var1);

    default void updateScale(float scaleX, float scaleY, float scaleZ) {
   
        this.setScaleX(scaleX);
        this.setScaleY(scaleY);
        this.setScaleZ(scaleZ);
    }

    void setPivotX(float var1);

    void setPivotY(float var1);

    void setPivotZ(float var1);

    default void updatePivot(float pivotX, float pivotY, float pivotZ) {
   
        this.setPivotX(pivotX);
        this.setPivotY(pivotY);
        this.setPivotZ(pivotZ);
    }

    float getPivotX();

    float getPivotY();

    float getPivotZ();

    boolean isHidden();

    boolean isHidingChildren();

    void setHidden(boolean var1);

    void setChildrenHidden(boolean var1);

    void saveInitialSnapshot();

    void markScaleAsChanged();

    void markRotationAsChanged();

    void markPositionAsChanged();

    boolean hasScaleChanged();

    boolean hasRotationChanged();

    boolean hasPositionChanged();

    void resetStateChanges();

    BoneSnapshot getInitialSnapshot();

    List<? extends CoreGeoBone> getChildBones();

    default BoneSnapshot saveSnapshot() {
   
        return new BoneSnapshot(this);
    }
}


代码解释

一、接口基础结构

  1. 核心定义
    CoreGeoBone是用于描述骨骼动画模型的Java接口,定义骨骼的旋转、位移、缩放等三维空间属性和层级关系。
    getName():获取骨骼唯一标识(如"head"或"arm")
    getParent():获取父骨骼引用,构建骨骼层级树结构

  2. 接口特性
    符合Java接口的抽象性规范:
    • 所有方法默认public abstract(未显式声明修饰符)
    • 包含默认方法(如updateRotation())实现通用逻辑
    • 无成员变量(仅通过方法操作状态)


二、三维空间属性控制

1. 旋转控制(Rotation)
float getRotX/Y/Z()

相关文章:

  • ASL集睿致远 CS5265AN typec转hdmi4k60hz方案
  • 【Java】——运算符详解
  • 60V耐压 DC降压芯片SL3037B替换MP2459 MP2451 MP2456 MP2457
  • 19、TCP连接四次挥手的过程,为什么是四次?【高频】
  • 华为hcia——Datacom实验指南——TCP传输原理和数据段格式
  • 优选算法的匠心之艺:二分查找专题(一)
  • C语言【数据结构】:时间复杂度和空间复杂度.详解
  • 传感云揭秘:边缘计算的革新力量
  • 【Qt】QWidget属性介绍
  • Vmware安装ubuntu18.04
  • Kotlin apply 方法的用法和使用场景
  • 态势感知产品通用的一些安全场景设计
  • 防火墙虚拟系统配置
  • Gitlab报错:sudo: a password is required
  • 无障碍阅读(Web Accessibility)NVDA打开朗读查看器后,enter键不生效的原因
  • 如何处理PHP中的文件上传错误
  • 《计算机图形学》第二课笔记-----二维变换的推导
  • 时序数据库 TDengine 到 MySQL 数据迁移同步
  • 【实战解析】smallredbook.item_get_video API:小红书视频数据获取与电商应用指南
  • 事件总线EventBus原理剖析
  • 猫是影子,又是平行时空的使者
  • 神舟二十号航天员乘组将于近日择机实施第一次出舱活动
  • 顶刊论文现“飙脏话辱骂第二作者”,期刊回应:正积极调查
  • 第78届世界卫生大会20日审议通过“大流行协定”
  • 习近平向2025年上海合作组织减贫和可持续发展论坛致贺信
  • 技术派|台军首次试射“海马斯”火箭炮,如何压制这种武器?