206 lines
4.8 KiB
C++
206 lines
4.8 KiB
C++
#include <catch2/catch_test_macros.hpp>
|
|
#include "animation.hpp"
|
|
#include "textures.hpp"
|
|
#include "dinkyecs.hpp"
|
|
#include "config.hpp"
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <chrono>
|
|
#include <thread>
|
|
#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);
|
|
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);
|
|
}
|
|
}
|