-
-
Notifications
You must be signed in to change notification settings - Fork 131
Geckolib Items (Geckolib4)
Because Minecraft only ever keeps one instance of any given Item, GeckoLib animations for items need to be handled slightly differently. Where objects like Entities and BlockEntities can have their animations handled directly, Items need to make sure it's matching up with its respective synced equivalent.
Ensure you're thoroughly reading the examples and descriptions on this page to determine the required differences.
Creating a GeckoLib item requires the following steps:
- Creating your Blockbench Model
- Creating your Geo Model
- Creating your item display json
- Creating your item class
- Creating and registering your renderer
Steps #1 and #2 will not be covered on this page, instead visit their respective links for info. This page will focus on steps #3, #4, and #5
Quick Summary
- Implement
GeoItem
- Register as a
synced animatable
- Override
getAnimatableInstanceCache
andregisterControllers
- Instantiate a new
AnimatableInstanceCache
viaGeckoLibUtil.createInstanceCache(this)
at the top of your item class and return it ingetAnimatableInstanceCache
- Add any controllers you want for animations in
registerControllers
- Reference your renderer
There are a few things needed to set up a GeckoLib item class.
The first is to implement GeoItem
on your item class, and override the two methods your IDE will tell you to override.
This interface is the base for animatable items in GeckoLib, and lets the various other features of the mod pick up your item as an animatable one.
Next, we'll create an instance of an AnimatableInstanceCache
for our item. This stores our animatable instance so that it can be retrieved by the renderer and other outside areas.
To do this, we'll instantiate a new cache via GeckoLibUtil.createInstanceCache(this)
at the top of your item class, caching it in a final variable.
Next, we'll override getAnimatableInstanceCache
in our item class if it hasn't been done already, and return the factory instance we just created.
And finally, override registerControllers
in your item class. This method is called when your item is first being used for animations, and is where you define your actual animation handling.
This finalises the base setup for an animatable Item. However because of the nature of items, with these steps alone you'll only be able to do animations based on the client-side state of the itemstack, which often isn't very useful.
To rectify this, we need to do a couple extra things.
GeckoLib4 adds the ability to trigger animations remotely, from the server side. This greatly simplifies the process of handling animations from the server side.
To use this, we'll need to ensure we've registered our animatable as a synchable one, by calling SingletonGeoAnimatable.registerSyncedAnimatable
in the constructor of our item class. This tells GeckoLib that it should expect to need to sync either animations or data from server to client during runtime for this item.
Then, we can call triggerableAnim
on the AnimationController
that we want the animation to play on. This method takes the name of the trigger, and the RawAnimation
that will be played when triggered.
When we're ready to play the animation, we can call triggerAnim
, ensuring that we pass in the correct trigger name, and the numerical id of the stack obtained from GeoItem.getOrAssignId
. This id is assigned and used internally by GeckoLib to match up animatable instances, since Items are not unique.
GeckoLib then handles the rest for us, automatically resetting the animation's state on completion. Magic!
You may want your item's animations to only show in first-person, or only in third-person, or some other setup.
To do this, we need to override GeoItem#isPerspectiveAware
in our item class, returning true
.
Then, in your controller predicate, you can get the current render perspective via AnimationState.getData(DataTickets.ITEM_RENDER_PERSPECTIVE)
.
From there, you can handle your predicate based on the render perspective. This will cause your predicate to be called for each render perspective currently being rendered each render pass, allowing you to treat your predicate as a separate, non-colliding context for each render perspective.
To then get the item rendering, we'll need to make a renderer then register it. Due to the nature of items, this requires a little more work than is usual to register.
To make the renderer itself, just make a class that extends GeoItemRenderer
, passing in the GeoModel
instance to the constructor
public class ExampleItemRenderer extends GeoItemRenderer<ExampleItem> {
public ExampleItemRenderer() {
super(new ExampleItemModel());
}
}
Item renderers get registered a little differently to other renderers, and GeckoLib utilises the same system, so we need to be aware of the correct method of registration.
GeckoLib4->4.4
Registering the renderer differs slightly between Forge and Fabric, so for convenience the below section is split into two.
Forge Registration
On more modern versions of Forge, Forge provides a client-interfacing handler for item-specific functionalities. GeckoLib utilises this to render, so we'll need to implement this in our item class.
See below for an example implementation of this:
@Override
public void initializeClient(Consumer<IClientItemExtensions> consumer) {
consumer.accept(new IClientItemExtensions() {
private ExampleItemRenderer renderer = null;
// Don't instantiate until ready. This prevents race conditions breaking things
@Override public BlockEntityWithoutLevelRenderer getItemStackRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return renderer;
}
});
}
Fabric Registration
GeckoLib implements a dynamic render retrieving system for animatable items, to allow for flexible render handling. We will use this to implement our GeoItem's renderer
To do this, we need to do a few things.
First, we create and cache a new renderProvider in our item. This is done via GeoItem#makeRenderer
, and MUST be stored as Supplier<Object>
private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);
Then, we override getRenderProvider
, and return this provider
@Override
public Supplier<Object> getRenderProvider() {
return this.renderProvider;
}
And finally, we override createRenderer
, and give the consumer a new RenderProvider
, which tells GeckoLib how to find our renderer.
@Override
public void createRenderer(Consumer<Object> consumer) {
consumer.accept(new RenderProvider() {
private ExampleItemRenderer renderer;
@Override
public BlockEntityWithoutLevelRenderer getCustomRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return this.renderer;
}
});
}
You can alternatively use Fabric's built-in item renderer registration method - though no guarantee is made for 100% compatibility with this option in the future if further functionality is required.
GeckoLib4.5+
GeckoLib implements a dynamic render retrieving system for animatable items, to allow for flexible render handling. We will use this to implement our GeoItem's renderer
To do this, we need to override createGeoRenderer
, and provide the consumer a new instance of GeoRenderProvider
.
Then, we override getGeoItemRenderer
in that, and cache-return our renderer instance
@Override
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
consumer.accept(new GeoRenderProvider() {
private ExampleItemRenderer renderer;
@Override
public BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return this.renderer;
}
});
}
You can alternatively use Forge/NeoForge/Fabric's built-in item renderer registration methods - though no guarantee is made for 100% compatibility with this option in the future if further functionality is required.
GeckoLib4->4.4
Forge
public class ExampleItem extends Item implements GeoItem {
private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
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);
}
// Utilise the existing forge hook to define our custom renderer
@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, "Activation", 0, state -> PlayState.STOP)
.triggerableAnim("activate", ACTIVATE_ANIM));
// We've marked the "activate" animation as being triggerable from the server
}
// Let's handle our use method so that we activate the animation when right-clicking while holding the box
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
if (level instanceof ServerLevel serverLevel)
triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");
return super.use(level, player, hand);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
Fabric
public final class ExampleItem extends Item implements GeoItem {
private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
private final Supplier<Object> renderProvider = GeoItem.makeRenderer(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);
}
// Utilise our own render hook to define our custom renderer
@Override
public void createRenderer(Consumer<Object> consumer) {
consumer.accept(new RenderProvider() {
private ExampleItemRenderer renderer;
@Override
public BlockEntityWithoutLevelRenderer getCustomRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return this.renderer;
}
});
}
@Override
public Supplier<Object> getRenderProvider() {
return this.renderProvider;
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Activation", 0, state -> PlayState.STOP)
.triggerableAnim("activate", ACTIVATE_ANIM));
// We've marked the "activate" animation as being triggerable from the server
}
// Let's handle our use method so that we activate the animation when right-clicking while holding the box
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
if (level instanceof ServerLevel serverLevel)
triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");
return super.use(level, player, hand);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.cache;
}
}
GeckoLib4.5+
public final class ExampleItem extends Item implements GeoItem {
private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
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);
}
// Utilise our own render hook to define our custom renderer
@Override
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
consumer.accept(new GeoRenderProvider() {
private ExampleItemRenderer renderer;
@Override
public BlockEntityWithoutLevelRenderer getGeoItemRenderer() {
if (this.renderer == null)
this.renderer = new ExampleItemRenderer();
return this.renderer;
}
});
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "Activation", 0, state -> PlayState.STOP)
.triggerableAnim("activate", ACTIVATE_ANIM));
// We've marked the "activate" animation as being triggerable from the server
}
// Let's handle our use method so that we activate the animation when right-clicking while holding the box
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
if (level instanceof ServerLevel serverLevel)
triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");
return super.use(level, player, hand);
}
@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.