#include "Animation.h" #include "UIDrawable.h" #include "UIEntity.h" #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Animation implementation Animation::Animation(const std::string& targetProperty, const AnimationValue& targetValue, float duration, EasingFunction easingFunc, bool delta) : targetProperty(targetProperty) , targetValue(targetValue) , duration(duration) , easingFunc(easingFunc) , delta(delta) { } void Animation::start(UIDrawable* target) { currentTarget = target; elapsed = 0.0f; // Capture startValue from target based on targetProperty if (!currentTarget) return; // Try to get the current value based on the expected type std::visit([this](const auto& targetVal) { using T = std::decay_t; if constexpr (std::is_same_v) { float value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v) { int value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v>) { // For sprite animation, get current sprite index int value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v) { sf::Color value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v) { sf::Vector2f value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v) { std::string value; if (currentTarget->getProperty(targetProperty, value)) { startValue = value; } } }, targetValue); } void Animation::startEntity(UIEntity* target) { currentEntityTarget = target; currentTarget = nullptr; // Clear drawable target elapsed = 0.0f; // Capture the starting value from the entity std::visit([this, target](const auto& val) { using T = std::decay_t; if constexpr (std::is_same_v) { float value = 0.0f; if (target->getProperty(targetProperty, value)) { startValue = value; } } else if constexpr (std::is_same_v) { // For entities, we might need to handle sprite_index differently if (targetProperty == "sprite_index" || targetProperty == "sprite_number") { startValue = target->sprite.getSpriteIndex(); } } // Entities don't support other types yet }, targetValue); } bool Animation::update(float deltaTime) { if ((!currentTarget && !currentEntityTarget) || isComplete()) { return false; } elapsed += deltaTime; elapsed = std::min(elapsed, duration); // Calculate easing value (0.0 to 1.0) float t = duration > 0 ? elapsed / duration : 1.0f; float easedT = easingFunc(t); // Get interpolated value AnimationValue currentValue = interpolate(easedT); // Apply currentValue to target (either drawable or entity) std::visit([this](const auto& value) { using T = std::decay_t; if (currentTarget) { // Handle UIDrawable targets if constexpr (std::is_same_v) { currentTarget->setProperty(targetProperty, value); } else if constexpr (std::is_same_v) { currentTarget->setProperty(targetProperty, value); } else if constexpr (std::is_same_v) { currentTarget->setProperty(targetProperty, value); } else if constexpr (std::is_same_v) { currentTarget->setProperty(targetProperty, value); } else if constexpr (std::is_same_v) { currentTarget->setProperty(targetProperty, value); } } else if (currentEntityTarget) { // Handle UIEntity targets if constexpr (std::is_same_v) { currentEntityTarget->setProperty(targetProperty, value); } else if constexpr (std::is_same_v) { currentEntityTarget->setProperty(targetProperty, value); } // Entities don't support other types yet } }, currentValue); return !isComplete(); } AnimationValue Animation::getCurrentValue() const { float t = duration > 0 ? elapsed / duration : 1.0f; float easedT = easingFunc(t); return interpolate(easedT); } AnimationValue Animation::interpolate(float t) const { // Visit the variant to perform type-specific interpolation return std::visit([this, t](const auto& target) -> AnimationValue { using T = std::decay_t; if constexpr (std::is_same_v) { // Interpolate float const float* start = std::get_if(&startValue); if (!start) return target; // Type mismatch if (delta) { return *start + target * t; } else { return *start + (target - *start) * t; } } else if constexpr (std::is_same_v) { // Interpolate integer const int* start = std::get_if(&startValue); if (!start) return target; float result; if (delta) { result = *start + target * t; } else { result = *start + (target - *start) * t; } return static_cast(std::round(result)); } else if constexpr (std::is_same_v>) { // For sprite animation, interpolate through the list if (target.empty()) return target; // Map t to an index in the vector size_t index = static_cast(t * (target.size() - 1)); index = std::min(index, target.size() - 1); return static_cast(target[index]); } else if constexpr (std::is_same_v) { // Interpolate color const sf::Color* start = std::get_if(&startValue); if (!start) return target; sf::Color result; if (delta) { result.r = std::clamp(start->r + target.r * t, 0.0f, 255.0f); result.g = std::clamp(start->g + target.g * t, 0.0f, 255.0f); result.b = std::clamp(start->b + target.b * t, 0.0f, 255.0f); result.a = std::clamp(start->a + target.a * t, 0.0f, 255.0f); } else { result.r = start->r + (target.r - start->r) * t; result.g = start->g + (target.g - start->g) * t; result.b = start->b + (target.b - start->b) * t; result.a = start->a + (target.a - start->a) * t; } return result; } else if constexpr (std::is_same_v) { // Interpolate vector const sf::Vector2f* start = std::get_if(&startValue); if (!start) return target; if (delta) { return sf::Vector2f(start->x + target.x * t, start->y + target.y * t); } else { return sf::Vector2f(start->x + (target.x - start->x) * t, start->y + (target.y - start->y) * t); } } else if constexpr (std::is_same_v) { // For text, show characters based on t const std::string* start = std::get_if(&startValue); if (!start) return target; // If delta mode, append characters from target if (delta) { size_t chars = static_cast(target.length() * t); return *start + target.substr(0, chars); } else { // Transition from start text to target text if (t < 0.5f) { // First half: remove characters from start size_t chars = static_cast(start->length() * (1.0f - t * 2.0f)); return start->substr(0, chars); } else { // Second half: add characters to target size_t chars = static_cast(target.length() * ((t - 0.5f) * 2.0f)); return target.substr(0, chars); } } } return target; // Fallback }, targetValue); } // Easing functions implementation namespace EasingFunctions { float linear(float t) { return t; } float easeIn(float t) { return t * t; } float easeOut(float t) { return t * (2.0f - t); } float easeInOut(float t) { return t < 0.5f ? 2.0f * t * t : -1.0f + (4.0f - 2.0f * t) * t; } // Quadratic float easeInQuad(float t) { return t * t; } float easeOutQuad(float t) { return t * (2.0f - t); } float easeInOutQuad(float t) { return t < 0.5f ? 2.0f * t * t : -1.0f + (4.0f - 2.0f * t) * t; } // Cubic float easeInCubic(float t) { return t * t * t; } float easeOutCubic(float t) { float t1 = t - 1.0f; return t1 * t1 * t1 + 1.0f; } float easeInOutCubic(float t) { return t < 0.5f ? 4.0f * t * t * t : (t - 1.0f) * (2.0f * t - 2.0f) * (2.0f * t - 2.0f) + 1.0f; } // Quartic float easeInQuart(float t) { return t * t * t * t; } float easeOutQuart(float t) { float t1 = t - 1.0f; return 1.0f - t1 * t1 * t1 * t1; } float easeInOutQuart(float t) { return t < 0.5f ? 8.0f * t * t * t * t : 1.0f - 8.0f * (t - 1.0f) * (t - 1.0f) * (t - 1.0f) * (t - 1.0f); } // Sine float easeInSine(float t) { return 1.0f - std::cos(t * M_PI / 2.0f); } float easeOutSine(float t) { return std::sin(t * M_PI / 2.0f); } float easeInOutSine(float t) { return 0.5f * (1.0f - std::cos(M_PI * t)); } // Exponential float easeInExpo(float t) { return t == 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f)); } float easeOutExpo(float t) { return t == 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f * t); } float easeInOutExpo(float t) { if (t == 0.0f) return 0.0f; if (t == 1.0f) return 1.0f; if (t < 0.5f) { return 0.5f * std::pow(2.0f, 20.0f * t - 10.0f); } else { return 1.0f - 0.5f * std::pow(2.0f, -20.0f * t + 10.0f); } } // Circular float easeInCirc(float t) { return 1.0f - std::sqrt(1.0f - t * t); } float easeOutCirc(float t) { float t1 = t - 1.0f; return std::sqrt(1.0f - t1 * t1); } float easeInOutCirc(float t) { if (t < 0.5f) { return 0.5f * (1.0f - std::sqrt(1.0f - 4.0f * t * t)); } else { return 0.5f * (std::sqrt(1.0f - (2.0f * t - 2.0f) * (2.0f * t - 2.0f)) + 1.0f); } } // Elastic float easeInElastic(float t) { if (t == 0.0f) return 0.0f; if (t == 1.0f) return 1.0f; float p = 0.3f; float a = 1.0f; float s = p / 4.0f; float t1 = t - 1.0f; return -(a * std::pow(2.0f, 10.0f * t1) * std::sin((t1 - s) * (2.0f * M_PI) / p)); } float easeOutElastic(float t) { if (t == 0.0f) return 0.0f; if (t == 1.0f) return 1.0f; float p = 0.3f; float a = 1.0f; float s = p / 4.0f; return a * std::pow(2.0f, -10.0f * t) * std::sin((t - s) * (2.0f * M_PI) / p) + 1.0f; } float easeInOutElastic(float t) { if (t == 0.0f) return 0.0f; if (t == 1.0f) return 1.0f; float p = 0.45f; float a = 1.0f; float s = p / 4.0f; if (t < 0.5f) { float t1 = 2.0f * t - 1.0f; return -0.5f * (a * std::pow(2.0f, 10.0f * t1) * std::sin((t1 - s) * (2.0f * M_PI) / p)); } else { float t1 = 2.0f * t - 1.0f; return a * std::pow(2.0f, -10.0f * t1) * std::sin((t1 - s) * (2.0f * M_PI) / p) * 0.5f + 1.0f; } } // Back (overshooting) float easeInBack(float t) { const float s = 1.70158f; return t * t * ((s + 1.0f) * t - s); } float easeOutBack(float t) { const float s = 1.70158f; float t1 = t - 1.0f; return t1 * t1 * ((s + 1.0f) * t1 + s) + 1.0f; } float easeInOutBack(float t) { const float s = 1.70158f * 1.525f; if (t < 0.5f) { return 0.5f * (4.0f * t * t * ((s + 1.0f) * 2.0f * t - s)); } else { float t1 = 2.0f * t - 2.0f; return 0.5f * (t1 * t1 * ((s + 1.0f) * t1 + s) + 2.0f); } } // Bounce float easeOutBounce(float t) { if (t < 1.0f / 2.75f) { return 7.5625f * t * t; } else if (t < 2.0f / 2.75f) { float t1 = t - 1.5f / 2.75f; return 7.5625f * t1 * t1 + 0.75f; } else if (t < 2.5f / 2.75f) { float t1 = t - 2.25f / 2.75f; return 7.5625f * t1 * t1 + 0.9375f; } else { float t1 = t - 2.625f / 2.75f; return 7.5625f * t1 * t1 + 0.984375f; } } float easeInBounce(float t) { return 1.0f - easeOutBounce(1.0f - t); } float easeInOutBounce(float t) { if (t < 0.5f) { return 0.5f * easeInBounce(2.0f * t); } else { return 0.5f * easeOutBounce(2.0f * t - 1.0f) + 0.5f; } } // Get easing function by name EasingFunction getByName(const std::string& name) { static std::unordered_map easingMap = { {"linear", linear}, {"easeIn", easeIn}, {"easeOut", easeOut}, {"easeInOut", easeInOut}, {"easeInQuad", easeInQuad}, {"easeOutQuad", easeOutQuad}, {"easeInOutQuad", easeInOutQuad}, {"easeInCubic", easeInCubic}, {"easeOutCubic", easeOutCubic}, {"easeInOutCubic", easeInOutCubic}, {"easeInQuart", easeInQuart}, {"easeOutQuart", easeOutQuart}, {"easeInOutQuart", easeInOutQuart}, {"easeInSine", easeInSine}, {"easeOutSine", easeOutSine}, {"easeInOutSine", easeInOutSine}, {"easeInExpo", easeInExpo}, {"easeOutExpo", easeOutExpo}, {"easeInOutExpo", easeInOutExpo}, {"easeInCirc", easeInCirc}, {"easeOutCirc", easeOutCirc}, {"easeInOutCirc", easeInOutCirc}, {"easeInElastic", easeInElastic}, {"easeOutElastic", easeOutElastic}, {"easeInOutElastic", easeInOutElastic}, {"easeInBack", easeInBack}, {"easeOutBack", easeOutBack}, {"easeInOutBack", easeInOutBack}, {"easeInBounce", easeInBounce}, {"easeOutBounce", easeOutBounce}, {"easeInOutBounce", easeInOutBounce} }; auto it = easingMap.find(name); if (it != easingMap.end()) { return it->second; } return linear; // Default to linear } } // namespace EasingFunctions // AnimationManager implementation AnimationManager& AnimationManager::getInstance() { static AnimationManager instance; return instance; } void AnimationManager::addAnimation(std::shared_ptr animation) { activeAnimations.push_back(animation); } void AnimationManager::update(float deltaTime) { for (auto& anim : activeAnimations) { anim->update(deltaTime); } cleanup(); } void AnimationManager::cleanup() { activeAnimations.erase( std::remove_if(activeAnimations.begin(), activeAnimations.end(), [](const std::shared_ptr& anim) { return anim->isComplete(); }), activeAnimations.end() ); } void AnimationManager::clear() { activeAnimations.clear(); }