// IAEngine: 2D Game Engine by IA // Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include namespace ia::iae { SpriteComponent::SpriteComponent(IN Node2D *node) : TextureComponent(node) { } VOID SpriteComponent::Draw() { const auto &animFrame = m_currentAnimationState; PositionOffset() = animFrame.Position; ScaleOffset() = animFrame.Scale; RotationOffset() = animFrame.Rotation; ColorOverlay() = animFrame.ColorOverlay; SetTexture(animFrame.Texture); TextureComponent::Draw(); } VOID SpriteComponent::DebugDraw() { TextureComponent::DebugDraw(); } VOID SpriteComponent::Update() { TextureComponent::Update(); UpdateAnimation(); } VOID SpriteComponent::FixedUpdate() { TextureComponent::FixedUpdate(); } Handle SpriteComponent::AddAnimation(IN CONST Animation &animation) { if (animation.Keys.empty()) return INVALID_HANDLE; m_animations.pushBack(animation); if(m_animations.size() == 1) ForceSetActiveAnimation(0); return m_animations.size() - 1; } VOID SpriteComponent::SetActiveAnimation(IN Handle animation) { if (animation == m_activeAnimationHandle) return; ForceSetActiveAnimation(animation); } VOID SpriteComponent::ForceSetActiveAnimation(IN Handle animation) { IA_RELEASE_ASSERT((animation != INVALID_HANDLE) && (animation < m_animations.size())); m_prevAnimationKeyFrameIndex = 0; m_activeAnimation = m_animations[animation]; m_prevAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 0); m_nextAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 1); m_currentAnimationState = m_prevAnimationKeyFrame; m_activeAnimationHandle = animation; SetTexture(m_currentAnimationState.Texture); } VOID SpriteComponent::SetAnimationFrame(IN INT32 animation, IN INT32 frame) { m_activeAnimation = {}; m_currentAnimationState = m_animations[animation].Keys[frame]; } VOID SpriteComponent::UpdateAnimation() { const auto keyCount = m_activeAnimation.Keys.size(); if (keyCount <= 1) return; if (m_currentAnimationState.ShouldInterpolate) { const auto t = m_timelinePosition / m_prevAnimationKeyFrame.Duration; #define INTERP_PROPERTY(name) \ m_currentAnimationState.name = \ m_prevAnimationKeyFrame.name + (m_nextAnimationKeyFrame.name - m_prevAnimationKeyFrame.name) * t; INTERP_PROPERTY(Position); INTERP_PROPERTY(Rotation); INTERP_PROPERTY(Scale); INTERP_PROPERTY(ColorOverlay); #undef INTERP_PROPERTY } if (m_timelinePosition >= m_prevAnimationKeyFrame.Duration) { m_prevAnimationKeyFrameIndex = (m_prevAnimationKeyFrameIndex + 1) % keyCount; if (!m_prevAnimationKeyFrameIndex && !m_activeAnimation.ShouldLoop) { m_activeAnimation = {}; return; } m_prevAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 0); m_nextAnimationKeyFrame = GetKeyFrame(m_prevAnimationKeyFrameIndex + 1); m_currentAnimationState = m_prevAnimationKeyFrame; m_timelinePosition = 0; } m_timelinePosition += Engine::GetFrameDeltaTime() * 1000; } SpriteComponent::AnimationKeyFrame SpriteComponent::GetKeyFrame(IN INT32 index) { return m_activeAnimation.Keys[index % m_activeAnimation.Keys.size()]; } } // namespace ia::iae