From 27fd225e90c775a57e0b118dcb83d4bc020a02cc Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Fri, 22 Sep 2023 03:35:11 +0300 Subject: [PATCH] Fixed looping animations --- src/modules/character/animation_system.cpp | 160 +++++++++---------- src/modules/character/animation_system.h | 171 +++++---------------- src/modules/character/character.cpp | 28 ++-- 3 files changed, 126 insertions(+), 233 deletions(-) diff --git a/src/modules/character/animation_system.cpp b/src/modules/character/animation_system.cpp index c128d38..e6be584 100644 --- a/src/modules/character/animation_system.cpp +++ b/src/modules/character/animation_system.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -31,6 +33,7 @@ static void _animation_process(flecs::entity_t entity, flecs::world &ecs, static void _stop_playing_caches(struct AnimationPlayerData *player); static void _set_process(struct AnimationPlayerData *player, bool p_process, bool p_force = false); +Map AnimationPlayerData::animation_set[2]; } //namespace ECS void ECS::play(struct ECS::AnimationPlayerData *player, const StringName &p_name, float p_custom_blend, float p_custom_scale, @@ -41,8 +44,9 @@ void ECS::play(struct ECS::AnimationPlayerData *player, if (String(name) == "") { name = player->playback.assigned; } + int type_id = player->type_id; - ERR_FAIL_COND_MSG(!player->animation_set.has(name), + ERR_FAIL_COND_MSG(!player->animation_set[type_id].has(name), "Animation not found: " + name + "."); Playback &c = player->playback; @@ -51,6 +55,7 @@ void ECS::play(struct ECS::AnimationPlayerData *player, float blend_time = 0; // find if it can blend ECS::BlendKey bk; + flecs::log::trace("%s: %p", __func__, c.current.from); bk.from = c.current.from->name; bk.to = name; @@ -88,7 +93,7 @@ void ECS::play(struct ECS::AnimationPlayerData *player, _stop_playing_caches(player); } - c.current.from = &player->animation_set[name]; + c.current.from = &player->animation_set[type_id][name]; flecs::log::trace("setting animation from %p", c.current.from); if (c.assigned != name) { // reset @@ -132,24 +137,24 @@ void ECS::play(struct ECS::AnimationPlayerData *player, #endif StringName next = animation_get_next(player, p_name); - if (next != StringName() && player->animation_set.has(next)) { + if (next != StringName() && player->animation_set[type_id].has(next)) { queue(player, next); } flecs::log::trace("%s(): %d animation %p", __func__, __LINE__, - c.current.from->get_animation().ptr()); + c.current.from->get_animation()); } static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player, flecs::entity_t entity, flecs::world &ecs, AnimationData *p_anim, Node *p_root_override) { // Already cached? - assert(p_anim->get_animation().is_valid()); + assert(p_anim->get_animation() != nullptr); if (p_anim->node_cache_size == p_anim->get_animation()->get_track_count()) { return; } - const Animation *a = p_anim->get_animation().operator->(); + const Animation *a = p_anim->get_animation(); p_anim->node_cache_size = a->get_track_count(); @@ -178,10 +183,22 @@ static void ECS::_ensure_node_caches(struct ECS::AnimationPlayerData *player, key.bone_idx = bone_id; String path = np.get_subname(0); - if (!player->node_cache_map.has(key)) - player->node_cache_map[key] = ECS::TrackNodeCache(); + AnimationCache *cache = e.get_mut(); - p_anim->node_cache[i] = &player->node_cache_map[key]; + if (!cache->node_cache_map.has(key)) { + int j, idx = -1; + for (j = 0; j < NODE_CACHE_UPDATE_MAX; j++) + if (!cache->node_cache_data[j].used) { + idx = j; + break; + } + assert(idx >= 0); + cache->node_cache_data[idx] = ECS::TrackNodeCache(); + cache->node_cache_data[idx].used = true; + cache->node_cache_map[key] = idx; + } + + p_anim->node_cache[i] = &cache->node_cache_data[cache->node_cache_map[key]]; p_anim->node_cache[i]->path = a->track_get_path(i); p_anim->node_cache[i]->resource = resource; if (a->track_get_type(i) == Animation::TYPE_TRANSFORM) { @@ -245,7 +262,7 @@ static void ECS::_animation_process_animation(flecs::entity_t entity, ERR_FAIL_COND(p_anim->node_cache_size != p_anim->get_animation()->get_track_count()); - const Animation *a = p_anim->get_animation().ptr(); + const Animation *a = p_anim->get_animation(); for (int i = 0; i < a->get_track_count(); i++) { // If an animation changes this animation (or it @@ -876,13 +893,11 @@ static void ECS::_animation_process_data(flecs::entity_t entity, { float delta = p_delta * player->speed_scale * cd.speed_scale; float next_pos = cd.pos + delta; - if (cd.from->get_animation().is_null()) { + if (cd.from->get_animation() == nullptr) { ERR_PRINT("bad animation"); return; } - assert(cd.from->get_animation().is_valid()); - flecs::log::trace("%s(): %d animation %p", __func__, __LINE__, - cd.from->get_animation().ptr()); + assert(cd.from->get_animation() != nullptr); float len = cd.from->get_animation()->get_length(); bool loop = cd.from->get_animation()->has_loop(); if (!loop) { @@ -890,26 +905,27 @@ static void ECS::_animation_process_data(flecs::entity_t entity, next_pos = 0; else if (next_pos > len) next_pos = len; - } - bool backwards = - signbit(delta); // Negative zero means playing backwards too - delta = next_pos - - cd.pos; // Fix delta (after determination of backwards - // because negative zero is lost here) - if (&cd == &player->playback.current) { - if (!backwards && cd.pos <= len && next_pos == len) { - //playback finished - player->end_reached = true; - player->end_notify = - cd.pos < len; // Notify only if not - // already at the end - } - if (backwards && cd.pos >= 0 && next_pos == 0) { - //playback finished - player->end_reached = true; - player->end_notify = - cd.pos > 0; // Notify only if not - // already at the beginning + bool backwards = + signbit(delta); // Negative zero means playing backwards too + delta = next_pos - + cd.pos; // Fix delta (after determination of backwards + // because negative zero is lost here) + if (&cd == &player->playback.current) { + if (!backwards && cd.pos <= len && next_pos == len) { + //playback finished + player->end_reached = true; + flecs::log::trace("AT END!!!"); + player->end_notify = + cd.pos < len; // Notify only if not + // already at the end + } + if (backwards && cd.pos >= 0 && next_pos <= 0) { + //playback finished + player->end_reached = true; + player->end_notify = + cd.pos > 0; // Notify only if not + // already at the beginning + } } } else { float looped_next_pos = Math::fposmod(next_pos, len); @@ -930,14 +946,12 @@ static void ECS::_animation_process2(flecs::entity_t entity, flecs::world &ecs, struct ECS::AnimationPlayerData *player, float p_delta, bool p_started) { ECS::Playback &c = player->playback; - player->accum_pass++; - if (c.current.from->get_animation().is_null()) { + if (c.current.from->get_animation() == nullptr) { ERR_PRINT("bad animation2"); return; } - flecs::log::trace("%s(): %d animation %p", __func__, __LINE__, - c.current.from->get_animation().ptr()); + player->accum_pass++; _animation_process_data(entity, ecs, player, c.current, p_delta, 1.0f, c.seeked && p_delta != 0, p_started); if (p_delta != 0) @@ -946,7 +960,7 @@ static void ECS::_animation_process2(flecs::entity_t entity, flecs::world &ecs, for (List::Element *E = c.blend.back(); E; E = prev) { Blend &b = E->get(); float blend = b.blend_left / b.blend_time; - if (b.data.from->get_animation().is_null()) { + if (b.data.from->get_animation() == nullptr) { ERR_PRINT("bad animation3"); b.blend_left -= Math::absf(player->speed_scale * p_delta); @@ -962,8 +976,6 @@ static void ECS::_animation_process2(flecs::entity_t entity, flecs::world &ecs, if (b.blend_left < 0) c.blend.erase(E); } - flecs::log::trace("%s(): %d animation %p", __func__, __LINE__, - c.current.from->get_animation().ptr()); } /* transforms are set here */ static void ECS::_animation_update_transforms(flecs::entity_t entity, @@ -977,15 +989,13 @@ static void ECS::_animation_update_transforms(flecs::entity_t entity, t.origin = nc->loc_accum; t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum); flecs::entity bone_entity = ecs.entity(nc->bone_idx); - if (bone_entity) { + if (bone_entity.is_alive()) { ECS::BonePose *bone = bone_entity.get_mut(); bone->pose = t; bone_entity.modified(); } } - flecs::log::trace("update bones %f %f %f\n", t.origin.x, - t.origin.y, t.origin.z); } player->cache_update_size = 0; #ifdef VALUE_TRACK_SUPPORT @@ -1046,14 +1056,7 @@ static void ECS::_set_process( static void ECS::_animation_process(flecs::entity_t entity, flecs::world &ecs, struct ECS::AnimationPlayerData *player, float p_time) { - flecs::log::trace("animation_pocess: %f", p_time); if (player->playback.current.from) { - flecs::log::trace("animation at start: %p %p", - player->playback.current.from, - player->playback.current.from->get_animation().ptr()); - flecs::log::trace("length: %f", - player->playback.current.from->get_animation() - ->get_length()); player->end_reached = false; player->end_notify = false; _animation_process2( @@ -1079,8 +1082,6 @@ static void ECS::_animation_process(flecs::entity_t entity, flecs::world &ecs, } player->end_reached = false; } - flecs::log::trace("animation at end: %p", - player->playback.current.from->get_animation().ptr()); } else _set_process(player, false); } @@ -1101,52 +1102,61 @@ void ECS::queue( StringName ECS::animation_get_next( struct ECS::AnimationPlayerData *player, const StringName &p_animation) { - if (!player->animation_set.has(p_animation)) { + int type_id = player->type_id; + if (!player->animation_set[type_id].has(p_animation)) { return StringName(); } - return player->animation_set[p_animation].next; + return player->animation_set[type_id][p_animation].next; } String ECS::get_current_animation(struct ECS::AnimationPlayerData *player) { return player->playing ? player->playback.assigned : ""; } -Error ECS::add_animation(struct ECS::AnimationPlayerData *player, - const StringName &p_name, const Ref &p_animation) +Error ECS::add_animation(flecs::entity_t entity, flecs::world &ecs, struct ECS::AnimationPlayerData *player, + const StringName &p_name, const Animation *p_animation) { - ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_animation == nullptr, ERR_INVALID_PARAMETER); + int type_id = player->type_id; - if (player->animation_set.has(p_name)) { + if (player->animation_set[type_id].has(p_name)) { AnimationData ad(p_animation); ad.name = p_name; - player->animation_set[p_name] = ad; + player->animation_set[type_id][p_name] = ad; // player->animation_set[p_name].animation = //p_animation; - clear_caches(player); + clear_caches(entity, ecs, player); } else { AnimationData ad(p_animation); ad.name = p_name; - player->animation_set[p_name] = ad; + player->animation_set[type_id][p_name] = ad; } return OK; } -void ECS::remove_animation( +void ECS::remove_animation(flecs::entity_t entity, flecs::world &ecs, struct ECS::AnimationPlayerData *player, const StringName &p_name) { - ERR_FAIL_COND(!player->animation_set.has(p_name)); + int type_id = player->type_id; + ERR_FAIL_COND(!player->animation_set[type_id].has(p_name)); stop(player); - player->animation_set.erase(p_name); + player->animation_set[type_id].erase(p_name); - clear_caches(player); + clear_caches(entity, ecs, player); } -void ECS::clear_caches(struct ECS::AnimationPlayerData *player) +void ECS::clear_caches(flecs::entity_t entity, flecs::world &ecs, struct ECS::AnimationPlayerData *player) { + int type_id = player->type_id, i; + flecs::entity e = ecs.entity(entity); + ECS::AnimationCache *cache = e.get_mut(); _stop_playing_caches(player); - player->node_cache_map.clear(); + cache->node_cache_map.clear(); + for (i = 0; i < NODE_CACHE_UPDATE_MAX; i++) + cache->node_cache_data[i].used = false; + for (Map::Element *E = - player->animation_set.front(); + player->animation_set[type_id].front(); E; E = E->next()) E->get().node_cache_size = 0; player->cache_update_size = 0; @@ -1154,23 +1164,13 @@ void ECS::clear_caches(struct ECS::AnimationPlayerData *player) player->cache_update_prop_size = 0; player->cache_update_bezier_size = 0; #endif + e.modified(); } void ECS::advance(flecs::entity_t entity, flecs::world &ecs, ECS::AnimationPlayerData *player, float p_time) { - flecs::log::trace( - "start current.from %p", player->playback.current.from); - flecs::log::trace("advance animation at start: %p %p", - player->playback.current.from, - player->playback.current.from->get_animation().ptr()); ECS::_animation_process(entity, ecs, player, p_time); - // player->playback.current.from = nullptr; - flecs::log::trace("advance animation at end: %p %p", - player->playback.current.from, - player->playback.current.from->get_animation().ptr()); - flecs::log::trace( - "end current.from %p", player->playback.current.from); } void ECS::stop(struct ECS::AnimationPlayerData *player, bool p_reset) { diff --git a/src/modules/character/animation_system.h b/src/modules/character/animation_system.h index e075037..0fab9e3 100644 --- a/src/modules/character/animation_system.h +++ b/src/modules/character/animation_system.h @@ -19,8 +19,9 @@ enum SpecialProperty { SP_NODE2D_SCALE, }; struct TrackNodeCache { + bool used; NodePath path; - uint32_t id; + flecs::entity_t id; Ref resource; #if 0 Node *node; @@ -38,6 +39,7 @@ struct TrackNodeCache { float audio_start; float audio_len; bool animation_playing; +#if 0 struct PropertyAnim { TrackNodeCache *owner; SpecialProperty special; @@ -55,6 +57,8 @@ struct TrackNodeCache { } }; Map property_anim; +#endif +#if 0 struct BezierAnim { Vector bezier_property; TrackNodeCache *owner; @@ -71,8 +75,9 @@ struct TrackNodeCache { } }; Map bezier_anim; +#endif TrackNodeCache() : - id(0), + used(false), id(-1), #if 0 node(nullptr), spatial(nullptr), @@ -188,13 +193,14 @@ struct AnimationData { StringName next; int node_cache_size; struct TrackNodeCache *node_cache[128]; - AnimationData(Ref anim) : + AnimationData(const Animation *anim) : animation(anim), node_cache_size(0) { - assert(animation.is_valid()); + assert(animation); + animation = anim; } AnimationData() : - animation(Ref(nullptr)), node_cache_size(0) + animation(nullptr), node_cache_size(0) { } AnimationData(const AnimationData &d) @@ -202,19 +208,19 @@ struct AnimationData { int i; name = d.name; next = d.next; + assert(d.animation); node_cache_size = d.node_cache_size; for (i = 0; i < 128; i++) node_cache[i] = d.node_cache[i]; animation = d.animation; - assert(animation.is_valid()); - flecs::log::trace("%s copy-constructed normally %p: %p", - __func__, this, animation.ptr()); + assert(animation); } AnimationData(AnimationData &&d) { int i; name = d.name; next = d.next; + assert(d.animation); node_cache_size = d.node_cache_size; for (i = 0; i < 128; i++) node_cache[i] = d.node_cache[i]; @@ -223,97 +229,32 @@ struct AnimationData { d.node_cache_size = 0; d.name = ""; d.next = ""; - d.animation = Ref(nullptr); - flecs::log::trace("%s move-constructed normally %p: %p", - __func__, this, animation.ptr()); + d.animation = nullptr; } AnimationData &operator=(const AnimationData &d) { memnew_placement(this, AnimationData(d)); return *this; } - const Ref &get_animation() const + const Animation *get_animation() const { - assert(animation.is_valid()); - flecs::log::trace("animation ptr %p", animation.ptr()); return animation; } - ~AnimationData() { animation = Ref(nullptr); } + ~AnimationData() { animation = nullptr; } private: - Ref animation; + const Animation *animation; }; struct PlaybackData { AnimationData *from; float pos; float speed_scale; - - PlaybackData() - { - pos = 0; - speed_scale = 1.0; - from = nullptr; - } - PlaybackData(const PlaybackData &d) - { - pos = d.pos; - speed_scale = d.speed_scale; - from = d.from; - flecs::log::trace( - "%s copy-constructed normally %p", __func__, this); - } - PlaybackData(PlaybackData &&d) - { - pos = d.pos; - speed_scale = d.speed_scale; - from = d.from; - d.pos = 0; - d.speed_scale = 1.0f; - d.from = nullptr; - flecs::log::trace( - "%s move-constructed normally %p", __func__, this); - } - PlaybackData &operator=(const PlaybackData &d) - { - from = d.from; - pos = d.pos; - speed_scale = d.speed_scale; - return *this; - } }; struct Blend { PlaybackData data; float blend_time; float blend_left; - Blend() - { - blend_left = 0.0f; - blend_time = 0.0f; - } - Blend(const Blend &d) - { - data = PlaybackData(d.data); - blend_time = d.blend_time; - blend_left = d.blend_left; - flecs::log::trace( - "%s copy-constructed normally %p", __func__, this); - } - Blend(Blend &&d) - { - data = std::move(d.data); - blend_time = d.blend_time; - blend_left = d.blend_left; - d.blend_left = 0.0f; - d.blend_time = 0.0f; - flecs::log::trace( - "%s move-constructed normally %p", __func__, this); - } - Blend &operator=(const Blend &d) - { - memnew_placement(this, Blend(d)); - return *this; - } }; struct Playback { List blend; @@ -326,39 +267,6 @@ struct Playback { blend.clear(); seeked = false; started = false; - flecs::log::trace( - "%s constructed normally %p", __func__, this); - } - Playback(const Playback &d) - { - blend = List(); - const List::Element *e = d.blend.front(); - /* copy construct blends */ - while (e) { - blend.push_back(Blend(e->get())); - e = e->next(); - } - current = PlaybackData(d.current); - StringName assigned = d.assigned; - seeked = d.seeked; - started = d.started; - flecs::log::trace( - "%s copy-constructed normally %p", __func__, this); - } - Playback(Playback &&d) - { - blend = std::move(d.blend); - current = std::move(d.current); - StringName assigned = d.assigned; - seeked = d.seeked; - started = d.started; - flecs::log::trace( - "%s move-constructed normally %p", __func__, this); - } - Playback &operator=(const Playback &d) - { - memnew_placement(this, Playback(d)); - return *this; } }; struct BlendKey { @@ -385,8 +293,15 @@ struct TrackNodeCacheKey { } }; enum { NODE_CACHE_UPDATE_MAX = 1024, BLEND_FROM_MAX = 3 }; + +struct AnimationCache { + Map node_cache_map; + TrackNodeCache node_cache_data[NODE_CACHE_UPDATE_MAX]; +}; + struct AnimationPlayerData { - Map node_cache_map; + int type_id; + TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX]; int cache_update_size; @@ -400,7 +315,7 @@ struct AnimationPlayerData { uint64_t accum_pass; float speed_scale; float default_blend_time; - Map animation_set; + static Map animation_set[2]; Map blend_times; Playback playback; @@ -416,18 +331,18 @@ struct AnimationPlayerData { NodePath root; #endif bool playing; - AnimationPlayerData() + AnimationPlayerData(int id) { + type_id = id; processing = false; playing = false; active = false; - node_cache_map.clear(); cache_update_size = 0; playing_caches.clear(); accum_pass = 0; speed_scale = 1.0f; default_blend_time = 0; - animation_set.clear(); + animation_set[id].clear(); blend_times.clear(); queued.clear(); end_reached = false; @@ -438,13 +353,11 @@ struct AnimationPlayerData { AnimationProcessMode::ANIMATION_PROCESS_MANUAL; processing = false; active = false; - flecs::log::trace( - "%s constructed normally %p", __func__, this); } AnimationPlayerData(const AnimationPlayerData &d) { int i; - node_cache_map = d.node_cache_map; + type_id = d.type_id; for (i = 0; i < NODE_CACHE_UPDATE_MAX; i++) cache_update[i] = d.cache_update[i]; cache_update_size = d.cache_update_size; @@ -452,7 +365,6 @@ struct AnimationPlayerData { accum_pass = d.accum_pass; speed_scale = d.speed_scale; default_blend_time = d.default_blend_time; - animation_set = d.animation_set; blend_times = d.blend_times; playback = d.playback; queued = d.queued; @@ -464,8 +376,6 @@ struct AnimationPlayerData { processing = d.processing; active = d.active; playing = d.playing; - flecs::log::trace( - "%s copy-constructed normally %p", __func__, this); } AnimationPlayerData(AnimationPlayerData &&d) { @@ -496,8 +406,6 @@ struct AnimationPlayerData { d.playing = playing; d.cache_update_size = 0; d.active = false; - flecs::log::trace( - "%s move-constructed normally %p", __func__, this); } AnimationPlayerData &operator=(const AnimationPlayerData &d) { @@ -525,15 +433,12 @@ struct AnimationPlayerData { active = d.active; playing = d.playing; #endif - flecs::log::trace( - "%s copy-assigned normally %p", __func__, this); - return *this; } AnimationPlayerData &operator=(AnimationPlayerData &&d) { int i; - node_cache_map = d.node_cache_map; + type_id = d.type_id; for (i = 0; i < NODE_CACHE_UPDATE_MAX; i++) cache_update[i] = d.cache_update[i]; cache_update_size = d.cache_update_size; @@ -541,7 +446,6 @@ struct AnimationPlayerData { accum_pass = d.accum_pass; speed_scale = d.speed_scale; default_blend_time = d.default_blend_time; - animation_set = d.animation_set; blend_times = d.blend_times; playback = d.playback; queued = d.queued; @@ -556,9 +460,6 @@ struct AnimationPlayerData { d.playing = playing; d.cache_update_size = 0; d.active = false; - flecs::log::trace( - "%s move-assigned normally %p", __func__, this); - return *this; } ~AnimationPlayerData() { playing = false; } @@ -566,9 +467,9 @@ struct AnimationPlayerData { /* public functions */ StringName find_animation( struct AnimationPlayerData *player, const Ref &p_animation); -Error add_animation(struct AnimationPlayerData *player, - const StringName &p_name, const Ref &p_animation); -void remove_animation( +Error add_animation(flecs::entity_t entity, flecs::world &ecs, struct AnimationPlayerData *player, + const StringName &p_name, const Animation *p_animation); +void remove_animation(flecs::entity_t entity, flecs::world &ecs, struct AnimationPlayerData *player, const StringName &p_name); void rename_animation(struct AnimationPlayerData *player, const StringName &p_name, const StringName &p_new_name); @@ -636,7 +537,7 @@ void advance(flecs::entity_t entity, flecs::world &ecs, AnimationPlayerData *player, float p_time); void set_root(struct AnimationPlayerData *player, const NodePath &p_root); NodePath get_root(struct AnimationPlayerData *player); -void clear_caches(struct AnimationPlayerData *player); +void clear_caches(flecs::entity_t entity, flecs::world &ecs, struct AnimationPlayerData *player); void get_argument_options(struct AnimationPlayerData *player, const StringName &p_function, int p_idx, List *r_options); } //namespace ECS diff --git a/src/modules/character/character.cpp b/src/modules/character/character.cpp index b7f74f5..0502363 100644 --- a/src/modules/character/character.cpp +++ b/src/modules/character/character.cpp @@ -88,15 +88,17 @@ int Character::create_character(int id) root.emplace(); root.emplace(body, ECS::RigidBody::TYPE_KINEMATIC); root.emplace(); - root.emplace(); + root.emplace(); + root.emplace(id); List anim_list; scene_data[id].animations.get_key_list(&anim_list); List::Element *e = anim_list.front(); ECS::AnimationPlayerData *player = root.get_mut(); while (e) { - ECS::add_animation( - player, e->get(), scene_data[id].animations[e->get()]); + assert(scene_data[id].animations[e->get()].is_valid()); + ECS::add_animation(root, ecs, + player, e->get(), scene_data[id].animations[e->get()].ptr()); flecs::log::trace("added animation %s", String(e->get()).ascii().get_data()); e = e->next(); @@ -360,27 +362,17 @@ void Character::animation_system_init() ecs.system("UpdateAnimation") .kind(flecs::OnUpdate) .each([](flecs::entity e, ECS::AnimationPlayerData &player) { - flecs::log::trace("== start of animation frame %p", - player.playback.current.from); - if (player.playback.current.from) - flecs::log::trace("start animation %p", - player.playback.current.from - ->get_animation() - .ptr()); - if (!player.playing) - flecs::log::trace("not playing any animation"); if (!player.playing) ECS::play(&player, "male-mx-walk-loop"); + if (player.playing && player.end_reached) { + ECS::stop(&player, true); + if (!player.playing) + flecs::log::trace("stopped: not playing any animation"); + } flecs::world w = e.world(); ECS::advance(e.id(), w, &player, SceneTree::get_singleton() ->get_physics_process_time()); - if (player.playback.current.from) - flecs::log::trace("end animation %p", - player.playback.current.from - ->get_animation() - .ptr()); - flecs::log::trace("== end of animation frame"); }); } void Character::initialize()