From 4356b1535ecf8b0b4592f8e3b46c11bdb348c2ef Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 4 Feb 2026 15:10:07 -0500 Subject: [PATCH] Animation tool now lets you cycle through different sequence/transform 'forms' and shows you which one you're viewing. --- animate2.cpp | 33 ++++++++++++++-- animate2.hpp | 18 +++++++-- assets/animate2.json | 91 +++++++++++++++++++++++++------------------- tools/animator.cpp | 77 ++++++++++++++++++++++++++++++++----- tools/animator.hpp | 7 ++++ 5 files changed, 170 insertions(+), 56 deletions(-) diff --git a/animate2.cpp b/animate2.cpp index 045aac3..1b8dc52 100644 --- a/animate2.cpp +++ b/animate2.cpp @@ -157,6 +157,31 @@ namespace animate2 { // scale_out.x, scale_out.y); } + void Animate2::set_form(const std::string& as_form) { + dbc::check(forms.contains(as_form), + fmt::format("form {} does not exist in animation", as_form)); + stop(); + + const auto& [seq_name, tr_name] = forms.at(as_form); + + dbc::check(sequences.contains(seq_name), + fmt::format("sequences do NOT have \"{}\" name", seq_name)); + + dbc::check(transforms.contains(tr_name), + fmt::format("transforms do NOT have \"{}\" name", tr_name)); + + // everything good, do the update + form_name = as_form; + sequence_name = seq_name; + transform_name = tr_name; + sequence = sequences.at(seq_name); + transform = transforms.at(tr_name); + + $frame_rects = calc_frames(); + transform.easing_func = ease2::get_easing(transform.easing); + transform.motion_func = ease2::get_motion(transform.motion); + } + Animate2 load(const std::string &file, const std::string &anim_name) { using nlohmann::json; std::ifstream infile(file); @@ -165,10 +190,10 @@ namespace animate2 { Animate2 anim; animate2::from_json(data[anim_name], anim); - // BUG: json doesn't like may "fancy" no-constructor bullshit - anim.$frame_rects = anim.calc_frames(); - anim.transform.easing_func = ease2::get_easing(anim.transform.easing); - anim.transform.motion_func = ease2::get_motion(anim.transform.motion); + dbc::check(anim.forms.contains("idle"), + fmt::format("animation {} must have 'idle' form", anim_name)); + + anim.set_form("idle"); return anim; } diff --git a/animate2.hpp b/animate2.hpp index 51f17d7..01c7491 100644 --- a/animate2.hpp +++ b/animate2.hpp @@ -86,20 +86,32 @@ namespace animate2 { return tr.looped; } + using Form = std::pair; + class Animate2 { public: Sheet sheet; - Sequence sequence; - Transform transform; + std::unordered_map sequences; + std::unordered_map transforms; + std::unordered_map forms; OnFrameHandler onFrame = nullptr; + Sequence sequence{}; + Transform transform{}; + std::vector $frame_rects{calc_frames()}; OnLoopHandler onLoop = DefaultOnLoop; bool playing = false; + // mostly for debugging purposes + std::string form_name="idle"; + std::string sequence_name=""; + std::string transform_name=""; + std::vector calc_frames(); void play(); void stop(); + void set_form(const std::string& form); void apply(sf::Sprite& sprite); void update_frame(); void update(); @@ -113,5 +125,5 @@ namespace animate2 { ENROLL_COMPONENT(Sequence, frames, durations); ENROLL_COMPONENT(Transform, min_x, min_y, max_x, max_y, simple, flipped, ease_rate, scaled, toggled, looped, easing, motion); - ENROLL_COMPONENT(Animate2, sheet, sequence, transform); + ENROLL_COMPONENT(Animate2, sheet, sequences, transforms, forms); } diff --git a/assets/animate2.json b/assets/animate2.json index a43e7d5..814ab10 100644 --- a/assets/animate2.json +++ b/assets/animate2.json @@ -5,48 +5,59 @@ "frame_width": 720, "frame_height": 720 }, - "sequence": { - "frames": [0, 1], - "durations": [240, 240] + "sequences": { + "normal": {"frames": [0, 1], "durations": [800, 240] }, + "reversed": {"frames": [1, 0], "durations": [800, 200] }, + "meth": {"frames": [0, 1], "durations": [33, 33] } }, - "transform": { - "min_x": 0.6, - "min_y": 0.6, - "max_x": 0.8, - "max_y": 0.8, - "simple": false, - "flipped": false, - "ease_rate": 5.0, - "scaled": true, - "toggled": false, - "looped": true, - "easing": "out_circle", - "motion": "scale_grow" - } - }, - "rat_king_meth": { - "sheet": { - "frames": 2, - "frame_width": 720, - "frame_height": 720 + "transforms": { + "rushing": { + "min_x": 0.6, + "min_y": 0.6, + "max_x": 0.8, + "max_y": 0.8, + "simple": false, + "flipped": false, + "ease_rate": 5.0, + "scaled": true, + "toggled": false, + "looped": true, + "easing": "in_out_back", + "motion": "move_rush" + }, + "meth": { + "min_x": -20.0, + "min_y": -20.0, + "max_x": 20.0, + "max_y": 20.0, + "simple": true, + "flipped": true, + "ease_rate": 5.0, + "scaled": true, + "toggled": false, + "looped": true, + "easing": "normal_dist", + "motion": "move_shake" + }, + "breathe": { + "min_x": 0.90, + "min_y": 0.90, + "max_x": 1.0, + "max_y": 1.0, + "simple": true, + "flipped": false, + "ease_rate": 3.5, + "scaled": true, + "toggled": false, + "looped": true, + "easing": "in_out_back", + "motion": "scale_squeeze" + } }, - "sequence": { - "frames": [0, 1], - "durations": [133, 133] - }, - "transform": { - "min_x": -20.0, - "min_y": -20.0, - "max_x": 20.0, - "max_y": 20.0, - "simple": false, - "flipped": true, - "ease_rate": 5.0, - "scaled": true, - "toggled": false, - "looped": true, - "easing": "normal_dist", - "motion": "move_shake" + "forms": { + "idle": ["normal", "breathe"], + "attack": ["normal", "rushing"], + "hurt": ["reversed", "meth"] } } } diff --git a/tools/animator.cpp b/tools/animator.cpp index 74d52ce..1d5b0c6 100644 --- a/tools/animator.cpp +++ b/tools/animator.cpp @@ -9,12 +9,12 @@ #include "animate2.hpp" #include "tools/animator.hpp" #include +#include +#include /* * TODO: * - * - Easing functions from strings in json. - * - Map of animation forms. * - Bring back shaders. */ @@ -80,8 +80,17 @@ namespace animator { $anim.play(); } break; + case Event::PREV_FORM: + change_form(-1); + $ui.update_status($anim); + break; + case Event::NEXT_FORM: + change_form(1); + $ui.update_status($anim); + break; case Event::RELOAD: reload(); + $ui.update_status($anim); state(State::START); break; default: @@ -92,6 +101,26 @@ namespace animator { void FSM::END(Event ev) { } + void FSM::change_form(int direction) { + dbc::check($anim.forms.size() > 0, "you can't use an empty animation.forms idiot."); + + $cur_form_i = std::clamp($cur_form_i + direction, 0, int($anim.forms.size()) - 1); + dbc::check($cur_form_i >= 0, "you fucked up, Zed"); + + // this is the dumbest shit ever + auto key_view = std::views::keys($anim.forms); + std::vector keys(key_view.begin(), key_view.end()); + + dbc::check(size_t($cur_form_i) < keys.size(), "form index outside of form keys vector"); + + $cur_form = keys[$cur_form_i]; + fmt::println("cur_form_i {}; cur_form: {}", $cur_form_i, $cur_form); + + // set_form will stop the animation + $anim.set_form($cur_form); + $anim.play(); + } + void FSM::run_animation() { if($anim.playing) { $anim.update(); @@ -110,20 +139,29 @@ namespace animator { void FSM::check_update() { if($timer.getElapsedTime().toDuration() > 500ms) { - auto mod_time = std::filesystem::last_write_time("assets/animate2.json"); + try { + auto mod_time = std::filesystem::last_write_time("assets/animate2.json"); - if($last_mod_time < mod_time) { - event(Event::RELOAD); + if($last_mod_time < mod_time) { + event(Event::RELOAD); + } + + $timer.restart(); + } catch(const std::filesystem::filesystem_error& err) { + fmt::println("failed to open {}: {}", err.path1().string(), err.what()); + $timer.restart(); } - - $timer.restart(); } } void FSM::reload() { $anim = animate2::load("assets/animate2.json", $anim_name); - $anim.play(); + + // BUG: this will throw the index off after reloads, oh well + $anim.set_form($cur_form); $last_mod_time = std::filesystem::last_write_time("assets/animate2.json"); + + $anim.play(); } void FSM::handle_keyboard_mouse() { @@ -137,6 +175,10 @@ namespace animator { case KEY_PRESS: if($router.scancode == KEY::Space) { event(Event::PLAY_STOP); + } else if($router.scancode == KEY::Up) { + event(Event::PREV_FORM); + } else if($router.scancode == KEY::Down) { + event(Event::NEXT_FORM); } break; case MOUSE_CLICK: @@ -155,7 +197,7 @@ namespace animator { void FSM::render() { $window.clear(); - $ui.render($window, true); + $ui.render($window, false); $window.display(); } @@ -177,13 +219,24 @@ namespace animator { $ui.set(viewer, { sprite_name, 0, false}); $ui.init(); + + $overlay.position(0, 0, width/4, height/4); + $overlay.layout( + "[form]" + "[sequence]" + "[transform]" + "[error]"); + + $overlay.init(); } void UI::render(sf::RenderWindow& window, bool debug) { $ui.render(window); + $overlay.render(window); if(debug) { $ui.debug_layout(window); + $overlay.debug_layout(window); } } @@ -191,6 +244,12 @@ namespace animator { return $ui.mouse(x, y, mods); } + void UI::update_status(animate2::Animate2& anim) { + $overlay.show_text("form", guecs::to_wstring(anim.form_name)); + $overlay.show_text("sequence", guecs::to_wstring(anim.sequence_name)); + $overlay.show_text("transform", guecs::to_wstring(anim.transform_name)); + } + std::shared_ptr UI::get_sprite() { auto viewer = $ui.entity("viewer"); return $ui.get(viewer).sprite; diff --git a/tools/animator.hpp b/tools/animator.hpp index 66a745b..e0a4a44 100644 --- a/tools/animator.hpp +++ b/tools/animator.hpp @@ -16,6 +16,8 @@ namespace animator { enum class Event { TICK=__LINE__, PLAY_STOP=__LINE__, + NEXT_FORM=__LINE__, + PREV_FORM=__LINE__, RELOAD=__LINE__, }; @@ -23,11 +25,13 @@ namespace animator { struct UI { guecs::UI $ui; + guecs::UI $overlay; void button(const std::string& name, std::function cb); void init(const std::string& sprite_name, const std::string& background, int width, int height); void render(sf::RenderWindow& window, bool debug=false); bool mouse(float x, float y, guecs::Modifiers mods); + void update_status(animate2::Animate2& anim); std::shared_ptr get_sprite(); }; @@ -44,6 +48,8 @@ namespace animator { std::string $background=""; std::filesystem::file_time_type $last_mod_time; sf::Clock $timer; + std::string $cur_form = "idle"; + int $cur_form_i = 0; void init(const std::string &sprite_name, const std::string& background, const std::string &anim_name); void event(Event ev, std::any data={}); @@ -58,6 +64,7 @@ namespace animator { void tick(); void reload(); void check_update(); + void change_form(int direction); }; }