#include "animate2.hpp" #include #include #include "dbc.hpp" #include "rand.hpp" namespace animate2 { std::vector Animate2::calc_frames() { std::vector frames; for(int frame_i : $sequence.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(); } 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($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++; frame_change = true; } 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() { $sequence.subframe += $transform.speed; update_frame(); } void Animate2::motion(sf::Vector2f& pos, sf::Vector2f& scale) { $transform.twitching($sequence); $transform.lerp($sequence, pos, scale); } float Transform::twitching(Sequence& seq) { float tick = 1 - std::powf(ease_rate, seq.subframe + 0.0001); switch(easing) { case ease::NONE: return 0.0; case ease::SINE: return std::abs(std::sin(seq.subframe * ease_rate)); case ease::OUT_CIRC: return ease::out_circ(tick); case ease::OUT_BOUNCE: return ease::out_bounce(tick); case ease::IN_OUT_BACK: return ease::in_out_back(tick); case ease::RANDOM: return Random::uniform_real(0.0001f, 1.0f); case ease::NORM_DIST: return Random::normal(0.5f, 0.1f); case ease::LINEAR: return tick; default: dbc::sentinel( fmt::format("Invalid easing {} given to animation", int(easing))); } } void Transform::lerp(Sequence& seq, sf::Vector2f& pos_out, sf::Vector2f& scale_out) { float tick = twitching(seq); if(stationary) { switch(motion) { case ease::SHAKE: { pos_out.x += std::lerp(min_x, max_x, tick); } break; case ease::BOUNCE: { pos_out.y -= std::lerp(min_y, max_y, tick); } break; case ease::RUSH: { scale_out.x = std::lerp(min_x, max_x, tick); scale_out.y = std::lerp(min_y, max_y, tick); pos_out.y = pos_out.y - (pos_out.y * scale_out.y - pos_out.y); } break; case ease::SQUEEZE: { scale_out.x *= std::lerp(min_x, max_x, tick); } break; case ease::SQUASH: { scale_out.y *= std::lerp(min_y, max_y, tick); } break; case ease::STRETCH: { scale_out.x = std::lerp(min_x, max_x, tick); } break; case ease::GROW: { scale_out.y = std::lerp(min_y, max_y, tick); } break; case ease::SLIDE: { pos_out.x = std::lerp(min_x, max_x, tick); pos_out.y = std::lerp(min_y, max_y, tick); } break; default: dbc::sentinel("Unknown animation.motion setting."); } } else { dbc::sentinel("scale should not run"); scale_out.x = std::lerp(scale_out.x * min_x, scale_out.x * max_x, tick); scale_out.y = std::lerp(scale_out.y * min_y, scale_out.y * max_y, tick); } } }