Animation Overview: Difference between revisions
Created page with "Animations are one of OpenBOR's fundamental building blocks. A model uses animations to describe how it looks and behaves over time. Walking, standing, attacking, falling, taking damage, and many other actions are represented by ordered animation frames. Each animation belongs to a model and occupies a recognized animation slot such as <code>idle</code>, <code>walk</code>, <code>jump</code>, <code>pain</code>, or <code>attack1</code>. OpenBOR's native logic selects many..." |
No edit summary |
||
| (2 intermediate revisions by the same user not shown) | |||
| Line 3: | Line 3: | ||
Each animation belongs to a model and occupies a recognized animation slot such as <code>idle</code>, <code>walk</code>, <code>jump</code>, <code>pain</code>, or <code>attack1</code>. OpenBOR's native logic selects many of these slots automatically. Scripts may also select an animation or frame directly. | Each animation belongs to a model and occupies a recognized animation slot such as <code>idle</code>, <code>walk</code>, <code>jump</code>, <code>pain</code>, or <code>attack1</code>. OpenBOR's native logic selects many of these slots automatically. Scripts may also select an animation or frame directly. | ||
An animation frame is more than an image. It may also contain timing, offsets, collision boxes, movement, sounds, draw settings, entity spawns, and other behavior. | An animation frame is more than an image. It may also contain timing, offsets, collision boxes, movement, sounds, draw settings, entity spawns, and other behavior.__TOC__ | ||
__TOC__ | |||
== Basic structure == | == Basic structure == | ||
| Line 85: | Line 83: | ||
Define important state explicitly in each animation. In particular, specifying a delay inside every animation avoids accidentally inheriting a previously active delay while the model file is being parsed. | Define important state explicitly in each animation. In particular, specifying a delay inside every animation avoids accidentally inheriting a previously active delay while the model file is being parsed. | ||
=== Frame numbering === | === Frame numbering === | ||
Frames use zero-based indexes. The first frame line is frame 0, the second is frame 1, and so on. Commands that target a frame number use these indexes unless their own documentation states otherwise. | |||
The same image may appear more than once when different timing or behavior is needed. OpenBOR reuses its globally cached pixel data, so repeating an image does not require another pixel-data allocation. | |||
Frame <code>none</code> adds a logical frame without a visible sprite, which can be useful for invisible states or script-controlled behavior. | |||
== Offsets == | == Offsets == | ||
| Line 101: | Line 99: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
OpenBOR places the selected point at the entity's world position. For a standing character, the anchor is usually near the ground between the character's feet | OpenBOR places the selected point at the entity's world position. For a standing character, the anchor is usually near the ground between the character's feet. | ||
Choose a consistent animation canvas size during production and align every source frame to the same anchor before export. Guides for the ground line and horizontal center make this much faster than correcting alignment one frame at a time in the model file. | |||
OpenBOR automatically trims unused image space when it loads sprites. Consistent source canvases are still useful because the trimming process preserves the intended relationship between the artwork and its anchor without retaining transparent padding in memory. | |||
offset | Poor source alignment makes an entity appear to shake, slide, or change height when frames cycle. Per-frame offset changes remain available for deliberate repositioning or assets that cannot be realigned, but they should generally be the exception rather than the primary production workflow. | ||
== Looping == | == Looping == | ||
Animations do not loop unless looping is enabled.<syntaxhighlight lang="text"> | |||
loop {enable} {loop frame - optional} {end frame - optional} | |||
# Default | |||
loop 0 0 0 | |||
</syntaxhighlight>loop <code>1</code> repeats the animation. loop <code>0</code> disables looping. | |||
The optional loop frame selects the zero-based frame to return to when the animation repeats. The optional end frame is an exclusive boundary - when playback reaches that frame index, it returns to the selected loop frame instead of displaying the boundary frame. | |||
loop | |||
<code> | For example, if end frame is <code>7</code>, frame <code>7</code> - the sixth frame, does not display - the animation instantly loops on reaching that frame. If end frame is not provided animation proceeds to the last frame and loops. | ||
Idle and walk animations commonly loop. Attacks, pain reactions, falls, and transitions commonly play once and stop on their final frame until native logic or a script selects another animation. | Idle and walk animations commonly loop. Attacks, pain reactions, falls, and transitions commonly play once and stop on their final frame until native logic or a script selects another animation. | ||
| Line 147: | Line 141: | ||
=== Delay units === | === Delay units === | ||
Optional time unit setting. | |||
{| class="wikitable" | {| class="wikitable" | ||
! Unit | ! Unit | ||
| Line 170: | Line 164: | ||
| Stores the value directly as logical clock ticks without time-unit conversion. | | Stores the value directly as logical clock ticks without time-unit conversion. | ||
|} | |} | ||
OpenBOR converts the supplied value from the selected unit into logical clock ticks. See [[Animation Overview#Logical clock precision|Logical clock precision]]. | |||
Examples: | Examples: | ||
| Line 209: | Line 205: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Fractional results truncate. No time unit can represent a duration smaller than the module's logical clock precision | Fractional results truncate. No time unit can represent a duration smaller than the module's logical clock precision, and any fractional remainder is discarded. | ||
For example, at the default game speed of 200 ticks per second, each logical tick represents 5 milliseconds. Any value from 15 through 19 milliseconds therefore converts to three ticks, representing 15 milliseconds, while 20 milliseconds converts exactly to four ticks. | |||
The <code>direct</code> unit is useful when exact integration with the logical clock matters. Its real-time duration changes with game speed because its value is already a tick count. | The <code>direct</code> unit is useful when exact integration with the logical clock matters. Its real-time duration changes with game speed because its value is already a tick count. By pairing a game speed evenly divisible by a target frame rate with direct delays, creators can time animations in exact frame intervals. For example, a game speed of 240 provides four logical ticks for each frame at 60 Hz. This supports frame-based timing conventions commonly used by the fighting game community and fixed-refresh console games. | ||
=== Infinite delay === | === Infinite delay === | ||
| Line 246: | Line 240: | ||
=== Enhanced delay === | === Enhanced delay === | ||
Enhanced delay, or <code>edelay</code>, adjusts frame delays through a model's multiplier, modifier, range, and cap settings. These calculations occur after time-unit conversion, so enhanced-delay ranges, modifiers, and caps operate on logical tick values rather than the source unit written in the animation. | [[Enhanced Delay|Enhanced delay]], or <code>edelay</code>, adjusts frame delays through a model's multiplier, modifier, range, and cap settings. These calculations occur after time-unit conversion, so enhanced-delay ranges, modifiers, and caps operate on logical tick values rather than the source unit written in the animation. | ||
Enhanced-delay arithmetic is bounded to the finite delay range. It cannot overflow into behavior flags or create an infinite delay through calculation. | Enhanced-delay arithmetic is bounded to the finite delay range. It cannot overflow into behavior flags or create an infinite delay through calculation. | ||
| Line 358: | Line 352: | ||
When debugging an animation, begin with a minimal definition containing <code>anim</code>, <code>delay</code>, <code>offset</code>, and <code>frame</code>. Confirm its images, alignment, and timing before adding collision, movement, effects, or scripts. | When debugging an animation, begin with a minimal definition containing <code>anim</code>, <code>delay</code>, <code>offset</code>, and <code>frame</code>. Confirm its images, alignment, and timing before adding collision, movement, effects, or scripts. | ||
[[Category:Openbor]] | |||
[[Category:Animation]] | |||
[[Category:Model]] | |||
Latest revision as of 00:17, 18 August 2026
Animations are one of OpenBOR's fundamental building blocks. A model uses animations to describe how it looks and behaves over time. Walking, standing, attacking, falling, taking damage, and many other actions are represented by ordered animation frames.
Each animation belongs to a model and occupies a recognized animation slot such as idle, walk, jump, pain, or attack1. OpenBOR's native logic selects many of these slots automatically. Scripts may also select an animation or frame directly.
An animation frame is more than an image. It may also contain timing, offsets, collision boxes, movement, sounds, draw settings, entity spawns, and other behavior.
Basic structure
The anim command begins an animation definition. Commands following it configure the animation or prepare properties for its frames. Each frame command adds one frame using the properties currently in effect.
anim idle
loop 1
delay 12
offset 32 96
frame data/chars/example/idle_01.png
frame data/chars/example/idle_02.png
frame data/chars/example/idle_03.png
This example creates a looping idle animation with three frames. With the default centisecond delay unit, each frame lasts 0.12 seconds.
Indentation and blank lines are optional, but they make animation blocks easier to read.
Animation identifiers
The value following anim identifies the animation's purpose. Common examples include:
| Identifier | Typical purpose |
|---|---|
idle
|
Standing without moving. |
walk
|
Ordinary movement. |
jump
|
Initial or rising jump state. |
land
|
Landing from a jump or fall. |
pain
|
Reacting to damage. |
fall
|
Knockdown or falling reaction. |
rise
|
Recovering from a fall. |
attack1
|
A native attack animation. |
freespecial1
|
A creator-configured special move. |
Animation identifiers are behavioral slots, not arbitrary labels. Supplying an animation makes the corresponding behavior available to native engine logic and scripts. Some model types require certain animations to operate normally, while others are optional.
Frame construction
OpenBOR reads model files from top to bottom. Frame property commands update temporary state, and frame commits a copy of that state to the animation.
anim idle
loop 1
offset 32 96
delay 10
frame data/chars/example/idle_01.png
delay 25
frame data/chars/example/idle_02.png
The first frame receives a delay of 10. The second receives a delay of 25 because the delay changes before its frame command.
Commands placed after a frame line cannot alter the frame that was already added. Most frame properties remain active for subsequent frames until another command changes or disables them. Some one-shot properties, such as a frame sound, reset after the frame is added.
Define important state explicitly in each animation. In particular, specifying a delay inside every animation avoids accidentally inheriting a previously active delay while the model file is being parsed.
Frame numbering
Frames use zero-based indexes. The first frame line is frame 0, the second is frame 1, and so on. Commands that target a frame number use these indexes unless their own documentation states otherwise.
The same image may appear more than once when different timing or behavior is needed. OpenBOR reuses its globally cached pixel data, so repeating an image does not require another pixel-data allocation.
Frame none adds a logical frame without a visible sprite, which can be useful for invisible states or script-controlled behavior.
Offsets
The offset command selects the anchor point inside a frame image.
offset 32 96
frame data/chars/example/idle_01.png
OpenBOR places the selected point at the entity's world position. For a standing character, the anchor is usually near the ground between the character's feet.
Choose a consistent animation canvas size during production and align every source frame to the same anchor before export. Guides for the ground line and horizontal center make this much faster than correcting alignment one frame at a time in the model file.
OpenBOR automatically trims unused image space when it loads sprites. Consistent source canvases are still useful because the trimming process preserves the intended relationship between the artwork and its anchor without retaining transparent padding in memory.
Poor source alignment makes an entity appear to shake, slide, or change height when frames cycle. Per-frame offset changes remain available for deliberate repositioning or assets that cannot be realigned, but they should generally be the exception rather than the primary production workflow.
Looping
Animations do not loop unless looping is enabled.
loop {enable} {loop frame - optional} {end frame - optional}
# Default
loop 0 0 0
loop 1 repeats the animation. loop 0 disables looping.
The optional loop frame selects the zero-based frame to return to when the animation repeats. The optional end frame is an exclusive boundary - when playback reaches that frame index, it returns to the selected loop frame instead of displaying the boundary frame.
For example, if end frame is 7, frame 7 - the sixth frame, does not display - the animation instantly loops on reaching that frame. If end frame is not provided animation proceeds to the last frame and loops.
Idle and walk animations commonly loop. Attacks, pain reactions, falls, and transitions commonly play once and stop on their final frame until native logic or a script selects another animation.
Delay
Delay controls how long a frame remains active before OpenBOR advances the animation. The command is stateful and applies to each following frame until another delay command changes it.
Syntax
delay <value> [unit]
The unit is optional. Omitting it is equivalent to selecting global.
delay 10
delay 10 global
Both examples use the module's global delay unit.
Delay units
Optional time unit setting.
| Unit | Meaning |
|---|---|
global
|
Uses the unit selected by global_config_delay_unit in data/models.txt. This is the default mode.
|
millisecond
|
Interprets the value as thousandths of a second. |
centisecond
|
Interprets the value as hundredths of a second. This is the default global unit and preserves traditional OpenBOR timing. |
second
|
Interprets the value as seconds. |
minute
|
Interprets the value as minutes. |
direct
|
Stores the value directly as logical clock ticks without time-unit conversion. |
OpenBOR converts the supplied value from the selected unit into logical clock ticks. See Logical clock precision.
Examples:
delay 250 millisecond
delay 25 centisecond
delay 2 second
delay 1 minute
delay 50 direct
Singular unit names are required.
Global delay unit
Use global_config_delay_unit in data/models.txt to select the unit used by unqualified and explicitly global delays.
global_config_delay_unit centisecond
The setting accepts millisecond, centisecond, second, minute, or direct. It does not accept global, because the global setting must resolve to a concrete unit.
If the command is omitted, the global unit is centisecond. Changing the setting reinterprets every model delay that uses global, so modules should choose a unit deliberately. Explicit frame units remain independent of the global selection.
The setting is read while models are loaded. OpenBOR converts each delay to logical ticks and stores the converted result with the animation frame. It is not a runtime timing switch for animations that are already loaded.
Logical clock precision
OpenBOR animation timing ultimately uses whole logical clock ticks. Unit conversion follows these relationships:
millisecond = value * game speed / 1000
centisecond = value * game speed / 100
second = value * game speed
minute = value * game speed * 60
direct = value
Fractional results truncate. No time unit can represent a duration smaller than the module's logical clock precision, and any fractional remainder is discarded.
For example, at the default game speed of 200 ticks per second, each logical tick represents 5 milliseconds. Any value from 15 through 19 milliseconds therefore converts to three ticks, representing 15 milliseconds, while 20 milliseconds converts exactly to four ticks.
The direct unit is useful when exact integration with the logical clock matters. Its real-time duration changes with game speed because its value is already a tick count. By pairing a game speed evenly divisible by a target frame rate with direct delays, creators can time animations in exact frame intervals. For example, a game speed of 240 provides four logical ticks for each frame at 60 Hz. This supports frame-based timing conventions commonly used by the fighting game community and fixed-refresh console games.
Infinite delay
Use infinite or the infinity symbol ∞ when a frame should not advance through ordinary clock timing.
anim idle
delay infinite
offset 32 96
frame data/chars/example/idle_01.png
delay ∞
An infinite frame remains active until something outside normal timing changes its animation or frame. Scripts may use the DELAY_INFINITE constant for the same engine value.
The word infinite is the most portable model-file spelling. The ∞ form is UTF-8 text and may be damaged by old Windows editors, archive tools, or other utilities that do not preserve Unicode correctly.
Any negative numeric delay also becomes infinite for backward compatibility. New content should use infinite or ∞ so its intent is explicit.
Finite range and behavior flags
Finite delay values use the lower 32 bits of an unsigned 64-bit value. The largest finite value is 4,294,967,295. Conversions that would exceed this limit clamp to the largest finite delay instead of overflowing or accidentally becoming infinite.
The upper 32 bits are reserved for delay behavior flags. Bit 63 is the infinite flag. Enhanced-delay calculations modify only the finite lower value and preserve behavior flags, so an infinite frame cannot accidentally become finite.
Enhanced delay
Enhanced delay, or edelay, adjusts frame delays through a model's multiplier, modifier, range, and cap settings. These calculations occur after time-unit conversion, so enhanced-delay ranges, modifiers, and caps operate on logical tick values rather than the source unit written in the animation.
Enhanced-delay arithmetic is bounded to the finite delay range. It cannot overflow into behavior flags or create an infinite delay through calculation.
Frame properties
Timing and graphics form only the minimum animation. Frame property commands can add behavior such as:
| Property | Purpose |
|---|---|
bbox
|
Defines the frame's body collision area. Use bbox none to disable it.
|
| Attack collision commands | Define areas that damage or otherwise affect targets. |
move, movea, and movez
|
Apply frame movement along the model's axes. |
sound
|
Plays a sound when the frame begins. |
| Draw commands | Change scaling, rotation, transparency, remapping, clipping, and other rendering behavior. |
| Spawn commands | Create or summon child entities on selected frames. |
| Animation scripts | Execute creator-defined logic as frames change. |
Property commands normally appear before the frame they configure:
anim idle
loop 1
delay 10
offset 32 96
bbox 12 20 40 76 10
frame data/chars/example/idle_01.png
bbox none
frame data/chars/example/idle_02.png
The first frame has a body collision box. The second does not because bbox none changes the pending frame state before the second frame command.
Practical example
anim idle
loop 1
offset 32 96
bbox 12 20 40 76 10
delay 100 millisecond
frame data/chars/example/idle_01.png
delay 150 millisecond
frame data/chars/example/idle_02.png
delay 100 millisecond
frame data/chars/example/idle_03.png
anim attack1
loop 0
offset 40 96
delay 80 millisecond
frame data/chars/example/attack_01.png
move 4
frame data/chars/example/attack_02.png
move 0
delay 160 millisecond
frame data/chars/example/attack_03.png
The idle animation loops with intentionally uneven timing. The attack plays once, moves the entity during its second frame, stops that movement before the third frame, and holds the final attack pose longer.
Common problems
| Symptom | Likely cause |
|---|---|
| Frames appear to shake or slide. | Offsets do not align the same visual anchor across images. |
| A property affects the wrong frame. | The property command appears after the intended frame line, or persistent frame state was not changed before the next frame.
|
| An animation plays much too quickly or slowly. | The delay unit or global_config_delay_unit does not match the values used by the model.
|
| Very small delays appear identical. | Both values truncate to the same logical tick count at the selected game speed. |
| An animation never advances. | Its delay is infinite, its animation has stopped, or external logic is holding or replacing its state. |
| A non-looping animation does not repeat. | This is expected unless native logic or a script selects the animation again. |
When debugging an animation, begin with a minimal definition containing anim, delay, offset, and frame. Confirm its images, alignment, and timing before adding collision, movement, effects, or scripts.