本篇教程的视频:

本篇教程源代码

GitHub地址:TutorialMod-EntityAni-1.21

介绍

前面我们已经为我们的模组添加了生物实体,但这个实体没有动画,本篇教程我们就来为我们的生物实体添加动画

当然,这里的动画是采用原版的关键帧系统,而不是GeckoLib这个库(原版的动画系统是在加入骆驼以后启用的,其他动物的动画是硬编码)

动画制作的注意事项也在上篇教程中提到了,这里就不再赘述,我们是在Blockbench中制作动画

动画其实也没什么技术含量,只要不是特别复杂的动画,你K帧就好了,要涉及特效啥的那就另当别论了

生物实体动画

我这里的例子是创建了三个动画状态,分别是待机行走攻击,其中攻击的动画我们会在下一篇教程中使用

导出动画

那么现在我们就要导出动画了,我们选择导出 -> Export Animations to Java,这个是Blockbench的插件Animation to Java Converter提供的功能,不要忘记装

在弹出的映射选择界面,Mappings选择Yarn,然后选择一个地方导出,这里导出的文件是txt文件,但里面是Java的代码

创建TigerAnimation类

我们现在就回到我们的模组中,创建一个TigerAnimation类,这个类用于实体的动画

然后我们将刚才导出的txt文件中的代码粘贴到这个类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public class TigerAnimation {

public static final Animation IDLE = Animation.Builder.create(2f).looping()
.addBoneAnimation("head",
new Transformation(Transformation.Targets.TRANSLATE,
new Keyframe(0f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createTranslationalVector(0f, -0.5f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("body",
new Transformation(Transformation.Targets.TRANSLATE,
new Keyframe(0f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createTranslationalVector(0f, -0.5f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("tail",
new Transformation(Transformation.Targets.TRANSLATE,
new Keyframe(0f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createTranslationalVector(0f, -0.5f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createTranslationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("tail",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(0f, -20f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(0f, 20f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR))).build();
public static final Animation WALK = Animation.Builder.create(2f).looping()
.addBoneAnimation("left_front_leg",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(-15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("right_front_leg",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(-15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("left_back_leg",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(-15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("right_back_leg",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(-15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(15f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR)))
.addBoneAnimation("tail",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(0.5f, AnimationHelper.createRotationalVector(0f, -20f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.5f, AnimationHelper.createRotationalVector(0f, 20f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR))).build();
public static final Animation ATTACK = Animation.Builder.create(2f).looping()
.addBoneAnimation("head",
new Transformation(Transformation.Targets.ROTATE,
new Keyframe(0f, AnimationHelper.createRotationalVector(0f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1f, AnimationHelper.createRotationalVector(20f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(1.0416767f, AnimationHelper.createRotationalVector(-16.67f, 0f, 0f),
Transformation.Interpolations.LINEAR),
new Keyframe(2f, AnimationHelper.createRotationalVector(-0.83f, 0f, 0f),
Transformation.Interpolations.LINEAR))).build();
}

没有的类都给它导入进来

Animation是动画类,create方法是创建动画,参数是动画的持续时间,这里是2flooping是循环播放,循环得设置,不然所有的动画只播放一次

addBoneAnimation是添加骨骼动画,其实这里的参数我们了解也好,不了解也没关系

这里的Keyframe就是原版的关键帧类

修改实体类

现在我们就回到我们的实体类TigerEntity中,添加动画

首先我们要为实体添加动画状态

1
2
3
public static final AnimationState idleAnimation = new AnimationState();

public int idleAnimationTimeOut = 0;

这里我们添加了一个idleAnimation动画状态,还有一个idleAnimationTimeOut变量,这个是动画的持续时间

然后我们写setupAnimation方法,这个方法是设置动画的方法

1
2
3
4
5
6
7
8
private void setupAnimation() {
if (idleAnimationTimeOut <= 0) {
idleAnimationTimeOut = this.random.nextInt(40) + 80;
idleAnimation.start(this.age);
} else {
idleAnimationTimeOut--;
}
}

这里我们判断idleAnimationTimeOut是否小于等于0,如果是,就设置一个随机的时间,然后开始动画,否则就减少时间

然后是tick方法

1
2
3
4
5
@Override
public void tick() {
super.tick();
setupAnimation();
}

在它的里面加入setupAnimation方法

最后再重写updateLimbs方法

1
2
3
4
5
@Override
protected void updateLimbs(float posDelta) {
float f = this.getPose() == EntityPose.STANDING ? Math.min(posDelta * 6.0f, 1.0f) : 0.0f;
this.limbAnimator.updateLimbs(f, 0.2f);
}

这个方法是更新实体的动画,这里我们设置了一个limbAnimator,这个是实体的动画控制器,updateLimbs方法是更新实体的动画

说实话,这个f就是一个随机值,具体怎么来的得问Mojang了(因为这里的方法是照搬骆驼的)

设置模型

那么现在我们就回到我们的TigerModel类中,设置对应模型的动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void setAngles(T entity, float limbAngle, float limbDistance, float animationProgress, float headYaw, float headPitch) {
this.getPart().traverse().forEach(ModelPart::resetTransform);
this.setHeadAngles(headYaw, headPitch);

this.animateMovement(TigerAnimation.WALK, limbAngle, limbDistance, 2f, 2.5f);
this.updateAnimation(TigerEntity.idleAnimation, TigerAnimation.IDLE, animationProgress, 1f);
}

private void setHeadAngles(float headYaw, float headPitch) {
headYaw = MathHelper.clamp(headYaw, -30.0f, 30.0f);
headPitch = MathHelper.clamp(headPitch, -25.0f, 45.0f);
this.head.yaw = headYaw * 0.017453292f;
this.head.pitch = headPitch * 0.017453292f;
}

我们重写之前留下来的setAngles方法,这里我们先重置模型的变换,这个一定要写,不然动画会出问题(你可以试试,很抽象)

然后我们设置头部的角度,单独拿了一个方法过来,虽然也是骆驼的,但也能用(只是这种奇奇怪怪的随机数我是不知道它的含义的)

随后我们调用animateMovement方法,这个方法是设置实体的移动动画,这里我们设置的是WALK动画,后面两个参数分别是角度缩放和距离缩放

最后我们调用updateAnimation方法,这个方法是更新动画,这里我们设置的是IDLE动画,后面两个参数分别是动画进度和速度

那么现在我们就可以进入我们的游戏中看看我们的实体了