#include "animate2.hpp" #include #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()"); std::vector 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}); } return frames; } void Animate2::play() { dbc::check(!playing, "can't call play while playing?"); sequence.current = 0; sequence.subframe = 0.0f; sequence.loop_count = 0; playing = true; sequence.timer.start(); sequence.frame_count = sequence.frames.size(); $frame_rects = calc_frames(); } void Animate2::stop() { playing = false; 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(sequence.current < $frame_rects.size(), "current frame past $frame_rects"); // NOTE: pos is not updated yet 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()"); 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; frame_change = true; } else { sequence.subframe = std::lerp(sequence.subframe, 1.0, sequence.timer.DELTA * transform.ease_rate); std::cout << "subframe: " << sequence.subframe << " duration: " << duration << std::endl; } 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()"); } void Animate2::update() { update_frame(); } void Animate2::motion(sf::Sprite& sprite, sf::Vector2f pos, sf::Vector2f scale) { transform.lerp(sequence, pos, scale); if(transform.flipped) { scale.x *= -1; } sprite.setPosition(pos); if(transform.scaled) { sprite.setScale(scale); } } std::pair Animate2::commit() { return sequence.timer.commit(); } void Timer::start() { clock.start(); prev_time = clock.getElapsedTime().asSeconds(); } void Timer::reset() { clock.reset(); } void Timer::restart() { clock.restart(); prev_time = clock.getElapsedTime().asSeconds(); } sf::Time Timer::getElapsedTime() { return clock.getElapsedTime(); } std::pair Timer::commit() { // determine frame duration based on previous time current_time = clock.getElapsedTime().asSeconds(); frame_duration = current_time - prev_time; // update prev_time for the next call prev_time = current_time; // update accumulator, retaining previous errors accumulator += frame_duration; // find the tick count based on DELTA double tick_count = floor(accumulator / DELTA); // reduce accumulator by the number of DELTAS accumulator -= tick_count * DELTA; // that leaves the remaining errors for next loop // alpha is then what we lerp...but WHY?! alpha = accumulator / DELTA; // return the number of even DELTA ticks and the alpha return {int(tick_count), alpha}; } void Transform::lerp(Sequence& seq, sf::Vector2f& pos_out, sf::Vector2f& scale_out) { // float dt = 1 - std::powf(ease_rate, seq.subframe + 0.0001); float tick = easing(seq.subframe); motion(*this, pos_out, scale_out, tick); fmt::println("sub: {}, tick: {}, tr: {},{}; pos: {},{}; scale: {},{}", 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; } }