From dea0607901f912104aee4cf97d21b91255c35947 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 3 Feb 2026 00:30:49 -0500 Subject: [PATCH] Now loading the new animations out of assets/animate2.json --- animate2.cpp | 74 +++++++++++++++++++++++++++----------------- animate2.hpp | 21 +++++++++---- assets/animate2.json | 25 +++++++++++++++ components.hpp | 1 - json_mods.hpp | 11 +++++++ tests/animate2.cpp | 64 ++++++-------------------------------- tools/animator.cpp | 49 ++--------------------------- 7 files changed, 108 insertions(+), 137 deletions(-) create mode 100644 assets/animate2.json diff --git a/animate2.cpp b/animate2.cpp index b103ec5..7d96c42 100644 --- a/animate2.cpp +++ b/animate2.cpp @@ -3,20 +3,23 @@ #include #include "dbc.hpp" #include "rand.hpp" +#include +#include constexpr float SUB_FRAME_SENSITIVITY = 0.999f; namespace animate2 { std::vector Animate2::calc_frames() { - dbc::check($sequence.frames.size() == $sequence.durations.size(), "$sequence.frames.size() != $sequence.durations.size()"); + dbc::check(sequence.frames.size() == sequence.durations.size(), "sequence.frames.size() != sequence.durations.size()"); std::vector frames; - for(int frame_i : $sequence.frames) { + for(int frame_i : sequence.frames) { + dbc::check(frame_i < sheet.frames, "frame index greater than sheet frames"); frames.emplace_back( - sf::Vector2i{$sheet.frame_width * frame_i, 0}, // NOTE: one row only for now - sf::Vector2i{$sheet.frame_width, - $sheet.frame_height}); + sf::Vector2i{sheet.frame_width * frame_i, 0}, // NOTE: one row only for now + sf::Vector2i{sheet.frame_width, + sheet.frame_height}); } return frames; @@ -24,56 +27,58 @@ namespace animate2 { void Animate2::play() { dbc::check(!playing, "can't call play while playing?"); - $sequence.current = 0; - $sequence.subframe = 0.0f; - $sequence.loop_count = 0; + sequence.current = 0; + sequence.subframe = 0.0f; + sequence.loop_count = 0; playing = true; - $sequence.timer.start(); + sequence.timer.start(); + sequence.frame_count = sequence.frames.size(); + $frame_rects = calc_frames(); } void Animate2::stop() { playing = false; - $sequence.timer.reset(); + sequence.timer.reset(); } // need one for each kind of thing to animate // NOTE: possibly find a way to only run apply on frame change? void Animate2::apply(sf::Sprite& sprite) { - dbc::check(!$transform.simple, "can't call ::apply() on a simple animation, only ::motion()"); + dbc::check(!transform.simple, "can't call ::apply() on a simple animation, only ::motion()"); - dbc::check($sequence.current < $frame_rects.size(), "current frame past $frame_rects"); + dbc::check(sequence.current < $frame_rects.size(), "current frame past $frame_rects"); // NOTE: pos is not updated yet - auto& rect = $frame_rects.at($sequence.current); + auto& rect = $frame_rects.at(sequence.current); sprite.setTextureRect(rect); } // replaces step void Animate2::update_frame() { 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()"); + dbc::check(sequence.frame_count == sequence.frames.size(), "frame_count doesn't match frame.size()"); - auto duration = $sequence.durations.at($sequence.current); + auto duration = sequence.durations.at(sequence.current); bool frame_change = false; - if($sequence.timer.getElapsedTime() >= duration) { - $sequence.timer.restart(); - $sequence.current++; - if($sequence.subframe > SUB_FRAME_SENSITIVITY) $sequence.subframe = 0.0f; + if(sequence.timer.getElapsedTime() >= duration) { + sequence.timer.restart(); + sequence.current++; + if(sequence.subframe > SUB_FRAME_SENSITIVITY) sequence.subframe = 0.0f; frame_change = true; } else { - $sequence.subframe = std::lerp($sequence.subframe, 1.0, $sequence.timer.DELTA * $transform.ease_rate); + sequence.subframe = std::lerp(sequence.subframe, 1.0, sequence.timer.DELTA * transform.ease_rate); - fmt::println("subframe: {}, alpha: {}", $sequence.subframe, $sequence.timer.alpha); + std::cout << "subframe: " << sequence.subframe << " duration: " << duration << std::endl; } - if($sequence.current >= $sequence.frame_count) { - $sequence.loop_count++; - playing = onLoop($sequence, $transform); + if(sequence.current >= sequence.frame_count) { + sequence.loop_count++; + playing = onLoop(sequence, transform); } if(frame_change && onFrame != nullptr) onFrame(); - dbc::check($sequence.current < $sequence.frame_count, "onLoop fail: current frame out of frames.size()"); + dbc::check(sequence.current < sequence.frame_count, "onLoop fail: current frame out of frames.size()"); } void Animate2::update() { @@ -81,21 +86,21 @@ namespace animate2 { } void Animate2::motion(sf::Sprite& sprite, sf::Vector2f pos, sf::Vector2f scale) { - $transform.lerp($sequence, pos, scale); + transform.lerp(sequence, pos, scale); - if($transform.flipped) { + if(transform.flipped) { scale.x *= -1; } sprite.setPosition(pos); - if($transform.scaled) { + if(transform.scaled) { sprite.setScale(scale); } } std::pair Animate2::commit() { - return $sequence.timer.commit(); + return sequence.timer.commit(); } void Timer::start() { @@ -151,4 +156,15 @@ namespace animate2 { seq.subframe, tick, min_y, max_y, pos_out.x, pos_out.y, scale_out.x, scale_out.y); } + + Animate2 load(const std::string &file, const std::string &anim_name) { + using nlohmann::json; + std::ifstream infile(file); + auto data = json::parse(infile); + + Animate2 anim; + animate2::from_json(data[anim_name], anim); + + return anim; + } } diff --git a/animate2.hpp b/animate2.hpp index 0c80ce5..3642da5 100644 --- a/animate2.hpp +++ b/animate2.hpp @@ -8,11 +8,14 @@ #include #include "ease2.hpp" #include +#include "json_mods.hpp" namespace animate2 { + + template struct NameOf; + struct Sheet { - int width{0}; - int height{0}; + int frames{0}; int frame_width{0}; int frame_height{0}; }; @@ -38,7 +41,7 @@ namespace animate2 { std::vector durations{}; size_t current{0}; int loop_count{0}; - size_t frame_count{frames.size()}; + size_t frame_count{0}; Timer timer{}; float subframe{0.0f}; }; @@ -82,9 +85,9 @@ namespace animate2 { class Animate2 { public: - Sheet $sheet; - Sequence $sequence; - Transform $transform; + Sheet sheet; + Sequence sequence; + Transform transform; OnFrameHandler onFrame = nullptr; std::vector $frame_rects{calc_frames()}; @@ -101,4 +104,10 @@ namespace animate2 { std::pair commit(); }; + Animate2 load(const std::string &file, const std::string &anim_name); + + ENROLL_COMPONENT(Sheet, frames, frame_width, frame_height); + ENROLL_COMPONENT(Sequence, frames, durations); + ENROLL_COMPONENT(Transform, min_x, min_y, max_x, max_y, simple, flipped, ease_rate, scaled, toggled, looped); + ENROLL_COMPONENT(Animate2, sheet, sequence, transform); } diff --git a/assets/animate2.json b/assets/animate2.json new file mode 100644 index 0000000..6d4bc10 --- /dev/null +++ b/assets/animate2.json @@ -0,0 +1,25 @@ +{ + "rat_king_boss": { + "sheet": { + "frames": 2, + "frame_width": 720, + "frame_height": 720 + }, + "sequence": { + "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": false + } + } +} diff --git a/components.hpp b/components.hpp index 422eea0..b265319 100644 --- a/components.hpp +++ b/components.hpp @@ -231,7 +231,6 @@ namespace components { }; } - void init(); void configure_entity(DinkyECS::World& world, DinkyECS::Entity ent, json& data); diff --git a/json_mods.hpp b/json_mods.hpp index 85481af..e582af2 100644 --- a/json_mods.hpp +++ b/json_mods.hpp @@ -31,4 +31,15 @@ namespace nlohmann { } } }; + + template<> + struct adl_serializer { + static void to_json(json& j, const std::chrono::milliseconds& opt) { + j = opt.count(); + } + + static void from_json(const json& j, std::chrono::milliseconds& opt) { + opt = std::chrono::milliseconds{int(j)}; + } + }; } diff --git a/tests/animate2.cpp b/tests/animate2.cpp index 727fb5a..a061a75 100644 --- a/tests/animate2.cpp +++ b/tests/animate2.cpp @@ -15,40 +15,6 @@ using namespace textures; using namespace std::chrono_literals; using namespace animate2; -Animate2 crafter() { - Sheet sheet{ - .width{720*2}, - .height{720}, - .frame_width{720}, - .frame_height{720}, - }; - - Sequence sequence{ - .frames{0,1}, - .durations{Random::milliseconds(1, 33), Random::milliseconds(1, 33)} - }; - - REQUIRE(sequence.frame_count == sequence.frames.size()); - REQUIRE(sequence.frame_count == sequence.durations.size()); - - Transform transform{ - .min_x{0.6f}, - .min_y{0.6f}, - .max_x{0.8f}, - .max_y{0.8f}, - .simple{false}, - .flipped{false}, - .ease_rate{5.0f}, - .scaled{true}, - .toggled{false}, - .looped{false}, - .easing = ease2::in_out_back, - .motion = ease2::move_rush, - }; - - return {sheet, sequence, transform}; -} - void FAKE_RENDER() { std::this_thread::sleep_for(Random::milliseconds(5, 32)); } @@ -78,7 +44,7 @@ void PLAY_TEST(Animate2 &anim) { TEST_CASE("new animation system", "[animation-new]") { textures::init(); - auto anim = crafter(); + auto anim = animate2::load("assets/animate2.json", "rat_king_boss"); PLAY_TEST(anim); bool onLoop_ran = false; @@ -103,7 +69,7 @@ TEST_CASE("new animation system", "[animation-new]") { }; PLAY_TEST(anim); - REQUIRE(anim.$sequence.loop_count == 2); + REQUIRE(anim.sequence.loop_count == 2); // stops at end anim.onLoop = [](auto& seq, auto& tr) -> bool { @@ -121,10 +87,10 @@ TEST_CASE("confirm frame sequencing works", "[animation-new]") { textures::init(); animation::init(); - auto anim = crafter(); + auto anim = animate2::load("assets/animate2.json", "rat_king_boss"); - auto blanket = textures::get_sprite("ritual_crafting_area"); - sf::IntRect init_rect{{0,0}, {anim.$sheet.frame_width, anim.$sheet.frame_height}}; + auto boss = textures::get_sprite("rat_king_boss"); + sf::IntRect init_rect{{0,0}, {anim.sheet.frame_width, anim.sheet.frame_height}}; anim.play(); bool loop_ran = false; @@ -134,12 +100,12 @@ TEST_CASE("confirm frame sequencing works", "[animation-new]") { seq.current = 0; loop_ran = true; - REQUIRE(blanket.sprite->getTextureRect() != init_rect); + REQUIRE(boss.sprite->getTextureRect() != init_rect); return false; }; anim.onFrame = [&](){ - anim.apply(*blanket.sprite); + anim.apply(*boss.sprite); }; while(anim.playing) { @@ -151,7 +117,7 @@ TEST_CASE("confirm frame sequencing works", "[animation-new]") { REQUIRE(anim.playing == false); // this confirms it went back to the first frame - REQUIRE(blanket.sprite->getTextureRect() == init_rect); + REQUIRE(boss.sprite->getTextureRect() == init_rect); } TEST_CASE("confirm transition changes work", "[animation-new]") { @@ -162,7 +128,7 @@ TEST_CASE("confirm transition changes work", "[animation-new]") { sf::Vector2f pos{100,100}; sprite.setPosition(pos); auto scale = sprite.getScale(); - auto anim = crafter(); + auto anim = animate2::load("assets/animate2.json", "rat_king_boss"); // also testing that onFrame being null means it's not run REQUIRE(anim.onFrame == nullptr); @@ -170,23 +136,11 @@ TEST_CASE("confirm transition changes work", "[animation-new]") { anim.play(); REQUIRE(anim.playing == true); - sf::Clock clock; - clock.start(); - sf::Time start; - sf::Time delta; - while(anim.playing) { - start = clock.getElapsedTime(); - anim.update(); anim.motion(sprite, pos, scale); std::this_thread::sleep_for(10ms); - fmt::println("POSITION: {},{}; SCALE: {},{}; current: {}; subframe: {}", - pos.x, pos.y, scale.x, scale.y, anim.$sequence.current, anim.$sequence.subframe); - - delta = clock.getElapsedTime() - start; - fmt::println("FRAME RATE {}", 1.0f / delta.asSeconds()); } REQUIRE(anim.playing == false); diff --git a/tools/animator.cpp b/tools/animator.cpp index 7df5f78..c95d6fa 100644 --- a/tools/animator.cpp +++ b/tools/animator.cpp @@ -12,50 +12,7 @@ using namespace std::chrono_literals; bool YES_SYNC=true; - -animate2::Sheet sheet{ - .width{720*2}, - .height{720}, - .frame_width{720}, - .frame_height{720}, -}; - -animate2::Sequence sequence{ - .frames{0,1}, - .durations{800ms, 200ms} -}; - -animate2::Transform scale_tr{ - .min_x{0.6f}, - .min_y{0.6f}, - .max_x{0.8f}, - .max_y{0.8f}, - .simple{false}, - .flipped{false}, - .ease_rate{4.0f}, - .scaled{true}, - .toggled{false}, - .looped{true}, - .easing = ease2::in_out_back, - .motion = ease2::move_rush, -}; - -animate2::Transform move_tr{ - .min_x{-20.0f}, - .min_y{-20.0f}, - .max_x{20.0f}, - .max_y{20.0f}, - .simple{false}, - .flipped{false}, - .ease_rate{2.5f}, - .scaled{true}, - .toggled{false}, - .looped{true}, - .easing = ease2::normal_dist, - .motion = ease2::move_shake, -}; - -animate2::Animate2 anim{sheet, sequence, scale_tr}; +animate2::Animate2 anim = animate2::load("assets/animate2.json", "rat_king_boss"); namespace animator { @@ -100,10 +57,10 @@ namespace animator { if(anim.playing) anim.stop(); break; case Event::LOOP: - anim.$transform.looped = !anim.$transform.looped; + anim.transform.looped = !anim.transform.looped; break; case Event::TOGGLE: - anim.$transform.toggled = !anim.$transform.toggled; + anim.transform.toggled = !anim.transform.toggled; break; default: state(State::START);