Custom Character Animations - daviscook477/BaseMod GitHub Wiki
Spine/Dragonbones Animations (Game Native)
Spine is the toolkit Megacrit used to create their animations, and thus Spine has the best support within the game engine, with a smaller performance hit compared to both of the options below. However, a license for Spine costs USD99, which is a little outside what most people would like to spend on their Slay the Spire mod. If you already own it for some reason, great! But for the the rest of us, there's Dragonbones, which exports to Spine format. Grab it here. (Dragonbones is on github too!)
Dragonbones is primarily designed for skeletal animation, just like Spine and Spriter, however you can work in support for sprite animations as well. Either way, make sure you select armature template when creating your new project - then make your magic!
A few notes while animating -
The point where the lines cross should be the "floor" of the animation.
Slay the Spire runs animations at 30fps, while Dragonbones defaults to 24fps. Make sure you update that before you start timing!
While Dragonbones has support for multiple points per animation curve, the game engine does not, and will not correctly render your animation curve if you use them.
A curve like the example above will instead default to a linear transformation in the game engine. Thus you must unfortunately restrict yourself to only manipulating the starting and ending points of the curve
Once your animation is complete, export using these settings
- Data Config - Type must be Spine.
- Texture Config - Image Type must be Texture Atlas.
- And while it may not be the case any more, old versions of Dragonbones used to cause issues if Project Name was not set to skeleton, so keep that in mind.
To actually get your animation in game, just like Spriter below, in the constructor for your class extending CustomPlayer
you will call super()
using the CustomPlayer
constructor - inserting the relevant data for your custom character.
And then start your animation using
AnimationState.TrackEntry e = state.setAnimation(0, "YourAnimationName", true);
And then you should be good to go!
Spriter Animations
Get Spriter from https://brashmonkey.com/forum/index.php?/files/category/8-software/ and use it to create your animation. This will require having your character model split up into separate textures so you can move them about individually and some knowledge of skeletal animation. Try taking a look on youtube for "spriter tutorial". This is the official tutorial playlist from BrashMonkey https://www.youtube.com/playlist?list=PL8Vej0NhCcI5sxOT64-Z31LW59VUyAVfr. Once you've made your spriter file, copy it to the resources directory for your mod. Then when defining your class that extends CustomPlayer
when you make your call to super
you will want to use this constructor:
public CustomPlayer(String name, PlayerClass playerClass, String[] orbTextures, String orbVfxPath, float[] layerSpeeds, AbstractAnimation animation)
For the animation
parameter you will want to pass an instance of basemod.animations.SpriterAnimation
which has a very simple constructor: public SpriterAnimation(String filepath)
. Simply pass the path to your spriter file to the SpriterAnimation
constructor and you should be all set to be using animations made in Spriter.
G3DJ Animations (Old, Difficult to use, and has a Performance Hit)
To make custom character animations you need Blender and fbx-conv:
- Blender (https://www.blender.org/)
- fbx-conv (https://github.com/libgdx/fbx-conv)
In Blender you should just create a bunch of textured quads with your character art on them. (You need to be able to import images as planes). Look up some tutorial for 2d animation to see how to do this and make animations. You need these animations: (TODO list required animations for StS).
You have to make your character quads in the xy-plane with the textures facing in the negative Z direction. Turn on Backface Culling
in Blender so you can tell if your texture has its normals facing properly.
When exporting to fbx
from Blender, you should be able to leave things at default but here are the requirements:
- scale should be
1.0
to start out, up the scale to what looks right as you test Y-up
forward: -Z
Then when using fbx-conv to convert the fbx into either a g3db or g3dj file you need to use the -f
flag for flipping textures because otherwise everything will be upside down.
There are two file formats that libgdx supports: g3db
and g3dj
. g3db
is a binary format whereas g3dj
is a json format. Right now we only support loading from the json format but I'll get g3db
loading working soon enough.
Take a look at (https://github.com/gskleres/FruityMod-StS) to see an example of how to get the character to use these files once you've made them. This feature isn't particularly well supported yet so hop on the StS discord on the unsupported-modding
channel if you need help with it.
Here's an example of how to load a g3dj file:
// Model loader needs a binary json reader to decode
JsonReader jsonReader = new JsonReader();
// Create a model loader passing in our json reader
G3dModelLoader modelLoader = new G3dModelLoader(jsonReader);
// Now load the model by name
myModel = modelLoader.loadModel(Gdx.files.internal("data/seeker.g3dj"));
// Necessary to get transparent textures working - I don't know why
for (Material mat : myModel.materials) {
mat.set(new BlendingAttribute(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA));
}
// Now create an instance. Instance holds the positioning data, etc
// of an instance of your model
myInstance = new ModelInstance(myModel, 0, 0, 10.0f);
// fbx-conv is supposed to perform this rotation for you... it
// doesnt seem to
myInstance.transform.rotate(1, 0, 0, -90);
// You use an AnimationController to um, control animations. Each
// control is tied to the model instance
controller = new AnimationController(myInstance);
// Pick the current animation by name
controller.setAnimation("bottom|idle", 1, new AnimationListener() {
@Override
public void onEnd(AnimationDesc animation) {
// this will be called when the current animation is done.
// queue up another animation called "Jump".
// Passing a negative to loop count loops forever. 1f for
// speed is normal speed.
controller.queue("bottom|idle", -1, 1f, null, 0f);
}
@Override
public void onLoop(AnimationDesc animation) {
// TODO Auto-generated method stub
}
});
Code required in the receiveModelRender(ModelBatch batch, Environment env)
function:
controller.update(Gdx.graphics.getDeltaTime());
batch.render(myInstance, env);