#include #include "animation.hpp" #include "textures.hpp" #include "dinkyecs.hpp" #include "config.hpp" #include #include #include #include #include "rand.hpp" #include "animate2.hpp" using namespace components; 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{0.5f}, .speed{0.02f}, .scaled{true}, .stationary{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)); } void PLAY_TEST(Animate2 &anim) { anim.play(); while(anim.playing) { anim.update(); FAKE_RENDER(); } REQUIRE(anim.playing == false); } /* * Animation is a Sheet + Sequence + Transform. * * A Sheet is just a grid of images with a predefined size for each cell. Arbitrary sized cells not supported. * * A Sequence is a list of Sheet cells _in any order_. See https://github.com/yottahmd/ganim8-lib. Sequences have a timing element for the cells, possibly a list of durations or a single duration. * * A Transform is combinations of scale and/or position easing/motion, shader effects, and things like if it's looped or toggled. * * I like the ganim8 onLoop concept, just a callback that says what to do when the animation has looped. */ TEST_CASE("new animation system", "[animation-new]") { textures::init(); auto anim = crafter(); PLAY_TEST(anim); bool onLoop_ran = false; anim.onLoop = [&](auto& seq, auto& tr) -> bool { seq.current = 0; onLoop_ran = true; return tr.looped; }; PLAY_TEST(anim); REQUIRE(onLoop_ran == true); // only runs twice anim.onLoop = [](auto& seq, auto& tr) -> bool { if(seq.loop_count == 2) { seq.current = 0; return false; } else { seq.current = seq.current % seq.frame_count; return true; } }; 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; } }; } TEST_CASE("confirm frame sequencing works", "[animation-new]") { textures::init(); animation::init(); auto anim = crafter(); auto blanket = textures::get_sprite("ritual_crafting_area"); sf::IntRect init_rect{{0,0}, {anim.$sheet.frame_width, anim.$sheet.frame_height}}; anim.play(); bool loop_ran = false; // this will check that it moved to the next frame anim.onLoop = [&](auto& seq, auto& tr) -> bool { seq.current = 0; loop_ran = true; REQUIRE(blanket.sprite->getTextureRect() != init_rect); return false; }; anim.onFrame = [&](){ anim.apply(*blanket.sprite); }; while(anim.playing) { anim.update(); // NOTE: possibly find a way to only run apply on frame change? } REQUIRE(loop_ran == true); REQUIRE(anim.playing == false); // this confirms it went back to the first frame REQUIRE(blanket.sprite->getTextureRect() == init_rect); } TEST_CASE("confirm transition changes work", "[animation-new]") { textures::init(); animation::init(); auto anim = crafter(); sf::Vector2f pos{10,10}; sf::Vector2f scale{0.6, 0.6}; // also testing that onFrame being null means it's not run REQUIRE(anim.onFrame == nullptr); anim.play(); sf::Clock clock; clock.start(); sf::Time start; sf::Time delta; while(anim.playing) { start = clock.getElapsedTime(); anim.update(); anim.motion(pos, scale, 0.8f); 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); REQUIRE(pos != sf::Vector2f{0,0}); REQUIRE(scale != sf::Vector2f{0,0}); } TEST_CASE("playing with delta time", "[animation-new]") { animate2::Timer timer; timer.start(); for(int i = 0; i < 20; i++) { FAKE_RENDER(); auto [tick_count, alpha] = timer.commit(); fmt::println("tick: {}, alpha: {}", tick_count, alpha); } }