123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "UiFlipbookAnimationComponent.h"
- #include <AzCore/Serialization/EditContext.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Component/ComponentApplicationBus.h>
- #include <AzCore/Component/Entity.h>
- #include <AzCore/RTTI/BehaviorContext.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <LyShine/Bus/UiGameEntityContextBus.h>
- #include <LyShine/Bus/UiElementBus.h>
- #include <LyShine/Bus/UiImageBus.h>
- #include <LyShine/Bus/UiIndexableImageBus.h>
- #include <LyShine/UiSerializeHelpers.h>
- namespace
- {
- const char* notConfiguredMessage = "<Spritesheet/image index unavailable>";
- //! Renames the float field "Frame Delay" to "Framerate" (as of V3).
- bool ConvertFrameDelayToFramerate(
- AZ::SerializeContext& context,
- AZ::SerializeContext::DataElementNode& classElement)
- {
- int index = classElement.FindElement(AZ_CRC_CE("Frame Delay"));
- if (index != -1)
- {
- AZ::SerializeContext::DataElementNode& frameDelayNode = classElement.GetSubElement(index);
- float frameDelayValue = 0;
- if (!frameDelayNode.GetData<float>(frameDelayValue))
- {
- AZ_Error("Serialization", false, "Element Frame Delay is not a float.");
- return false;
- }
- // remove the FrameDelay node
- classElement.RemoveElement(index);
- // If Framerate doesn't exist yet, add it
- index = classElement.FindElement(AZ_CRC_CE("Framerate"));
- if (index == -1)
- {
- index = classElement.AddElement<float>(context, "Framerate");
- if (index == -1)
- {
- // Error adding the new sub element
- AZ_Error("Serialization", false, "Failed to create Framerate node");
- return false;
- }
- }
- // Finally, set the framerate to be the same value as the frame delay
- AZ::SerializeContext::DataElementNode& framerateNode = classElement.GetSubElement(index);
- if (!framerateNode.SetData<float>(context, frameDelayValue))
- {
- AZ_Error("Serialization", false, "Unable to set Framerate to legacy Frame Delay value (%.2f).", frameDelayValue);
- return false;
- }
- }
- return true;
- }
- //! Convert legacy components to use seconds-per-frame as default time unit for playback.
- //!
- //! Prior to V3, default unit of time for playback was seconds-per-frame.
- bool ConvertFramerateUnitToSeconds(
- AZ::SerializeContext& context,
- AZ::SerializeContext::DataElementNode& classElement)
- {
- // If Framerate Unit doesn't exist yet, add it
- int index = classElement.FindElement(AZ_CRC_CE("Framerate Unit"));
- if (index == -1)
- {
- index = classElement.AddElement<int>(context, "Framerate Unit");
- if (index == -1)
- {
- // Error adding the new sub element
- AZ_Error("Serialization", false, "Failed to create Framerate Unit node");
- return false;
- }
- }
- // Set the framerate unit to seconds for legacy reasons (FPS is default for newer versions of this component)
- AZ::SerializeContext::DataElementNode& framerateUnitNode = classElement.GetSubElement(index);
- const int secondsEnumVal = static_cast<int>(UiFlipbookAnimationInterface::FramerateUnits::SecondsPerFrame);
- if (!framerateUnitNode.SetData<int>(context, secondsEnumVal))
- {
- AZ_Error("Serialization", false, "Unable to set Framerate Unit to seconds (%d).", secondsEnumVal);
- return false;
- }
- return true;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- //! Forwards events to Lua for UiFlipbookAnimationNotificationsBus
- class UiFlipbookAnimationNotificationsBusBehaviorHandler
- : public UiFlipbookAnimationNotificationsBus::Handler
- , public AZ::BehaviorEBusHandler
- {
- public:
- AZ_EBUS_BEHAVIOR_BINDER(UiFlipbookAnimationNotificationsBusBehaviorHandler, "{0A92A44E-0C32-4AD6-9C49-222A484B54FF}", AZ::SystemAllocator,
- OnAnimationStarted, OnAnimationStopped, OnLoopSequenceCompleted);
- void OnAnimationStarted() override
- {
- Call(FN_OnAnimationStarted);
- }
- void OnAnimationStopped() override
- {
- Call(FN_OnAnimationStopped);
- }
- void OnLoopSequenceCompleted() override
- {
- Call(FN_OnLoopSequenceCompleted);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- static bool UiFlipbookAnimationComponentVersionConverter(AZ::SerializeContext& context,
- AZ::SerializeContext::DataElementNode& classElement)
- {
- // conversion from version 2:
- // - Rename "frame delay" to "framerate"
- // - Set "framerate unit" to seconds (default moving forward is FPS, but we use seconds for legacy compatibility)
- if (classElement.GetVersion() <= 2)
- {
- if (!ConvertFrameDelayToFramerate(context, classElement))
- {
- return false;
- }
- if (!ConvertFramerateUnitToSeconds(context, classElement))
- {
- return false;
- }
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Reflect(AZ::ReflectContext* context)
- {
- AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
- if (serializeContext)
- {
- serializeContext->Class<UiFlipbookAnimationComponent, AZ::Component>()
- ->Version(3, &UiFlipbookAnimationComponentVersionConverter)
- ->Field("Start Frame", &UiFlipbookAnimationComponent::m_startFrame)
- ->Field("End Frame", &UiFlipbookAnimationComponent::m_endFrame)
- ->Field("Loop Start Frame", &UiFlipbookAnimationComponent::m_loopStartFrame)
- ->Field("Loop Type", &UiFlipbookAnimationComponent::m_loopType)
- ->Field("Framerate Unit", &UiFlipbookAnimationComponent::m_framerateUnit)
- ->Field("Framerate", &UiFlipbookAnimationComponent::m_framerate)
- ->Field("Start Delay", &UiFlipbookAnimationComponent::m_startDelay)
- ->Field("Loop Delay", &UiFlipbookAnimationComponent::m_loopDelay)
- ->Field("Reverse Delay", &UiFlipbookAnimationComponent::m_reverseDelay)
- ->Field("Auto Play", &UiFlipbookAnimationComponent::m_isAutoPlay)
- ;
- AZ::EditContext* editContext = serializeContext->GetEditContext();
- if (editContext)
- {
- auto editInfo = editContext->Class<UiFlipbookAnimationComponent>("FlipbookAnimation",
- "Animates image sequences or images configured as sprite sheets.");
- editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::Category, "UI")
- ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Flipbook.png")
- ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Flipbook.svg")
- ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("UI"))
- ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
- ;
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiFlipbookAnimationComponent::m_startFrame, "Start frame", "Frame to start at")
- ->Attribute("EnumValues", &UiFlipbookAnimationComponent::PopulateIndexStringList)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiFlipbookAnimationComponent::OnStartFrameChange)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
- ;
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiFlipbookAnimationComponent::m_endFrame, "End frame", "Frame to end at")
- ->Attribute("EnumValues", &UiFlipbookAnimationComponent::PopulateIndexStringList)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiFlipbookAnimationComponent::OnEndFrameChange)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
- ;
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiFlipbookAnimationComponent::m_loopStartFrame, "Loop start frame", "Frame to start looping from")
- ->Attribute("EnumValues", &UiFlipbookAnimationComponent::PopulateConstrainedIndexStringList)
- ;
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiFlipbookAnimationComponent::m_loopType, "Loop type", "Go from start to end continuously or start to end and back to start")
- ->EnumAttribute(UiFlipbookAnimationInterface::LoopType::None, "None")
- ->EnumAttribute(UiFlipbookAnimationInterface::LoopType::Linear, "Linear")
- ->EnumAttribute(UiFlipbookAnimationInterface::LoopType::PingPong, "PingPong")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"))
- ;
- editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiFlipbookAnimationComponent::m_framerateUnit, "Framerate unit", "Unit of measurement for framerate")
- ->EnumAttribute(UiFlipbookAnimationInterface::FramerateUnits::FPS, "FPS")
- ->EnumAttribute(UiFlipbookAnimationInterface::FramerateUnits::SecondsPerFrame, "Seconds Per Frame")
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiFlipbookAnimationComponent::OnFramerateUnitChange)
- ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"))
- ;
- editInfo->DataElement(0, &UiFlipbookAnimationComponent::m_framerate, "Framerate", "Determines transition speed between frames")
- ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Constants::FloatMax)
- ;
- editInfo->DataElement(0, &UiFlipbookAnimationComponent::m_startDelay, "Start delay", "Number of seconds to wait before playing the flipbook (applied only once).")
- ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Constants::FloatMax)
- ;
- editInfo->DataElement(0, &UiFlipbookAnimationComponent::m_loopDelay, "Loop delay", "Number of seconds to delay until the loop sequence plays")
- ->Attribute(AZ::Edit::Attributes::Visibility, &UiFlipbookAnimationComponent::IsLoopingType)
- ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Constants::FloatMax)
- ;
- editInfo->DataElement(0, &UiFlipbookAnimationComponent::m_reverseDelay, "Reverse delay", "Number of seconds to delay until the reverse sequence plays (PingPong loop types only)")
- ->Attribute(AZ::Edit::Attributes::Visibility, &UiFlipbookAnimationComponent::IsPingPongLoopType)
- ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
- ->Attribute(AZ::Edit::Attributes::Max, AZ::Constants::FloatMax)
- ;
- editInfo->DataElement(0, &UiFlipbookAnimationComponent::m_isAutoPlay, "Auto Play", "Automatically starts playing the animation")
- ;
- }
- }
- AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
- if (behaviorContext)
- {
- behaviorContext->EBus<UiFlipbookAnimationBus>("UiFlipbookAnimationBus")
- ->Event("Start", &UiFlipbookAnimationBus::Events::Start)
- ->Event("Stop", &UiFlipbookAnimationBus::Events::Stop)
- ->Event("IsPlaying", &UiFlipbookAnimationBus::Events::IsPlaying)
- ->Event("GetStartFrame", &UiFlipbookAnimationBus::Events::GetStartFrame)
- ->Event("SetStartFrame", &UiFlipbookAnimationBus::Events::SetStartFrame)
- ->Event("GetEndFrame", &UiFlipbookAnimationBus::Events::GetEndFrame)
- ->Event("SetEndFrame", &UiFlipbookAnimationBus::Events::SetEndFrame)
- ->Event("GetCurrentFrame", &UiFlipbookAnimationBus::Events::GetCurrentFrame)
- ->Event("SetCurrentFrame", &UiFlipbookAnimationBus::Events::SetCurrentFrame)
- ->Event("GetLoopStartFrame", &UiFlipbookAnimationBus::Events::GetLoopStartFrame)
- ->Event("SetLoopStartFrame", &UiFlipbookAnimationBus::Events::SetLoopStartFrame)
- ->Event("GetLoopType", &UiFlipbookAnimationBus::Events::GetLoopType)
- ->Event("SetLoopType", &UiFlipbookAnimationBus::Events::SetLoopType)
- ->Event("GetFramerate", &UiFlipbookAnimationBus::Events::GetFramerate)
- ->Event("SetFramerate", &UiFlipbookAnimationBus::Events::SetFramerate)
- ->Event("GetFramerateUnit", &UiFlipbookAnimationBus::Events::GetFramerateUnit)
- ->Event("SetFramerateUnit", &UiFlipbookAnimationBus::Events::SetFramerateUnit)
- ->Event("GetStartDelay", &UiFlipbookAnimationBus::Events::GetStartDelay)
- ->Event("SetStartDelay", &UiFlipbookAnimationBus::Events::SetStartDelay)
- ->Event("GetLoopDelay", &UiFlipbookAnimationBus::Events::GetLoopDelay)
- ->Event("SetLoopDelay", &UiFlipbookAnimationBus::Events::SetLoopDelay)
- ->Event("GetReverseDelay", &UiFlipbookAnimationBus::Events::GetReverseDelay)
- ->Event("SetReverseDelay", &UiFlipbookAnimationBus::Events::SetReverseDelay)
- ->Event("GetIsAutoPlay", &UiFlipbookAnimationBus::Events::GetIsAutoPlay)
- ->Event("SetIsAutoPlay", &UiFlipbookAnimationBus::Events::SetIsAutoPlay)
- ;
- behaviorContext->EBus<UiFlipbookAnimationNotificationsBus>("UiFlipbookAnimationNotificationsBus")
- ->Handler<UiFlipbookAnimationNotificationsBusBehaviorHandler>()
- ;
- behaviorContext->Enum<(int)UiFlipbookAnimationInterface::LoopType::None>("eUiFlipbookAnimationLoopType_None")
- ->Enum<(int)UiFlipbookAnimationInterface::LoopType::Linear>("eUiFlipbookAnimationLoopType_Linear")
- ->Enum<(int)UiFlipbookAnimationInterface::LoopType::PingPong>("eUiFlipbookAnimationLoopType_PingPong")
- ;
- behaviorContext->Enum<(int)UiFlipbookAnimationInterface::FramerateUnits::FPS>("eUiFlipbookAnimationFramerateUnits_FPS")
- ->Enum<(int)UiFlipbookAnimationInterface::FramerateUnits::SecondsPerFrame>("eUiFlipbookAnimationFramerateUnits_SecondsPerFrame")
- ;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- AZ::u32 UiFlipbookAnimationComponent::GetMaxFrame() const
- {
- AZ::u32 numImageIndices = 0;
- UiIndexableImageBus::EventResult(numImageIndices, GetEntityId(), &UiIndexableImageBus::Events::GetImageIndexCount);
- return numImageIndices;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiFlipbookAnimationComponent::FrameWithinRange(AZ::u32 frameValue)
- {
- AZ::u32 maxFrame = GetMaxFrame();
- return maxFrame > 0 && frameValue < maxFrame;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::AZu32ComboBoxVec UiFlipbookAnimationComponent::PopulateIndexStringList() const
- {
- AZ::u32 numFrames = GetMaxFrame();
- if (numFrames > 0)
- {
- return LyShine::GetEnumSpriteIndexList(GetEntityId(), 0, numFrames - 1);
- }
-
- // Add an empty element to prevent an AzToolsFramework warning that fires
- // when an empty container is encountered.
- LyShine::AZu32ComboBoxVec comboBoxVec;
- comboBoxVec.push_back(AZStd::make_pair(0, notConfiguredMessage));
- return comboBoxVec;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- LyShine::AZu32ComboBoxVec UiFlipbookAnimationComponent::PopulateConstrainedIndexStringList() const
- {
- const char* errorMessage = notConfiguredMessage;
- AZ::u32 indexCount = GetMaxFrame();
- const bool isIndexedImage = indexCount > 1;
- if (isIndexedImage)
- {
- errorMessage = "<Invalid loop range>";
- }
- return LyShine::GetEnumSpriteIndexList(GetEntityId(), m_startFrame, m_endFrame, errorMessage);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::OnStartFrameChange()
- {
- m_endFrame = AZ::GetMax<AZ::u32>(m_startFrame, m_endFrame);
- m_currentFrame = AZ::GetClamp<AZ::u32>(m_currentFrame, m_startFrame, m_endFrame);
- m_loopStartFrame = AZ::GetClamp<AZ::u32>(m_loopStartFrame, m_startFrame, m_endFrame);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::OnEndFrameChange()
- {
- m_startFrame = AZ::GetMin<AZ::u32>(m_startFrame, m_endFrame);
- m_currentFrame = AZ::GetClamp<AZ::u32>(m_currentFrame, m_startFrame, m_endFrame);
- m_loopStartFrame = AZ::GetClamp<AZ::u32>(m_loopStartFrame, m_startFrame, m_endFrame);
- }
- void UiFlipbookAnimationComponent::OnFramerateUnitChange()
- {
- AZ_Assert(m_framerateUnit == FramerateUnits::FPS || m_framerateUnit == FramerateUnits::SecondsPerFrame,
- "New framerate unit added for flipbooks - please update this function accordingly!");
- m_framerate = m_framerate != 0.0f ? 1.0f / m_framerate : 0.0f;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiFlipbookAnimationComponent::IsPingPongLoopType() const
- {
- return m_loopType == LoopType::PingPong;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- bool UiFlipbookAnimationComponent::IsLoopingType() const
- {
- return m_loopType != LoopType::None;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- float UiFlipbookAnimationComponent::CalculateLoopDelay() const
- {
- float loopDelay = 0.0f;
- if (IsLoopingType())
- {
- const bool isStartFrame = m_currentFrame == m_loopStartFrame;
- const bool playingIntro = m_prevFrame < m_currentFrame && m_startFrame != m_loopStartFrame;
- const bool shouldApplyStartLoopDelay = isStartFrame && !playingIntro;
- if (shouldApplyStartLoopDelay)
- {
- loopDelay = m_loopDelay;
- }
- else if (m_loopType == LoopType::PingPong)
- {
- const bool isEndFrame = m_currentFrame == m_endFrame;
- const bool isPlayingReverse = m_currentLoopDirection < 0;
- const bool shouldApplyReverseDelay = isEndFrame && isPlayingReverse;
- if (shouldApplyReverseDelay)
- {
- loopDelay = m_reverseDelay;
- }
- }
- }
- return loopDelay;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Activate()
- {
- UiFlipbookAnimationBus::Handler::BusConnect(GetEntityId());
- UiInitializationBus::Handler::BusConnect(GetEntityId());
- UiSpriteSourceNotificationBus::Handler::BusConnect(GetEntityId());
- if (m_isPlaying)
- {
- // this is unlikely but possible. To get here a client would have to start the flipbook
- // playing and then deactivate and reactivate (e.g. add a component).
- AZ::EntityId canvasEntityId;
- UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
- if (canvasEntityId.IsValid())
- {
- UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Deactivate()
- {
- UiFlipbookAnimationBus::Handler::BusDisconnect();
- UiInitializationBus::Handler::BusDisconnect();
- UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
- UiSpriteSourceNotificationBus::Handler::BusDisconnect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Update(float deltaTime)
- {
- if (m_isPlaying)
- {
- m_elapsedTime += deltaTime;
- if (m_useStartDelay)
- {
- if (m_elapsedTime >= m_startDelay)
- {
- m_useStartDelay = false;
- m_elapsedTime = 0.0f;
- UiIndexableImageBus::Event(GetEntityId(), &UiIndexableImageBus::Events::SetImageIndex, m_currentFrame);
- }
- return;
- }
- const float loopDelay = CalculateLoopDelay();
- // Calculate the frame delay (time to transition to next frame) based on framerate.
- // If framerate is in FPS we convert to seconds-per-frame to test against elapsedTime.
- const float frameDelay = CalculateFramerateAsSecondsPerFrame();
- if (m_elapsedTime >= (frameDelay + loopDelay))
- {
- // Determine the number of frames that has elapsed and adjust
- // "elapsed time" to account for any additional time that has
- // passed given the current delta.
- const float elapsedTimeAfterDelayFrame = m_elapsedTime - (frameDelay + loopDelay);
- const AZ::s32 numFramesElapsed = static_cast<AZ::s32>(1 + (elapsedTimeAfterDelayFrame / frameDelay));
- m_elapsedTime = m_elapsedTime - ((numFramesElapsed * frameDelay) + loopDelay);
- // In case the loop direction is negative, we don't want to
- // subtract from the current frame if its zero.
- m_prevFrame = m_currentFrame;
- const AZ::s32 nextFrameNum = AZ::GetMax<AZ::s32>(0, static_cast<AZ::s32>(m_currentFrame) + numFramesElapsed * m_currentLoopDirection);
- m_currentFrame = static_cast<AZ::u32>(nextFrameNum);
- switch (m_loopType)
- {
- case LoopType::None:
- if (m_currentFrame > m_endFrame)
- {
- m_currentFrame = m_endFrame;
- Stop();
- }
- break;
- case LoopType::Linear:
- if (m_currentFrame > m_endFrame)
- {
- m_currentFrame = m_loopStartFrame;
- UiFlipbookAnimationNotificationsBus::Event(
- GetEntityId(), &UiFlipbookAnimationNotificationsBus::Events::OnLoopSequenceCompleted);
- }
- break;
- case LoopType::PingPong:
- if (m_currentLoopDirection > 0 && m_currentFrame >= m_endFrame)
- {
- m_currentLoopDirection = -1;
- m_currentFrame = m_endFrame;
- UiFlipbookAnimationNotificationsBus::Event(
- GetEntityId(), &UiFlipbookAnimationNotificationsBus::Events::OnLoopSequenceCompleted);
- }
- else if (m_currentLoopDirection < 0 && m_currentFrame <= m_loopStartFrame)
- {
- m_currentLoopDirection = 1;
- m_currentFrame = m_loopStartFrame;
- UiFlipbookAnimationNotificationsBus::Event(
- GetEntityId(), &UiFlipbookAnimationNotificationsBus::Events::OnLoopSequenceCompleted);
- }
- break;
- default:
- break;
- }
- // Show current frame
- UiIndexableImageBus::Event(GetEntityId(), &UiIndexableImageBus::Events::SetImageIndex, m_currentFrame);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::InGamePostActivate()
- {
- if (m_isPlaying)
- {
- // Could get here if Start was called from Lua in the OnActivate function
- if (!UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
- {
- AZ::EntityId canvasEntityId;
- UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
- if (canvasEntityId.IsValid())
- {
- UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
- }
- }
- }
- else if (m_isAutoPlay)
- {
- Start();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Start()
- {
- m_currentFrame = m_startFrame;
- m_currentLoopDirection = 1;
- m_isPlaying = true;
- m_elapsedTime = 0.0f;
- m_useStartDelay = m_startDelay > 0.0f ? true : false;
- // Show current frame
- if (!m_useStartDelay)
- {
- UiIndexableImageBus::Event(GetEntityId(), &UiIndexableImageBus::Events::SetImageIndex, m_currentFrame);
- }
- // Start the update loop
- if (!UiCanvasUpdateNotificationBus::Handler::BusIsConnected())
- {
- AZ::EntityId canvasEntityId;
- UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
- // if this element has not been fixed up yet then canvasEntityId will be invalid. We handle this
- // in InGamePostActivate
- if (canvasEntityId.IsValid())
- {
- UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
- }
- }
- // Let listeners know that we started playing
- UiFlipbookAnimationNotificationsBus::Event(GetEntityId(), &UiFlipbookAnimationNotificationsBus::Events::OnAnimationStarted);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::Stop()
- {
- m_isPlaying = false;
- UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
- UiFlipbookAnimationNotificationsBus::Event(GetEntityId(), &UiFlipbookAnimationNotificationsBus::Events::OnAnimationStopped);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::SetStartFrame(AZ::u32 startFrame)
- {
- if (!FrameWithinRange(startFrame))
- {
- AZ_Warning("UI", false, "Invalid frame value given: %u", startFrame);
- return;
- }
- m_startFrame = startFrame;
- OnStartFrameChange();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::SetEndFrame(AZ::u32 endFrame)
- {
- if (!FrameWithinRange(endFrame))
- {
- AZ_Warning("UI", false, "Invalid frame value given: %u", endFrame);
- return;
- }
- m_endFrame = endFrame;
- OnEndFrameChange();
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::SetCurrentFrame(AZ::u32 currentFrame)
- {
- // The current frame needs to stay between the start and end frames
- const bool validFrameValue = currentFrame >= m_startFrame && currentFrame <= m_endFrame;
- if (!validFrameValue)
- {
- AZ_Warning("UI", false, "Invalid frame value given: %u", currentFrame);
- return;
- }
- m_currentFrame = currentFrame;
- UiIndexableImageBus::Event(GetEntityId(), &UiIndexableImageBus::Events::SetImageIndex, m_currentFrame);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::SetLoopStartFrame(AZ::u32 loopStartFrame)
- {
- // Ensure that loop start frame exists within start and end frame range
- const bool validFrameValue = loopStartFrame >= m_startFrame && loopStartFrame <= m_endFrame;
- if (!validFrameValue)
- {
- AZ_Warning("UI", false, "Invalid frame value given: %u", loopStartFrame);
- return;
- }
- m_loopStartFrame = loopStartFrame;
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::SetLoopType(UiFlipbookAnimationInterface::LoopType loopType)
- {
- m_loopType = loopType;
- // PingPong is currently the only loop type that supports a negative loop
- // direction.
- if (m_loopType != LoopType::PingPong)
- {
- m_currentLoopDirection = 1;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////
- void UiFlipbookAnimationComponent::OnSpriteSourceChanged()
- {
- AZ::u32 indexCount = GetMaxFrame();
- const AZ::u32 newStartFrame = AZ::GetClamp<AZ::u32>(m_startFrame, 0, indexCount - 1);
- const AZ::u32 newEndFrame = AZ::GetClamp<AZ::u32>(m_endFrame, 0, indexCount - 1);
- const bool frameRangesChanged = newStartFrame != m_startFrame || newEndFrame != m_endFrame;
- if (frameRangesChanged)
- {
- m_startFrame = newStartFrame;
- m_endFrame = newEndFrame;
- OnStartFrameChange();
- OnEndFrameChange();
- }
- }
|