IAnimatable Interface - coldrockgames/gml-raptor GitHub Wiki
By adding this line after event_inherited() in the create event of your object, you can make it "Animatable".
implement(IAnimatable);
Animation Automation
This powerful interface allows you to define animations that shall happen, when the object enters a specific state. Automatically.
The AnimationEntry Class
This system relies on a series of AnimationEntry instances in the animations member of IAnimatable.
You may add these instances through GML-Code (new AnimationEntry(...)) or through RichJson. Examples follow shortly.
/// @func AnimationEntry(_name = undefined, _ianimatable = undefined)
/// @desc Create a new animation definition, optionally directly attached
/// to a specified IAnimatable instance with a specified name
function AnimationEntry(_name = undefined, _ianimatable = undefined) constructor {
construct(AnimationEntry);
if (_ianimatable != undefined && _name != undefined)
struct_set(_ianimatable.animations, _name, self);
auto_play = false; // will start automatically when the named state is entered
auto_stop = true; // will stop automatically when the named state is left
exclusive = false; // If true, will call animation_finish_all(self) before starting
state = true; // If true, will call add_event_state(name, state_after)
state_after = undefined; // return_to_state after event animation (if state==true)
delay = 0; // animation arguments
frames = 0; // animation arguments
repeats = 1; // animation arguments
curve = undefined; // animation arguments
before_start = undefined; // scriptor script (richjson) or callback (gml) to be launched
on_started = undefined; // scriptor script (richjson) or callback (gml) to be launched
on_finished = undefined; // scriptor script (richjson) or callback (gml) to be launched
}
You may define as many named animations as you like.
[!TIP] If the name of the animation matches the name of a state, and if it is set to
auto_start, then the StateMachine will start this animation every time the object enters the state.
Just set your behavior (auto_start, auto_stop, exclusive) and define your animation (delay, frames and curve). If you need to add code, use the callback hooks.
Let's examine those hooks a little closer:
- They can be either functions in GML (simply set them directly in the AnimationEntry instance)
- They can be
#script:commands of RichJson and point to Scriptor scripts.
The timing of the callbacks is as follows:
before_startwill be invoked after the animation has been created. In your object now is a memberevent_animset to point to this animation. You may useevent_animin the function or in the scriptor script.on_startedwill be invoked with the first rendering frame of the animation (it is implemented as.add_starting_triggeron the StateMachine)on_finishedis a.add_finished_triggeron the StateMachine and will be invoked when the animation ends. Theevent_animmember is still set.
IAnimatable Functions and Members
This interface adds some functions to your object as well as some member variables.
Member Variables
| Variable | Type | Description |
|---|---|---|
animations |
struct |
All your state animations are stored in this struct |
event_anim |
Animation |
The currently active state animation or undefined, if no animation is running |
event_anim_data |
struct |
Add here any information you would like to have accessible in the script/callback. The receiving function may access this under the same name, event_anim_data. |
Functions
/// @func add_event_state(_name)
/// @desc Add a pure event state to the state machine.
/// This is an empty state that will do nothing but
/// "return prev_state".
/// It's just there to launch auto_start event animations.
add_event_state = function(_name) {
/// @func add_event_animation(_name, _animation_entry)
/// @desc Add a new event animation.
/// If an animation with that name already exists, it is overwritten.
add_event_animation = function(_name, _animation_entry) {
/// @func delete_event_animation(_name)
/// @desc Removes an animation from the event animations.
/// Returns bool, whether an animation with that name existed.
delete_event_animation = function(_name) {
/// @func get_event_animation(_name)
/// @desc Returns the event animation with the specified name
/// or undefined, if it does not exist
get_event_animation = function(_name) {
/// @func is_event_animation_auto_play(_name)
is_event_animation_auto_play = function(_name) {
/// @func is_event_animation_auto_stop(_name)
is_event_animation_auto_stop = function(_name) {
/// @func run_event_animation(_anim_name)
/// @desc Starts the event animation.
/// Normally you do not need to calls this by yourself
/// (if the animation is set to auto_start)
/// but you may use it any time to launch a specific
/// animation that is registered as event animation.
run_event_animation = function(_anim_name) {
/// @func update_event_animation(_state, _prev_state)
/// @desc Update the state of animation based on a state
/// change in the state machine
/// NOTE: Normally you do not have to call this by yourself!
/// The deep integration of state machine and animation handles
/// this for you.
/// You MAY of course call this manually when you need it.
update_event_animation = function(_state, _prev_state) {
Example: Default Idle Animation
A definition like this will make your Monster run an idle animation every time it enters the "idle" state.
"Monster::skin/Monster.Elite": {
// ... some other stats and definitions ...
"animations": {
"idle::#macro:animation": {
"auto_play": true,
"frames": 60,
"curve": "#asset:acDefaultIdleAnim",
"before_start": "#script:game/dungeon/monster/default_animations@idle_starting",
},
},
},
This will launch the "idle" animation every time the Monster enters the "idle" state.
Short and sweet. And the best thing about this is:
- It works for all Monsters even those that haven't been created in your project yet.
- You do not need to write a single line of code. Just make your Monsters children of the "Monster" base object.
- You do only need to "implement" the interface once: In your base object. All children will use it.