Skip to content

Animation API

Wyn Price edited this page Apr 27, 2020 · 5 revisions

Non ECS

Definitions

  • AnimationContainer - holds all the animation data
  • AnimationContainerEntry - holds all the animation state for a instance
  • SingleAnimationContainerEntry - an implimentation of AnimationContainerEntry that runs a single animation at a time
  • ChannelAnimationContainerEntry - an implimentation of AnimationContainerEntry that can run multiple animations at once For this example, we're going to animate a mob called duck with the animations running and sitting

Creating the ModAnimationData class

Create a class to store the AnimationContainer and all of the Animation's. An example of this is as below:

public class ModAnimationData {
    public static final AnimationContainer MY_ANIMATION_CONTAINER = new AnimationContainer(MyMod.MOD_ID, "duck");

    public static final Animation RUNNING = new Animation(MyMod.MOD_ID, "running");
    public static final Animation SITTING = new Animation(MyMod.MOD_ID, "sitting");
}

Editing the Entity

Add the interface EntityWithAnimation onto your chosen entity class, and impliment the methods.

public class EntityDuck extends EntityCreature implements EntityWithAnimation {

    public EntityDuck(World worldIn) {
        super(worldIn);
    }

    @Override
    public AnimationContainerEntry getAnimationEntry() {
        return null;
    }
}

Next, create either an animation container (either single or channel)

Single animation container

Create the SingleAnimationContainerEntry field on the entity and return it on the getAnimationEntry method:

private SingleAnimationContainerEntry entry = new SingleAnimationContainerEntry(ModAnimationData.MY_ANIMATION_CONTAINER, this);

@Override
public EntityAnimationContainer getAnimationEntry() {
    return this.entry;
}

Channel animation container

Create the ChannelAnimationContainerEntry field on the entity and return it on the getAnimationEntry method:

private ChannelAnimationContainerEntry entry = new ChannelAnimationContainerEntry(ModAnimationData.MY_ANIMATION_CONTAINER, this, 10);

@Override
public EntityAnimationContainer getAnimationEntry() {
    return this.container;
}

