Added invariants to the Sequence to hunt down a bug in the tests but I'll leave it there for future testing.

This commit is contained in:
Zed A. Shaw 2026-02-21 13:24:39 -05:00
parent 1baca783fc
commit d56b4bd335
4 changed files with 44 additions and 18 deletions

View file

@ -35,7 +35,7 @@ namespace animate2 {
sequence.loop_count = 0;
playing = true;
sequence.timer.start();
sequence.frame_count = sequence.frames.size();
sequence.INVARIANT();
}
void Animate2::stop() {
@ -96,9 +96,8 @@ 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()");
sequence.INVARIANT();
auto [ticks, alpha] = sequence.timer.commit();
int duration = sequence.durations.at(sequence.current);
@ -119,16 +118,17 @@ namespace animate2 {
sequence.loop_count++;
sequence.easing_position = 0;
playing = onLoop(sequence, transform);
sequence.INVARIANT();
}
if(frame_change) play_sound();
if(frame_change && onFrame != nullptr) onFrame();
dbc::check(sequence.current < sequence.frame_count, "onLoop fail: current frame out of frames.size()");
}
void Animate2::motion(sf::Transformable& sprite, sf::Vector2f pos, sf::Vector2f scale) {
sequence.INVARIANT();
transform.apply(sequence, pos, scale);
if(transform.flipped) {
@ -219,6 +219,8 @@ namespace animate2 {
sequence = sequences.at(seq_name);
transform = transforms.at(tr_name);
sequence.frame_count = sequence.frames.size();
// BUG: should this be configurable instead?
for(auto duration : sequence.durations) {
sequence.easing_duration += float(duration);
@ -228,6 +230,8 @@ namespace animate2 {
$frame_rects = calc_frames();
transform.easing_func = ease2::get_easing(transform.easing);
transform.motion_func = ease2::get_motion(transform.motion);
sequence.INVARIANT();
}
Animate2 load(const std::string &file, const std::string &anim_name) {
@ -245,4 +249,23 @@ namespace animate2 {
return anim;
}
void Sequence::INVARIANT(const std::source_location location) {
dbc::check(frames.size() == durations.size(),
fmt::format("frames.size={} doesn't match durations.size={}",
frames.size(), durations.size()), location);
dbc::check(easing_duration > 0.0,
fmt::format("bad easing duration: {}", easing_duration), location);
dbc::check(frame_count == frames.size(),
fmt::format("frame_count={} doesn't match frames.size={}", frame_count, frames.size()), location);
dbc::check(frame_count == durations.size(),
fmt::format("frame_count={} doesn't match durations.size={}", frame_count, durations.size()), location);
dbc::check(current < durations.size(),
fmt::format("current={} went past end of fame durations.size={}",
current, durations.size()), location);
}
}

View file

@ -11,6 +11,7 @@
#include "ease2.hpp"
#include <fmt/core.h>
#include "json_mods.hpp"
#include <source_location>
namespace animate2 {
@ -44,11 +45,13 @@ namespace animate2 {
std::vector<int> durations{}; // in ticks
size_t current{0};
int loop_count{0};
size_t frame_count{0};
size_t frame_count{frames.size()};
Timer timer{};
int subframe{0};
float easing_duration{0.0f};
float easing_position{0.0f};
void INVARIANT(const std::source_location location = std::source_location::current());
};
struct Transform {

View file

@ -8,7 +8,7 @@
"sequences": {
"idle": {"frames": [0], "durations": [47] },
"hurt": {"frames": [0, 1], "durations": [5, 57] },
"attack": {"frames": [0, 1, 0, 1], "durations": [35, 15, 5, 5] }
"attack": {"frames": [0, 1, 0, 1], "durations": [35, 15, 5, 50] }
},
"transforms": {
"rushing": {

View file

@ -22,7 +22,11 @@ Animate2 load_animation(const string& name) {
anim.set_form("attack");
anim.transform.looped = false;
anim.sequence.durations = {Random::uniform(1, 5), Random::uniform(1, 5)};
for(size_t i = 0; i < anim.sequence.durations.size(); i++) {
anim.sequence.durations[i] = Random::uniform(1, 5);
}
return anim;
}
@ -61,6 +65,12 @@ TEST_CASE("new animation system", "[animation-new]") {
auto anim = load_animation("rat_king_boss");
PLAY_TEST(anim);
// test that toggled works
anim.transform.toggled = true;
PLAY_TEST(anim);
REQUIRE(anim.sequence.current == anim.sequence.frames.size() - 1);
anim.transform.toggled = false;
bool onLoop_ran = false;
anim.onLoop = [&](auto& seq, auto& tr) -> bool {
seq.current = 0;
@ -84,16 +94,6 @@ TEST_CASE("new animation system", "[animation-new]") {
PLAY_TEST(anim);
REQUIRE(anim.sequence.loop_count == 2);
// stops at end
anim.onLoop = [](auto& seq, auto& tr) -> bool {
if(seq.loop_count == 1) {
seq.current = seq.frame_count - 1;
return false;
} else {
return true;
}
};
}