From 1baca783fcee61948f43f6ded1309844a6d20d9e Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 21 Feb 2026 01:57:33 -0500 Subject: [PATCH] Sorted out the animation vs. easing contradiction I believe. Now there's a separate easing_duration that's calculated from the total duration of all frames. Then a easing_position determines where in the total the animation is, which is fed to the asing functions as a ration of easing_position / easing_duration. --- animate2.cpp | 45 ++++++++++++++++++++++---------------------- animate2.hpp | 11 ++++++----- assets/animate2.json | 19 +++++++------------ assets/cameras.json | 14 +++++--------- camera.cpp | 2 -- scene.cpp | 1 - tests/animate2.cpp | 2 +- tools/animator.cpp | 25 ++++-------------------- tools/animator.hpp | 3 +-- 9 files changed, 47 insertions(+), 75 deletions(-) diff --git a/animate2.cpp b/animate2.cpp index 11416f7..a3a536e 100644 --- a/animate2.cpp +++ b/animate2.cpp @@ -10,6 +10,8 @@ constexpr float SUB_FRAME_SENSITIVITY = 0.999f; namespace animate2 { + using namespace std::chrono_literals; + std::vector Animate2::calc_frames() { dbc::check(sequence.frames.size() == sequence.durations.size(), "sequence.frames.size() != sequence.durations.size()"); @@ -94,30 +96,28 @@ namespace animate2 { * elapsed is DELTA, or use elapsed here? */ void Animate2::update() { + dbc::check(sequence.easing_duration > 0.0, "bad easing duration"); dbc::check(playing, "attempt to update animation that's not playing"); dbc::check(sequence.frame_count == sequence.frames.size(), "frame_count doesn't match frame.size()"); - auto duration = sequence.durations.at(sequence.current); - auto elapsed = sequence.timer.getElapsedTime().toDuration(); - // std::cout << "subframe: " << sequence.subframe << " elapsed: " << elapsed << " duration: " << duration << std::endl; + auto [ticks, alpha] = sequence.timer.commit(); + int duration = sequence.durations.at(sequence.current); + sequence.subframe += ticks; + + sequence.easing_position += ticks; + bool frame_change = false; - if(elapsed < duration) { - // BUG: subframe will just run crazy because I don't actually do delta time difference here - sequence.subframe = std::lerp(sequence.subframe, 1.0, sequence.timer.DELTA * transform.ease_rate); - } else { + if(sequence.subframe >= duration) { sequence.timer.restart(); sequence.current++; - - if(sequence.subframe > SUB_FRAME_SENSITIVITY) { - sequence.subframe = 0.0f; - } - + sequence.subframe = 0; frame_change = true; } if(sequence.current >= sequence.frame_count) { sequence.loop_count++; + sequence.easing_position = 0; playing = onLoop(sequence, transform); } @@ -142,20 +142,18 @@ namespace animate2 { } } - std::pair Animate2::commit() { - return sequence.timer.commit(); - } - void Timer::start() { clock.start(); prev_time = clock.getElapsedTime().asSeconds(); } void Timer::reset() { + elapsed_ticks = 0; clock.reset(); } void Timer::restart() { + elapsed_ticks = 0; clock.restart(); prev_time = clock.getElapsedTime().asSeconds(); } @@ -182,6 +180,8 @@ namespace animate2 { accumulator -= tick_count * DELTA; // that leaves the remaining errors for next loop + elapsed_ticks += tick_count; + // alpha is then what we lerp...but WHY?! alpha = accumulator / DELTA; @@ -190,14 +190,9 @@ namespace animate2 { } void Transform::apply(Sequence& seq, sf::Vector2f& pos_out, sf::Vector2f& scale_out) { - // float dt = 1 - std::powf(ease_rate, seq.subframe + 0.0001); - float tick = easing_func(seq.subframe); + float tick = easing_func(seq.easing_position / seq.easing_duration); motion_func(*this, pos_out, scale_out, tick, relative); - - // fmt::println("sub: {}, tick: {}, tr: {},{}; pos: {},{}; scale: {},{}", - // seq.subframe, tick, min_y, max_y, pos_out.x, pos_out.y, - // scale_out.x, scale_out.y); } bool Animate2::has_form(const std::string& as_form) { @@ -224,6 +219,12 @@ namespace animate2 { sequence = sequences.at(seq_name); transform = transforms.at(tr_name); + // BUG: should this be configurable instead? + for(auto duration : sequence.durations) { + sequence.easing_duration += float(duration); + } + dbc::check(sequence.easing_duration > 0.0, "bad easing duration"); + $frame_rects = calc_frames(); transform.easing_func = ease2::get_easing(transform.easing); transform.motion_func = ease2::get_motion(transform.motion); diff --git a/animate2.hpp b/animate2.hpp index 3601da9..15f9601 100644 --- a/animate2.hpp +++ b/animate2.hpp @@ -29,6 +29,7 @@ namespace animate2 { double current_time = 0.0; double frame_duration = 0.0; double alpha = 0.0; + int elapsed_ticks = 0; sf::Clock clock{}; std::pair commit(); @@ -40,12 +41,14 @@ namespace animate2 { struct Sequence { std::vector frames{}; - std::vector durations{}; + std::vector durations{}; // in ticks size_t current{0}; int loop_count{0}; size_t frame_count{0}; Timer timer{}; - float subframe{0.0f}; + int subframe{0}; + float easing_duration{0.0f}; + float easing_position{0.0f}; }; struct Transform { @@ -55,7 +58,6 @@ namespace animate2 { float max_x{1.0f}; float max_y{1.0f}; bool flipped{false}; - float ease_rate{0.5f}; bool scaled{false}; bool relative{false}; @@ -123,7 +125,6 @@ namespace animate2 { void update(); void motion(sf::Transformable& sprite, sf::Vector2f pos, sf::Vector2f scale); void motion(sf::View& view_out, sf::Vector2f pos, sf::Vector2f scale); - std::pair commit(); }; Animate2 load(const std::string &file, const std::string &anim_name); @@ -131,6 +132,6 @@ namespace animate2 { ENROLL_COMPONENT(Sheet, frames, frame_width, frame_height); ENROLL_COMPONENT(Sequence, frames, durations); ENROLL_COMPONENT(Transform, min_x, min_y, max_x, max_y, - flipped, ease_rate, scaled, relative, toggled, looped, easing, motion); + flipped, scaled, relative, toggled, looped, easing, motion); ENROLL_COMPONENT(Animate2, sheet, sequences, transforms, forms, sounds); } diff --git a/assets/animate2.json b/assets/animate2.json index c2722a7..327a5de 100644 --- a/assets/animate2.json +++ b/assets/animate2.json @@ -6,9 +6,9 @@ "frame_height": 720 }, "sequences": { - "idle": {"frames": [0], "durations": [800] }, - "hurt": {"frames": [1, 1], "durations": [800, 240] }, - "attack": {"frames": [1, 0], "durations": [800, 200] } + "idle": {"frames": [0], "durations": [47] }, + "hurt": {"frames": [0, 1], "durations": [5, 57] }, + "attack": {"frames": [0, 1, 0, 1], "durations": [35, 15, 5, 5] } }, "transforms": { "rushing": { @@ -17,7 +17,6 @@ "max_x": 0.8, "max_y": 0.8, "flipped": false, - "ease_rate": 5.0, "scaled": true, "toggled": false, "looped": true, @@ -31,7 +30,6 @@ "max_x": 20.0, "max_y": 20.0, "flipped": true, - "ease_rate": 5.0, "scaled": true, "toggled": false, "looped": false, @@ -45,7 +43,6 @@ "max_x": 0, "max_y": -10, "flipped": false, - "ease_rate": 3.0, "scaled": false, "toggled": false, "looped": true, @@ -72,9 +69,9 @@ "frame_height": 540 }, "sequences": { - "idle": {"frames": [0], "durations": [800] }, - "hurt": {"frames": [0], "durations": [800] }, - "attack": {"frames": [0], "durations": [800] } + "idle": {"frames": [0], "durations": [47] }, + "hurt": {"frames": [0], "durations": [47] }, + "attack": {"frames": [0], "durations": [47] } }, "transforms": { "breathe": { @@ -83,7 +80,6 @@ "max_x": 0, "max_y": -10, "flipped": false, - "ease_rate": 3.0, "scaled": false, "toggled": false, "looped": true, @@ -110,7 +106,7 @@ "frame_height": 256 }, "sequences": { - "idle": {"frames": [0,1,2], "durations": [100, 100, 100] } + "idle": {"frames": [0,1,2], "durations": [10, 10, 10] } }, "transforms": { "render": { @@ -119,7 +115,6 @@ "max_x": 0, "max_y": 0, "flipped": false, - "ease_rate": 3.0, "scaled": true, "toggled": false, "looped": true, diff --git a/assets/cameras.json b/assets/cameras.json index dd75c5d..728b3d7 100644 --- a/assets/cameras.json +++ b/assets/cameras.json @@ -6,11 +6,11 @@ "frame_height": 768 }, "sequences": { - "idle": {"frames": [0], "durations": [800] }, - "pan": {"frames": [0], "durations": [800] }, - "shake": {"frames": [0], "durations": [800] }, - "dolly": {"frames": [0], "durations": [800] }, - "bounce": {"frames": [0], "durations": [800] } + "idle": {"frames": [0], "durations": [60] }, + "pan": {"frames": [0], "durations": [60] }, + "shake": {"frames": [0], "durations": [60] }, + "dolly": {"frames": [0], "durations": [60] }, + "bounce": {"frames": [0], "durations": [60] } }, "transforms": { "pan": { @@ -19,7 +19,6 @@ "max_x": 0.0, "max_y": 0.0, "flipped": false, - "ease_rate": 5.0, "scaled": false, "toggled": false, "looped": true, @@ -33,7 +32,6 @@ "max_x": 10.0, "max_y": 10.0, "flipped": false, - "ease_rate": 5.0, "scaled": false, "toggled": false, "looped": true, @@ -47,7 +45,6 @@ "max_x": 1.0, "max_y": 1.0, "flipped": false, - "ease_rate": 3.0, "scaled": false, "toggled": false, "looped": true, @@ -61,7 +58,6 @@ "max_x": 0, "max_y": 0, "flipped": false, - "ease_rate": 3.0, "scaled": false, "toggled": false, "looped": true, diff --git a/camera.cpp b/camera.cpp index 48361b0..799e264 100644 --- a/camera.cpp +++ b/camera.cpp @@ -98,8 +98,6 @@ namespace cinematic { } void Camera::update() { - // REFACTOR: there's no connection between anim.commit() and anim.update() - auto [ticks, alpha] = anim.commit(); if(anim.playing) anim.update(); } diff --git a/scene.cpp b/scene.cpp index 40ab0ac..582a7e9 100644 --- a/scene.cpp +++ b/scene.cpp @@ -131,7 +131,6 @@ namespace scene { inline void this_is_stupid_refactor(std::vector& elements) { for(auto& element : elements) { if(element.anim.playing) { - auto [ticks, alpha] = element.anim.commit(); element.anim.update(); element.anim.motion(*element.st.sprite, element.pos, element.scale); element.anim.apply(*element.st.sprite); diff --git a/tests/animate2.cpp b/tests/animate2.cpp index 882af6f..2c30fbb 100644 --- a/tests/animate2.cpp +++ b/tests/animate2.cpp @@ -22,7 +22,7 @@ Animate2 load_animation(const string& name) { anim.set_form("attack"); anim.transform.looped = false; - anim.sequence.durations = {Random::milliseconds(5, 33), Random::milliseconds(5, 33)}; + anim.sequence.durations = {Random::uniform(1, 5), Random::uniform(1, 5)}; return anim; } diff --git a/tools/animator.cpp b/tools/animator.cpp index 751d0bf..bbfa439 100644 --- a/tools/animator.cpp +++ b/tools/animator.cpp @@ -54,20 +54,11 @@ namespace animator { } void FSM::START(Event ev) { - switch(ev) { - case Event::TICK: - state(State::ANIMATE); - break; - default: - state(State::START); - } + state(State::ANIMATE); } void FSM::ANIMATE(Event ev) { switch(ev) { - case Event::TICK: - run_animation(); - break; case Event::PLAY_STOP: if($anim.playing) { $anim.stop(); @@ -122,7 +113,7 @@ namespace animator { $anim.play(); } - void FSM::run_animation() { + void FSM::update() { if($anim.playing) { $anim.update(); $anim.apply(*$ui.sprite); @@ -131,14 +122,6 @@ namespace animator { } } - void FSM::tick() { - auto [ticks, alpha] = $anim.commit(); - - for(int i = 0; i < ticks; i++) { - event(animator::Event::TICK, {}); - } - } - void FSM::check_update() { if($timer.getElapsedTime().toDuration() > 500ms) { try { @@ -356,9 +339,9 @@ int main(int argc, char* argv[]) { main.init(sprite_name, anim_name, background); while(main.active()) { - main.tick(); - main.check_update(); + main.update(); main.render(); + main.check_update(); main.handle_keyboard_mouse(); } diff --git a/tools/animator.hpp b/tools/animator.hpp index 6e894d7..b1e0c7e 100644 --- a/tools/animator.hpp +++ b/tools/animator.hpp @@ -66,8 +66,7 @@ namespace animator { void handle_keyboard_mouse(); void render(); bool active(); - void run_animation(); - void tick(); + void update(); void reload(); void check_update(); void change_form(int direction);