You give the channel animation container a number of channels (in this case I've given it 10), and that allows you to play one animation on each channel. For example, you can have all walking/running/swimming on one channel and eating/drinking on another channel. Doing this allows animations to be played on top of eachother. In the rest of this example, I shall be using a single animation container.

Entity renderer

Creating the renderer is very simple. Create it as you would normaly, but pass in a TabulaModel instead of ModelBase. This tabula model should be the model from your container. You can either use TabulaUtils#getModel(ResourceLocation), or the better way is to grab the model from the AnimationContainer with AnimationContainer#getMainModel#createModel

public class DuckRenderer extends RenderLiving<DuckEntity> {

    private static final ResourceLocation TEXTURE = new ResourceLocation(MyMod.MOD_ID,"textures/entity/duck.png");

    public DuckRenderer(RenderManager rendermanagerIn) {
        super(rendermanagerIn, ModAnimationData.MY_ANIMATION_CONTAINER.getMainModel().createModel(), 0.25F);
    }

    @Nullable
    @Override
    protected ResourceLocation getEntityTexture(DuckEntity entity) {
        return TEXTURE;
    }
}

Model and animation files

Folder location

We named our animation container with the modid: mymodid and the path duck, so the path would be in assets/mymodid/models/entities/duck/
Note, you can split up the model path by adding a _ to the path. For example:

  • new AnimationContainer("somemodid", "someentity") -> assets/somemodid/models/entities/someentity/
  • new AnimationContainer("somemodid", "someentity_a") -> assets/somemodid/models/entities/someentity/a/
  • new AnimationContainer("somemodid", "someentity_a_b") -> assets/somemodid/models/entities/someentity/a/b/

Tabula Model Location

The animation container will search for a .tbl file as the path passed into the animation container. (models/entities has been replaced with ...)

  • new AnimationContainer("somemodid", "someentity") -> assets/somemodid/.../someentity/someentity.tbl
  • new AnimationContainer("somemodid", "someentity_a") -> assets/somemodid/.../someentity/a/someentity_a.tbl
  • new AnimationContainer("somemodid", "someentity_a_b") -> assets/somemodid/.../someentity/a/b/someentity_a_b.tbl So with our duck, we'd put the .tbl file in assets/mymodid/models/entities/duck/duck.tbl

Animation files

There are two ways to define animations. With our .dca format, or a folder containing a list of .tbl models with an optional animation.json

DCA Files

Using the Dumbcode Animation Studio, you can produce .dca files. (DCA stands for DumbCodeAnimation) To load these, .dca files, put them in the same directory as the .tbl file. As the animations I've defined are running and sitting, the file tree would be as follows:

assets
└───mymodid
    └───models
        └───entities
            └───duck
                ├───duck.tbl
                ├───running.dca
                └───sitting.dca

List of tabula files

It's also possible to load a list of .tbl files as an animation, with an optional animation.json file. The folder would be created in the same folder as the base .tbl file and would be named the animation path.

assets
└───mymodid
    └───models
        └───entities
            └───duck
                ├───duck.tbl
                ├───running
                │   ├───duck_runnin_a.tbl
                │   ├───duck_runnin_b.tbl
                │   ├───duck_runnin_c.tbl
                │   └───duck_runnin_d.tbl
                └───sitting
                    ├───animation.json
                    ├───duck_sitting_0.tbl
                    ├───duck_sitting_1.tbl
                    ├───duck_sitting_2.tbl
                    ├───duck_sitting_3.tbl
                    ├───duck_sitting_4.tbl
                    └───duck_sitting_5.tbl

The naming of the .tbl files doesn't matter, but the animations are loaded in alphabetical order so ensure that the animations in the folder are in the correct order.

Animation.json

As you can see above, the animation.json is optional. All it does is define the time for each pose. Without it, each pose has a default of 5 ticks. Each part of the json file is optional. An empty json file is valid and will apply the default tick length (5) to each animation. The animation.json for the sitting animation is as follows:

{
   "base_time": 2,
   "overrides": [
      { "index": 0, "time": 5 },
      { "index": 4, "time": 12 },
   ]
}

This will mean the poses for sitting have the following time: [5, 2, 2, 2, 12, 2]

Loading animations for other modids

Say you had another mod othermod, and wanted to load the animation flying from that mod. The animation would be defined as new Animation("othermod", "flying");, although you would use the reference from the othermod mod. The only thing this changes is the location of the .dca file or the folder location, depending on which method you use.

For both methods, simply add the other mods modid (othermod) as a folder where the base .tbl model is, then either putting the .dca file there or the folder containing the .tbl files. Example for .dca:

assets
└───mymodid
    └───models
        └───entities
            └───duck
                ├───duck.tbl
                ├───othermod
                │   └───flying.dca
                ├───running.dca
                └───sitting.dca

Example for .tbl models:

D:.
└───mymodid
    └───models
        └───entities
            └───duck
                ├───duck.tbl
                ├───othermod
                │   └───flying
                │       ├───animation.json
                │       ├───duck_flying_0.tbl
                │       ├───duck_flying_1.tbl
                │       └───duck_flying_3.tbl
                ├───running
                │   ├───duck_runnin_2.tbl
                │   ├───duck_runnin_3.tbl
                │   ├───duck_runnin_a.tbl
                │   └───duck_runnin_b.tbl
                └───sitting
                    ├───animation.json
                    ├───duck_sitting_0.tbl
                    ├───duck_sitting_1.tbl
                    ├───duck_sitting_2.tbl
                    ├───duck_sitting_3.tbl
                    ├───duck_sitting_4.tbl
                    └───duck_sitting_5.tbl

Starting/Stopping the Animation

Starting

Once you've set it all up, playing the animation is very easy. You want to call the playAnimation method when the animation starts, calling it every tick will cause the animation to behave weird:

public void startSitting() {
    this.sittingTicks = 0;
    this.container.playAnimation(ModAnimationData.SITTING);
}

public void startRunning() {
    this.running = true;
    this.container.playAnimation(ModAnimationData.RUNNING);
}

You can also define the animation to have certian attributes. For example, we want the running animation to loop, so we can do:

this.container.playAnimation(ModAnimationData.RUNNING.createEntry().loop())

Stopping

You can stop animations with this.container.stopAnimation() for single animation containers, and this.container.stopAll() or this.container.stopAnimation(channel) for channel animation conatiners