This document describes the internals of how the combat and animation systems function. Kandria uses a combination of information provided natively by Aseprite, as well as an additional data file to describe extra per-animation and per-frame properties that the combat system needs to function.
For this reason, animation data is split across the following files:
Aseprite source file. This is only useful during development.
Aseprite exported JSON data. When exporting the animation from Aseprite, this file will be generated automatically. See exporting from aseprite
Aseprite exported sprite atlas. This is the packed atlas and contains all frames in a tight and efficient single image.
External animation data. This file is generated by the in-game animation editor and contains extra data relevant for combat. See combat data format.
For the most part you will be working within Aseprite and Kandria's editor. You should not have to modify the files manually often.
Any character in the game can be in several internal states, though for the purpose of this system, we'll focus on "animated" and "normal" states. In the normal state, animation is controlled tightly by the game and changes in actions and animations can occur at any point. Animations that are used in the normal state don't need any combat data, only basic animation data.
Every animation regardless of state has two properties:
When the animation reaches its end, it will loop back to this frame index. The index is zero-based. If no loop-to index is set, it instead uses
This designates which animation should be started if the current animation reaches its end and does not have a loop.
For animations that are used in the animated state, the movement of the character is no longer in the player's hands but is instead governed by per-frame motion data. The per-frame data contains the following attributes:
The added velocity of the character at this frame.
The velocity multiplier at this frame. This is useful to gradually slow down or accelerate existing character velocity. Typically this is set to zero to make the
velocity parameter be the effective velocity of the character.
The box relative to the character's position that designates where the attack would hit if colliding with another character.
The velocity that's added to a character at this frame if that character collides with the hurtbox.
The amount of damage that's dealt to the character if the character collides with the hurtbox.
The amount of time in seconds for which the other character is stunned for when colliding with the hurtbox.
When set the animation can be interrupted by a collision with a hurtbox.
When set the character cannot be damaged by a collision with a hurtbox.
When set the player can cancel the animation and instead switch to a different animation or state.
The name of an effect to trigger on this frame. Effects can cause sounds or particle effects.
The animated state is used for attacks, getting hit, and dying.
In the player's case, animations can be buffered, meaning that the player can hit the button to initiate another animation while the current animation is playing. The buffered animation will play as soon as the current animation becomes cancelable or ends.
For general ideas about how the combat system is supposed to behave for the player, see the design document's combat section.
x.lisp file that contains the animation data is formatted using standard Lisp syntax. It is structured using property lists:
The name as used by the game. This must correspond with the name of a tag in Aseprite and with the name used in the internal engine. Please see the animation sheet for the correct names.
These properties mostly correspond 1:1 with the above descriptions. Vectors are denoted by lists of numbers.
The flags are expressed as a bit array in the (left to right) order of cancelable, invincible, interruptable.
The effect must correspond to the name of an effect as known by the game. Please see the "
Typically you will not want to edit this file manually and instead use the animation editor.
When exporting from Aseprite to update animations, please choose "Export Sprite Sheet" with the following options:
Sheet Type: Packed
Border padding: 0
Inner Padding: 0
Trim Cels: checked
Output File: checked
JSON Data: checked
You can also do this for most files in an automated fashion by just running the following from the Portacle REPL:
(kandria::compile-resources T T)