-
-
Notifications
You must be signed in to change notification settings - Fork 131
Geckolib 4 Changes
GeckoLib 4.0 was a substantial overhaul of the entire library and all of its associated classes. Because of this, there will be some things you will need to change in your mod to update your mod to work with GeckoLib 4.0+
- Change Summary
- Creating your Animatable class
- Creating your AnimatableInstanceCache
- Creating your RawAnimation (Previously AnimationBuilder)
- Making a GeoArmor
- Other notable changes
- Examples
Due to the extent of the overhaul in 4.0, there's not really any point in listing out all changes. Instead we can give a quick summary of the things that are most likely to impact you as a mod dev.
The below list is not an exhaustive list of changes, these are just the ones that are likely to be the most important ones
-
AnimationData
->AnimatableManager
-
AnimationFactory
->AnimatableInstanceCache
-
IGeoRenderer
->GeoRenderer
-
GeoLayerRenderer
->GeoRenderLayer
-
ExtendedGeoEntityRenderer
->DynamicGeoEntityRenderer
-
LayerGlowingAreasGeo
->AutoGlowingGeoLayer
-
AnimatedGeoModel
->GeoModel
-
GeckoLibIdTracker
->AnimatableIdCache
-
AnimationBuilder
->RawAnimation
-
IAnimatable
->GeoAnimatable
-
IBone
->CoreGeoBone
-
ILoopType
->Animation$LoopType
-
AnimationEvent
->AnimationState
-
AbstractLayerGeo
-> Merged intoGeoRenderLayer
-
IAnimatableModelProvider
-> Removed, functionality moved into renderers -
GeoModelProvider
-> Removed, functionality moved into renderers -
AnimatedTickingGeoModel
-> Removed, functionality moved into renderers -
EasingManager
-> Merged intoEasingType
-
EasingFunctionArgs
-> Merged intoEasingType
-
IAnimationTickable
-> Removed, functionality moved into renderers -
IAnimatableModel
-> Merged intoGeoModel
-
EDefaultLoopTypes
-> Merged intoAnimation$LoopType
-
GeoProjectilesRenderer
-> Merged intoGeoEntityRenderer
- Animatable classes now specify the subtype they belong to via interface (GeoEntity, etc)
- GeoModels now define the default render type for themselves, not the renderer
- The numerous model classes have all been collapsed into one
- Native data syncing now exists
- Server-triggerable animations now exists
- Render layers have expanded functionality and are now supported on all geo renderers
- Defaulted model classes now exist
- Animation controller/manager extra data is now generified and specific
- Glowlayers now supports pre-made mask images instead of using mcmeta (optional, can still use mcmeta)
- LoopTypes now support json-defined loop types
- Custom LoopTypes and EasingTypes now supported natively
- AnimationFactory split into two classes after being renamed, uses factory method to get new instance
- Bedrock Animation Format is now supported, along with with catmullRom/smooth easing
- AnimationControllers are now registered to a registrar, as opposed to directly to the AnimatableManager, and animation controllers are now reliably ordered in operation
Previously in GeckoLib3, you would implement IAnimatable
. In GeckoLib4, there are a handful of specific interfaces that you select from depending on your animatable type.
These are important because they hold useful builtin methods that will likely be critical for your interaction with GeckoLib
Animatable Type | Interface |
---|---|
Entity | GeoEntity |
Projectile | GeoEntity |
BlockEntity | GeoBlockEntity |
Item | GeoItem |
Armor | GeoItem |
Existing Entity | GeoReplacedEntity |
E.G.
public class ExampleEntity extends PathfinderMob implements GeoEntity
With exception to the very newest of the GeckoLib3 versions, you previously instantiated your AnimationFactory
with its constructor. In GeckoLib4, this class has been renamed, and you instantiate your cache with GeckoLibUtil.createInstanceCache(Animatable)
.
This allows GeckoLib to create a cache of an appropriate type for your animatable
E.G.
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
In GeckoLib3, animations to pass to the AnimationController
were created by using the class's constructor, then chaining addAnimation
.
As of GeckoLib4, this is a little different.
You now create your RawAnimation
via RawAnimation.begin()
, followed by chaining one of the various methods available in the RawAnimation
class.
One notable change is that by default; animations will now use the loop type defined in the animation json.
This means that if you use the thenPlay
method in RawAnimation
, your animation will loop in whatever manner your animation json defined, allowing for users to customise animations in resourcepacks to a greater degree.
As always, you should cache your RawAnimation
instances in a static field so you're not recreating the instance every render frame.
E.G.
private static final RawAnimation SPIN_ANIM = RawAnimation.begin().thenPlay("misc.spin");
In GeckoLib4, we make use of some inline render providers for armor rendering, instead of arbitrarily registering classes to the armor renderer.
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
private GeoArmorRenderer<?> renderer;
@Override
public @NotNull HumanoidModel<?> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<?> original) {
if (this.renderer == null)
this.renderer = new MyArmorRenderer();
// This prepares our GeoArmorRenderer for the current render frame.
// These parameters may be null however, so we don't do anything further with them
this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);
return this.renderer;
}
});
}
TBA
GeckoLib4 allows for animatables to define the default RenderType
for their object in the GeoModel
. This means that you do not need to extend or anonymously override the renderer to change the render type.
LoopType now supports custom loop types, allowing developers to handle looping in their own specific way.
This is done by registering a new instance of LoopType
to GeckoLib via GeckoLibUtil.addCustomLoopType(String, LoopType)
. This must be called during your mod constructor, otherwise the loop type will not be registered in time for GeckoLib to use it when deserialization the animation jsons
EasingType now supports custom easings, allowing developers to apply custom easings to animations if needed.
This is done by registering a new instance of EasingType
to GeckoLib via GeckoLibUtil.addCustomEasingType(String, EasingType)
. This must be called during your mod constructor, otherwise the easing type will not be registered in time for GeckoLib to use it when deserialization the animation jsons
Render Layers have been expanded greatly in GeckoLib4. Not only does the base layer have much more information provided to you at render time, it also comes with additional hooks that allow for pre-render operations, as well as a hook to render right in the middle of when a bone is being rendered.
In addition to this, several additional layers have been bundled in for use, such as a layer for dynamically hiding/showing bones, or one to render items or blocks alongside the model.
Previously in GeckoLib3, ExtendedGeoEntityRenderer
bundled several additional rendering functions for more advanced animatable rendering.
In GeckoLib4, about half of that functionality has been split into various GeoRenderLayers
allowing for any animatable to take advantage of the functionality.
Additionally, the class has been renamed to DynamicGeoEntityRenderer
.
DynamicGeoEntityRenderer
is now primarily used for per-bone render overrides, such as custom textures or render types.
GeckoLib4+ provides several built-in animatable model subclasses. These defaulted classes allow for easy and consistent handling of animatable asset paths. These are completely optional to use, but it is recommended to use them as they encourage consistency of use.
Example entity usage:
public class MyEntityModel extends DefaultedEntityGeoModel<MyEntity> {
public MyEntityModel() {
super(new ResourceLocation(MyMod.MOD_ID, "monster/my_entity"));
}
// This creates a new GeoModel with the following asset paths:
// Animation Json: assets/mymod/animations/entity/monster/my_entity.animation.json
// Model Json: assets/mymod/geo/entity/monster/my_entity.geo.json
// Texture: assets/mymod/textures/entity/monster/my_entity.png
}
As an added bonus, the DefaultedEntityGeoModel
class can also automatically handle head rotation for you, by simply using the alternate constructor with a boolean in it.
This will then automatically rotate the entity's "head" bone to the position it should be facing.
GeckoLib4 comes bundled with a suite of example/template animations. These can all be found in the DefaultAnimations class.
They are completely optional to use, but they do encourage consistency of animation naming, as well as saving you from needing to instantiate new RawAnimations
for basic animations.
It also contains several generic animation controller factories, allowing you to quickly instantiate a basic controller with minimal additional effort
E.G.
// Here we register two controllers that automatically handles walking, idling, and attacking for us
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(
DefaultAnimations.genericWalkIdleController(this),
DefaultAnimations.genericAttackAnimation(this, DefaultAnimations.ATTACK_STRIKE)
);
}
GeckoLib4 makes use of a functionality called DataTickets
. These are generified objects that act as 'keys' for transient data in GeckoLib.
For example, the AnimationState
class no longer has a getExtraDataOfType
method. Instead, it's replaced with a getData
method that takes a DataTicket
key. This returns the exact data that has been stored either by you or GeckoLib previously, without requiring you to filter out potential unwanted values.
GeckoLib stores a handful of default DataTicket
implementations in the DataTickets class, but you can create your own at any time without any registration required.
E.G.
Entity entity = state.getData(DataTickets.ENTITY);
New to GeckoLib4 is native data-syncing for animatables. This allows for mod devs to efficiently send quick data bites to relevant clients for handling on the client side without needing to register synched entity data or custom packets.
This is done through SerializableDataTickets. Unlike normal DataTickets
, the serializable variants do require registration with GeckoLib to function.
This can be done through GeckoLibUtil.addDataTicket
at any time.
GeckoLib itself pre-registers several potentially useful SerializableDataTickets
however, so you may not need to register one of your own.
These can be found in the DataTickets class
To use this system, simply call setAnimData
in your animatable class, providing the ticket and the data you want to send. GeckoLib will handle the rest for you. You can also call this on the client side to set and store data there if wanted
E.G.
setAnimData(DataTickets.ACTIVE, true);
Note that for singleton animatables (Items and replaced geo entities), you must additionally register your syncable animatable with GeckoLib in your class constructor via SingletonGeoAnimatable.registerSyncedAnimatable
E.G.
public ExampleItem(Properties properties) {
super(properties);
SingletonGeoAnimatable.registerSyncedAnimatable(this);
}
New to GeckoLib4 is the ability to trigger animations in an animatable, from the server! This opens a lot of interesting options for more complex objects and animations.
Triggered animations will override the AnimationState
calls for the AnimationController
that is running it for the duration that it is being run, so it is recommended that you only trigger non-looping animations, but it can still be stopped by calling stop()
on the controller at any stage.
To use this system, we must first tell our controller that they have a triggerable animation. This is done by calling .triggerableAnim
on the controller when we instantiate it, giving it a trigger name, and the RawAnimation
associated with the trigger
E.G.
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "shoot_controller", state -> PlayState.STOP)
.triggerableAnim("shoot", SHOOT_ANIM));
}
Once that has been done, we can then call triggerAnim
from the server side at any time, providing the controller name and the trigger name.
E.G.
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
if (level instanceof ServerLevel serverLevel)
triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "shoot_controller", "shoot");
return super.use(level, player, hand);
}
Note that for singleton animatables (Items and replaced geo entities), you must additionally register your syncable animatable with GeckoLib in your class constructor via SingletonGeoAnimatable.registerSyncedAnimatable
E.G.
public ExampleItem(Properties properties) {
super(properties);
SingletonGeoAnimatable.registerSyncedAnimatable(this);
}
public class ExampleEntity extends PathfinderMob implements GeoEntity {
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public ExampleEntity(EntityType<? extends ExampleEntity> type, Level level) {
super(type, level);
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "idle", 5, state -> state.setAndContinue(DefaultAnimations.IDLE)));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
public final class ExampleItem extends Item implements GeoItem {
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstancecache(this);
public ExampleItem(Properties properties) {
super(properties);
// Register our item as server-side handled.
// This enables both animation data syncing and server-side animation triggering
SingletonGeoAnimatable.registerSyncedAnimatable(this);
}
@Override
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
private ExampleItemRenderer renderer;
@Override
public BlockEntityWithoutLevelRenderer getCustomRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return this.renderer;
}
});
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "shoot_controller", state -> PlayState.STOP)
.triggerableAnim("shoot", DefaultAnimations.ATTACK_SHOOT));
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
if (level instanceof ServerLevel serverLevel)
triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "shoot_controller", "shoot");
return super.use(level, player, hand);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
public final class ExampleArmor extends ArmorItem implements GeoItem {
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public ExampleArmor(ArmorMaterial armorMaterial, EquipmentSlot slot, Properties properties) {
super(armorMaterial, slot, properties);
}
@Override
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
private ExampleArmorRenderer<?> renderer;
@Override
public @NotNull HumanoidModel<?> getHumanoidArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, HumanoidModel<?> original) {
if (this.renderer == null)
this.renderer = new ExampleArmorRenderer();
this.renderer.prepForRender(livingEntity, itemStack, equipmentSlot, original);
return this.renderer;
}
});
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(DefaultAnimations.genericIdleController(this));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
public class ExampleBlockEntity extends BlockEntity implements GeoBlockEntity {
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
public ExampleBlockEntity(BlockPos pos, BlockState state) {
super(TileRegistry.EXAMPLE_BLOCK_ENTITY.get(), pos, state);
}
@Override
public void (AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, state -> {
if (getLevel().getDayTime() > 23000 || getLevel().getDayTime() < 13000) {
return state.setAndContinue(DefaultAnimations.REST);
}
else {
return state.setAndContinue(DefaultAnimations.IDLE);
}
}));
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
Geckolib 3
Geckolib 4
- Installation
- Getting Started
- Upgrading from GeckoLib 3.1.x to 4.0
- Updating to GeckoLib 4.5
- Basic
- Advanced
- Miscellaneous
Package repository hosting is graciously provided by Cloudsmith.
Cloudsmith is the only fully hosted, cloud-native, universal package management solution that enables your organization to create, store and share packages in any format, to any place, with total confidence.