---
title: Animating Items
hide_meta: true
---

<Callout variant="info">
    This page assumes you have exported the assets properly as per [How to Export Your Project](../blockbench/exporting_assets)

    You can find working examples [here](https://github.com/AzureDoom/Azurelib-Rewrite-Examples/tree/1.21.1/common/src/main/java/mod/azure/azexamples/items)
</Callout>

# How to animate Items

## Creating your custom AzItemAnimator

For this, you will create a class which extends `AzItemAnimator`. This class will handle the registration of animation controllers and providing the animation file's location for the animations.

Example:
```java
public class ExampleItemAnimator extends AzItemAnimator {
    private static final ResourceLocation ANIMATIONS = ResourceLocation.fromNamespaceAndPath(
        YOUR_MOD_ID,
        "animations/item/exampleitem.animation.json"
    );

    @Override
    public void registerControllers(AzAnimationControllerContainer<ItemStack> animationControllerContainer) {
        animationControllerContainer.add(
            AzAnimationController.builder(this, "base_controller")
                .build()
        );
    }

    @Override
    public @NotNull ResourceLocation getAnimationLocation(ItemStack animatable) {
        return ANIMATIONS;
    }
}
```

Some highlights of the above example.

- `ANIMATIONS`:
This stores the `ResourceLocation` reference to the animation JSON file. You need to replace `YOUR_MOD_ID` with your mod's ID and ensure the file path matches your JSON animation file.

- `registerControllers()`
Registers the animation controllers that will define the animation behavior and states for the item. You can multiple if needed.

## Creating an Animation Dispatcher

<Callout variant="info">
    You can find more about [AzCommands here](../misc/animating/azcommands_101)
</Callout>

This class isn't required but is highly suggested to help store your animation commands to call in other classes.

Example:
```java
public class ExampleItemDispatcher {
    private static final AzCommand FIRING_COMMAND = AzCommand.create("base_controller", "firing", AzPlayBehaviors.PLAY_ONCE);

    public void firing(Entity entity, ItemStack itemStack) {
        FIRING_COMMAND.sendForItem(entity, itemStack);
    }
}
```

## Accessing your Dispatcher to trigger animations

You will now define your ExampleItemDispatcher from above in your Item class.

```java
public class ExampleItem extends Item {
    // This is your class where you will setup the AzCommands/Animations you wish to play
    public final ExampleItemDispatcher dispatcher;

    public ExampleItem(Properties properties) {
        super(properties);
        // Create the instance of the class here to use later.
        this.dispatcher = new ExampleItemDispatcher();
    }
}
```

Now to use the dispatcher, you would simply call it in any method where you want it trigger for, like if I want the firing to happing on use, I would do it like so:
```java
    @Override
    public void onUseTick(Level level, LivingEntity livingEntity, ItemStack stack, int remainingUseDuration) {
        super.onUseTick(level, livingEntity, stack, remainingUseDuration);
        if (livingEntity instanceof Player player && !level.isClientSide()) {
            // This is where you now trigger an animation to play
            dispatcher.firing(player, stack);
        }
    }
```

## Creating your Renderer

This renderer is responsible for how your custom item is displayed in the game.

It connects the item with the following:
1. **Geometry file (`geo.json`)**: Defines the 3D model of the item.
2. **Texture file (`.png`)**: The visual appearance of the item (applies over the geometry).
3. **Animator**: Animations for the item, provided by `ExampleItemAnimator`.

<Callout variant="info">
    See [AzRendererConfigs 101](../misc/rendering/azrenderer_config#azitemrendererconfig) for all AzItemRendererConfig#builder options.
</Callout>
<Callout variant="warning">
    There is currently a bug in the plugin which exports the displays with an offset applied, please use useNewOffset(true) in your AzItemRendererConfig builder to fix this.
</Callout>

```java
public class ExampleItemRenderer extends AzItemRenderer {
    private static final ResourceLocation GEO = ResourceLocation.fromNamespaceAndPath(
        YOUR_MOD_ID,
        "geo/item/exampleitem.geo.json"
    );

    private static final ResourceLocation TEX = ResourceLocation.fromNamespaceAndPath(
        YOUR_MOD_ID,
        "textures/item/exampleitem.png"
    );

    public ExampleItemRenderer() {
        super(
            AzItemRendererConfig.builder(GEO, TEX)
                .setAnimatorProvider(ExampleItemAnimator::new).build()
        );
    }
}
```

## Different animations in first person and third person

By default an item stack has one animator, so whatever plays in first person also plays in third person. If you want the two views to play different animations at the same time (for example an aimed fire in first person while other players see a hip fire), opt in with `useSeparateFirstPersonAnimator()`. The first-person hand contexts then get their own animator with their own controllers, bone state and timer; third person, GUI, ground, item frames and every other context keep using the default animator.

```java
public ExampleItemRenderer() {
    super(
        AzItemRendererConfig.builder(GEO, TEX)
            .setAnimatorProvider(ExampleItemAnimator::new)
            .useSeparateFirstPersonAnimator()
            .build()
    );
}
```

Both animators are built from the same `AzItemAnimator`, so they share controller names and the same animation file. Target one view when you dispatch:

```java
public class ExampleItemDispatcher {
    private static final AzCommand AIM_FIRING = AzCommand.create("base_controller", "aimfiring", AzPlayBehaviors.PLAY_ONCE);
    private static final AzCommand FIRING = AzCommand.create("base_controller", "firing", AzPlayBehaviors.PLAY_ONCE);

    public void firing(Entity entity, ItemStack itemStack) {
        AIM_FIRING.sendForItemFirstPerson(entity, itemStack);
        FIRING.sendForItemThirdPerson(entity, itemStack);
    }
}
```

`sendForItem(entity, stack)` without a view still plays on both animators, and `sendForItem(entity, stack, AzItemAnimatorView)` lets you pick the view from a variable. For anything other than a plain first/third split, `setAnimatorViewProvider(context -> ...)` maps each `ItemDisplayContext` to a view yourself.

## Registering your Renderer

Call `AzItemRendererRegistry#register()` in your `onInitializeClient` for Fabric, or inside `event.enqueueWork(...)` in your `FMLClientSetupEvent` handler for NeoForge.

<Callout variant="warning">
    On NeoForge, setup events fire for every mod **at the same time** on parallel worker threads. Always register inside `event.enqueueWork(...)`, which runs your code on the main thread after every mod's handler has finished, so your registrations can't race another mod's. Registering directly in the event handler can cause renderers to go missing at random between launches. Fabric runs its initializers one after another on a single thread, so no wrapper is needed there.
</Callout>

Fabric:

```java
@Override
public void onInitializeClient() {
    AzItemRendererRegistry.register(ExampleItemRenderer::new, YourItemRegistry.YOUR_ITEM);
}
```

NeoForge:

```java
private void onClientSetup(FMLClientSetupEvent event) {
    event.enqueueWork(() -> {
        AzItemRendererRegistry.register(ExampleItemRenderer::new, YourItemRegistry.YOUR_ITEM);
    });
}
```

## Registering your Armor for proper animation triggering

To ensure trigger animations work properly, you will need to also call `AzIdentityRegistry#register()` in your `onInitialize` for Fabric, or inside `event.enqueueWork(...)` in your `FMLCommonSetupEvent` handler for NeoForge. `FMLCommonSetupEvent` also runs in parallel across mods, so the same `enqueueWork` rule applies.

Fabric:

```java
@Override
public void onInitialize() {
    AzIdentityRegistry.register(YourItemRegistry.YOUR_ITEM, ...);
}
```

NeoForge:

```java
private void onCommonSetup(FMLCommonSetupEvent event) {
    event.enqueueWork(() -> {
        AzIdentityRegistry.register(YourItemRegistry.YOUR_ITEM, ...);
    });
}
```