-
Notifications
You must be signed in to change notification settings - Fork 1
Animation API
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 calledduck
with the animationsrunning
andsitting
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");
}
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)
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;
}
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.
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;
}
}
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/
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 inassets/mymodid/models/entities/duck/duck.tbl
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
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
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.
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]
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
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())
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
Somthing missing or broken? Tell Wyn or Jack about it on discord @Wyn Price#0001
or @Jack | JTGhawk137 #4033