From e531a087f644b301ea0d09eff6d118664a485b57 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 10 Mar 2023 18:53:40 +0800 Subject: [PATCH 1/3] Added events for play, pause and finished animations --- Assets/Scripts/AnimTest.cs | 13 ++ .../src/Animation/SHAnimationClip.cpp | 3 +- .../src/Animation/SHAnimationController.cpp | 28 +++- .../src/Animation/SHAnimationController.h | 8 +- .../src/Animation/SHAnimationSystem.h | 41 +++++- .../src/Animation/SHAnimatorComponent.cpp | 67 ++++++++- .../src/Animation/SHAnimatorComponent.h | 19 +++ SHADE_Engine/src/Events/SHEventDefines.h | 4 +- SHADE_Engine/src/Scripting/SHScriptEngine.cpp | 91 ++++++++++++ SHADE_Engine/src/Scripting/SHScriptEngine.h | 13 ++ SHADE_Managed/src/Components/Animator.cxx | 138 ++++++++++++++++++ SHADE_Managed/src/Components/Animator.hxx | 129 ++++++++++++++++ SHADE_Managed/src/Components/UIElement.cxx | 29 ++-- SHADE_Managed/src/Engine/EngineInterface.cxx | 2 + 14 files changed, 563 insertions(+), 22 deletions(-) diff --git a/Assets/Scripts/AnimTest.cs b/Assets/Scripts/AnimTest.cs index b0761476..69b47497 100644 --- a/Assets/Scripts/AnimTest.cs +++ b/Assets/Scripts/AnimTest.cs @@ -25,6 +25,10 @@ namespace SHADE.Test protected override void awake() { Animator = GetComponent(); + + Animator.OnClipStartedPlaying.RegisterAction((_) => Debug.Log("Start Playing")); + Animator.OnClipPaused.RegisterAction((_) => Debug.Log("Pause")); + Animator.OnClipFinished.RegisterAction((_) => Debug.Log("Finished")); } protected override void update() @@ -61,6 +65,15 @@ namespace SHADE.Test AnimationSystem.TimeScale = AnimationSystem.TimeScale > 0.0f ? 0.0f : AnimationSystem.DefaultTimeScale; } + + if (Input.GetKeyUp(Input.KeyCode.P)) + { + if (Animator.IsPlaying) + Animator.Pause(); + else + Animator.Play(); + + } } #endregion } diff --git a/SHADE_Engine/src/Animation/SHAnimationClip.cpp b/SHADE_Engine/src/Animation/SHAnimationClip.cpp index b4a62651..2066506f 100644 --- a/SHADE_Engine/src/Animation/SHAnimationClip.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationClip.cpp @@ -31,8 +31,7 @@ namespace SHADE return; const float SECS_PER_TICK = 1.0f / static_cast(rawAnim->GetTicksPerSecond()); - const int ONE_PAST_LAST_FRAME = lastFrame + 1; - duration = static_cast(ONE_PAST_LAST_FRAME - firstFrame) * SECS_PER_TICK; + duration = static_cast(lastFrame - firstFrame) * SECS_PER_TICK; startTimeStamp = static_cast(firstFrame) * SECS_PER_TICK; } } \ No newline at end of file diff --git a/SHADE_Engine/src/Animation/SHAnimationController.cpp b/SHADE_Engine/src/Animation/SHAnimationController.cpp index 8152426f..3c04614a 100644 --- a/SHADE_Engine/src/Animation/SHAnimationController.cpp +++ b/SHADE_Engine/src/Animation/SHAnimationController.cpp @@ -15,6 +15,7 @@ of DigiPen Institute of Technology is prohibited. #include "SHAnimationSystem.h" #include "ECS_Base/Managers/SHSystemManager.h" #include "SHAnimationClip.h" +#include "Events/SHEventManager.hpp" namespace SHADE { @@ -55,7 +56,7 @@ namespace SHADE /*-----------------------------------------------------------------------------------*/ /* Lifecycle Functions */ /*-----------------------------------------------------------------------------------*/ - void SHAnimationController::Update(InstanceData& instData, float dt) + void SHAnimationController::Update(EntityID eid, InstanceData& instData, float dt) { // Is there a valid node if (!instData.CurrentNode) @@ -72,6 +73,7 @@ namespace SHADE instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp() + instData.CurrentNode->Clip->GetTotalDuration(); // Go to next state + Handle originalClip = instData.CurrentNode->Clip; bool stateChanged = false; for (const auto& transition : instData.CurrentNode->Transitions) { @@ -107,7 +109,29 @@ namespace SHADE } // Handle if there is no next state, we repeat - if (!stateChanged) + if (stateChanged) + { + // Raise events + SHEventManager::BroadcastEvent + ( + SHAnimationSystem::FinishedEvent + { + eid, + originalClip + }, + SH_ANIMATION_FINISHED_EVENT + ); + SHEventManager::BroadcastEvent + ( + SHAnimationSystem::PlayEvent + { + eid, + instData.CurrentNode ? instData.CurrentNode->Clip : Handle() + }, + SH_ANIMATIONS_PLAY_EVENT + ); + } + else { instData.ClipPlaybackTime = instData.CurrentNode->Clip->GetStartTimeStamp(); } diff --git a/SHADE_Engine/src/Animation/SHAnimationController.h b/SHADE_Engine/src/Animation/SHAnimationController.h index 56bf6f45..e664b445 100644 --- a/SHADE_Engine/src/Animation/SHAnimationController.h +++ b/SHADE_Engine/src/Animation/SHAnimationController.h @@ -18,6 +18,7 @@ of DigiPen Institute of Technology is prohibited. // Project Includes #include "SH_API.h" #include "Resource/SHHandle.h" +#include "ECS_Base/SHECSMacros.h" namespace SHADE { @@ -162,7 +163,12 @@ namespace SHADE /// /// Runs a single update for the animation controller. /// - void Update(InstanceData& instData, float dt); + /// + /// EntityID of the entity that this animation controller is updating. + /// + /// Instance data that stores the current state. + /// Frame time. + void Update(EntityID eid, InstanceData& instData, float dt); /*---------------------------------------------------------------------------------*/ /* Usage Functions */ diff --git a/SHADE_Engine/src/Animation/SHAnimationSystem.h b/SHADE_Engine/src/Animation/SHAnimationSystem.h index fd972e47..975547c0 100644 --- a/SHADE_Engine/src/Animation/SHAnimationSystem.h +++ b/SHADE_Engine/src/Animation/SHAnimationSystem.h @@ -19,6 +19,11 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /*-----------------------------------------------------------------------------------*/ + /* Forward Declaration */ + /*-----------------------------------------------------------------------------------*/ + class SHAnimationClip; + /*-----------------------------------------------------------------------------------*/ /* Type Definitions */ /*-----------------------------------------------------------------------------------*/ @@ -29,7 +34,7 @@ namespace SHADE { public: /*---------------------------------------------------------------------------------*/ - /* Type Definitions */ + /* Type Definitions - System Routines */ /*---------------------------------------------------------------------------------*/ /// /// Responsible for updating the playback of all animator components and computing @@ -42,6 +47,40 @@ namespace SHADE void Execute(double dt) noexcept override final; }; + /*---------------------------------------------------------------------------------*/ + /* Type Definitions - Event Data */ + /*---------------------------------------------------------------------------------*/ + /// + /// Event data for the SH_ANIMATIONS_PLAY_EVENT event which is raised on the frame + /// that an animation has started playing. + /// + struct PlayEvent + { + EntityID Entity; + Handle PlayingClip; + float CurrentTimeStamp; + }; + /// + /// Event data for the SH_ANIMATIONS_PAUSED_EVENT event which is raised on the frame + /// that an animation is paused. + /// + struct PausedEvent + { + EntityID Entity; + Handle PausedClip; + float PauseTimeStamp; + }; + /// + /// Event data for the SH_ANIMATIONS_FINISHED_EVENT event which is raised on the + /// frame that an animation has finished playing. This will not be called for any + /// animation that loops. + /// + struct FinishedEvent + { + EntityID Entity; + Handle FinishedClip; + }; + /*---------------------------------------------------------------------------------*/ /* Constants */ /*---------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index d21ee5e1..fb866cbb 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -25,6 +25,7 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/Managers/SHSystemManager.h" #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" #include "Tools/SHDebugDraw.h" +#include "Events/SHEventManager.hpp" namespace SHADE { @@ -35,6 +36,8 @@ namespace SHADE { isPlaying = true; playOnce = false; + + raisePlayEvent(); } void SHAnimatorComponent::Play(Handle clip) @@ -91,11 +94,13 @@ namespace SHADE isPlaying = true; currPlaybackTime = 0.0f; + raisePlayEvent(); } void SHAnimatorComponent::Pause() { isPlaying = false; + raisePauseEvent(); } void SHAnimatorComponent::Stop() @@ -210,7 +215,20 @@ namespace SHADE return animController->SetTrigger(animInstanceData, paramName); } - + + /*-----------------------------------------------------------------------------------*/ + /* Getter Functions */ + /*-----------------------------------------------------------------------------------*/ + Handle SHAnimatorComponent::GetCurrentClip() const noexcept + { + if (animController) + { + return animInstanceData.CurrentNode ? animInstanceData.CurrentNode->Clip : Handle(); + } + + return currClip; + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions - Update */ /*-----------------------------------------------------------------------------------*/ @@ -221,7 +239,7 @@ namespace SHADE return; // Update the animation controller - animController->Update(animInstanceData, dt); + animController->Update(GetEID(), animInstanceData, dt); // Get current clip currClip = animInstanceData.CurrentNode->Clip; @@ -241,6 +259,7 @@ namespace SHADE playOnce = false; isPlaying = false; currPlaybackTime = currClip->GetStartTimeStamp() + currClip->GetTotalDuration(); + raiseFinishEvent(); } else { @@ -299,6 +318,50 @@ namespace SHADE updatePoseWithClip(poseTime, rawAnimData, child, transformMatrix); } } + + /*-----------------------------------------------------------------------------------*/ + /* Helper Functions - Event */ + /*-----------------------------------------------------------------------------------*/ + void SHAnimatorComponent::raisePlayEvent() + { + SHEventManager::BroadcastEvent + ( + SHAnimationSystem::PlayEvent + { + GetEID(), + GetCurrentClip(), + GetCurrentClipPlaybackTime() + }, + SH_ANIMATIONS_PLAY_EVENT + ); + } + + void SHAnimatorComponent::raisePauseEvent() + { + SHEventManager::BroadcastEvent + ( + SHAnimationSystem::PausedEvent + { + GetEID(), + GetCurrentClip(), + GetCurrentClipPlaybackTime() + }, + SH_ANIMATIONS_PAUSED_EVENT + ); + } + + void SHAnimatorComponent::raiseFinishEvent() + { + SHEventManager::BroadcastEvent + ( + SHAnimationSystem::FinishedEvent + { + GetEID(), + GetCurrentClip() + }, + SH_ANIMATION_FINISHED_EVENT + ); + } } /*-------------------------------------------------------------------------------------*/ diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.h b/SHADE_Engine/src/Animation/SHAnimatorComponent.h index 01e50861..ba18bf71 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.h +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.h @@ -24,6 +24,7 @@ of DigiPen Institute of Technology is prohibited. #include "Math/SHQuaternion.h" #include "SHRawAnimation.h" #include "SHAnimationController.h" +#include "SHAnimationSystem.h" namespace SHADE { @@ -166,6 +167,20 @@ namespace SHADE /// /// Handle GetAnimationController() const noexcept { return animController; } + /// + /// Retrieves the currently playing clip. If there is an Animation Controller, it + /// will be retrieved from it. Otherwise, the currently assigned Animation Clip is provided. + /// + /// Handle to the currently playing Animation Clip if any. + Handle GetCurrentClip() const noexcept; + /// + /// Retrieves the current timestamp of the currently playing clip. This is relative + /// to the start frame of the Animation Clip. This function will retrieve the correct + /// playback time depending on the current playback mode (Animation Controller or + /// Manual). + /// + /// Time in seconds of the current timestamp of the current clip. + float GetCurrentClipPlaybackTime() const noexcept { return animController ? animInstanceData.ClipPlaybackTime : currPlaybackTime; } private: /*---------------------------------------------------------------------------------*/ @@ -201,6 +216,10 @@ namespace SHADE void updatePoseWithClip(float poseTime, Handle rawAnimData, Handle node, const SHMatrix& parentMatrix); template T getInterpolatedValue(const std::vector>& keyframes, float poseTime); + // Event Functions + void raisePlayEvent(); + void raisePauseEvent(); + void raiseFinishEvent(); /*---------------------------------------------------------------------------------*/ /* RTTR */ diff --git a/SHADE_Engine/src/Events/SHEventDefines.h b/SHADE_Engine/src/Events/SHEventDefines.h index cdef68b9..2606370f 100644 --- a/SHADE_Engine/src/Events/SHEventDefines.h +++ b/SHADE_Engine/src/Events/SHEventDefines.h @@ -31,4 +31,6 @@ constexpr SHEventIdentifier SH_BUTTON_HOVER_ENTER_EVENT { 22 }; constexpr SHEventIdentifier SH_BUTTON_HOVER_EXIT_EVENT { 23 }; constexpr SHEventIdentifier SH_ASSET_COMPILE_EVENT { 24 }; constexpr SHEventIdentifier SH_GRAPHICS_LIGHT_DELETE_EVENT { 25 }; - +constexpr SHEventIdentifier SH_ANIMATIONS_PAUSED_EVENT { 26 }; +constexpr SHEventIdentifier SH_ANIMATIONS_PLAY_EVENT { 27 }; +constexpr SHEventIdentifier SH_ANIMATION_FINISHED_EVENT { 28 }; diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp index bd2cfea5..3c36f998 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.cpp +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.cpp @@ -34,6 +34,8 @@ of DigiPen Institute of Technology is prohibited. #include "UI/Events/SHButtonClickEvent.h" #include "UI/SHUIComponent.h" #include "Editor/EditorWindow/MenuBar/SHEditorMenuBar.h" +#include "Animation/SHAnimatorComponent.h" +#include "Resource/SHResourceManager.h" namespace SHADE { @@ -412,6 +414,49 @@ namespace SHADE return eventData->handle; } + SHEventHandle SHScriptEngine::onAnimatorRemoved(SHEventPtr eventPtr) + { + auto eventData = reinterpret_cast*>(eventPtr.get()); + if (eventData->data->removedComponentType == ComponentFamily::GetID()) + csAnimatorOnRemoved(eventData->data->eid); + return eventData->handle; + } + + SHEventHandle SHScriptEngine::onAnimatorClipStartedPlaying(SHEventPtr eventPtr) + { + auto eventData = reinterpret_cast*>(eventPtr.get()); + csAnimatorOnClipStartPlaying + ( + eventData->data->Entity, + SHResourceManager::GetAssetID(eventData->data->PlayingClip).value_or(INVALID_ASSET_ID), + eventData->data->CurrentTimeStamp + ); + return eventData->handle; + } + + SHEventHandle SHScriptEngine::onAnimatorClipPaused(SHEventPtr eventPtr) + { + auto eventData = reinterpret_cast*>(eventPtr.get()); + csAnimatorOnClipPaused + ( + eventData->data->Entity, + SHResourceManager::GetAssetID(eventData->data->PausedClip).value_or(INVALID_ASSET_ID), + eventData->data->PauseTimeStamp + ); + return eventData->handle; + } + + SHEventHandle SHScriptEngine::onAnimatorClipFinished(SHEventPtr eventPtr) + { + auto eventData = reinterpret_cast*>(eventPtr.get()); + csAnimatorOnClipFinished + ( + eventData->data->Entity, + SHResourceManager::GetAssetID(eventData->data->FinishedClip).value_or(INVALID_ASSET_ID) + ); + return eventData->handle; + } + /*-----------------------------------------------------------------------------------*/ /* Helper Functions */ /*-----------------------------------------------------------------------------------*/ @@ -578,6 +623,30 @@ namespace SHADE DEFAULT_CSHARP_NAMESPACE + ".UIElement", "OnHoverExited" ); + csAnimatorOnRemoved = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Animator", + "OnComponentRemoved" + ); + csAnimatorOnClipStartPlaying = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Animator", + "RaiseOnClipStartedPlaying" + ); + csAnimatorOnClipPaused = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Animator", + "RaiseOnClipPaused" + ); + csAnimatorOnClipFinished = dotNet.GetFunctionPtr + ( + DEFAULT_CSHARP_LIB_NAME, + DEFAULT_CSHARP_NAMESPACE + ".Animator", + "RaiseOnClipFinished" + ); csEditorRenderScripts = dotNet.GetFunctionPtr ( DEFAULT_CSHARP_LIB_NAME, @@ -663,6 +732,28 @@ namespace SHADE }; SHEventManager::SubscribeTo(SH_BUTTON_HOVER_EXIT_EVENT, std::dynamic_pointer_cast(hoverExitedUIElementEventReceiver)); + /* Animator */ + std::shared_ptr> removedAnimatorEventReceiver + { + std::make_shared>(this, &SHScriptEngine::onAnimatorRemoved) + }; + SHEventManager::SubscribeTo(SH_COMPONENT_REMOVED_EVENT, std::dynamic_pointer_cast(removedAnimatorEventReceiver)); + std::shared_ptr> animStartedPlayingEventReceiver + { + std::make_shared>(this, &SHScriptEngine::onAnimatorClipStartedPlaying) + }; + SHEventManager::SubscribeTo(SH_ANIMATIONS_PLAY_EVENT, std::dynamic_pointer_cast(animStartedPlayingEventReceiver)); + std::shared_ptr> animPausedEventReceiver + { + std::make_shared>(this, &SHScriptEngine::onAnimatorClipPaused) + }; + SHEventManager::SubscribeTo(SH_ANIMATIONS_PAUSED_EVENT, std::dynamic_pointer_cast(animPausedEventReceiver)); + std::shared_ptr> animFinishedEventReceiver + { + std::make_shared>(this, &SHScriptEngine::onAnimatorClipFinished) + }; + SHEventManager::SubscribeTo(SH_ANIMATION_FINISHED_EVENT, std::dynamic_pointer_cast(animFinishedEventReceiver)); + /* SceneGraph */ // Register for SceneNode child added event std::shared_ptr> addChildEventReceiver diff --git a/SHADE_Engine/src/Scripting/SHScriptEngine.h b/SHADE_Engine/src/Scripting/SHScriptEngine.h index b207ae64..1582c082 100644 --- a/SHADE_Engine/src/Scripting/SHScriptEngine.h +++ b/SHADE_Engine/src/Scripting/SHScriptEngine.h @@ -24,6 +24,7 @@ of DigiPen Institute of Technology is prohibited. #include "ECS_Base/System/SHSystemRoutine.h" #include "Events/SHEventDefines.h" #include "Events/SHEvent.h" +#include "Assets/SHAssetMacros.h" namespace SHADE { @@ -251,6 +252,8 @@ namespace SHADE using CsScriptDeserialiseYamlFuncPtr = bool(*)(EntityID, const void*); using CsScriptEditorFuncPtr = void(*)(EntityID); using CsEventRelayFuncPtr = void(*)(EntityID); + using CsEventRelayAnimFuncPtr = void(*)(EntityID, AssetID); + using CsEventRelayAnimTimeFuncPtr = void(*)(EntityID, AssetID, float); /*-----------------------------------------------------------------------------*/ /* Constants */ @@ -295,6 +298,12 @@ namespace SHADE CsEventRelayFuncPtr csUIElementOnReleased = nullptr; CsEventRelayFuncPtr csUIElementOnHoverEntered = nullptr; CsEventRelayFuncPtr csUIElementOnHoverExited = nullptr; + CsEventRelayFuncPtr csAnimatorOnRemoved = nullptr; + CsEventRelayAnimTimeFuncPtr csAnimatorOnClipStartPlaying = nullptr; + CsEventRelayAnimTimeFuncPtr csAnimatorOnClipPaused = nullptr; + CsEventRelayAnimFuncPtr csAnimatorOnClipFinished = nullptr; + + // - Editor CsScriptEditorFuncPtr csEditorRenderScripts = nullptr; CsFuncPtr csEditorUndo = nullptr; @@ -315,6 +324,10 @@ namespace SHADE SHEventHandle onSceneNodeChildrenAdded(SHEventPtr eventPtr); SHEventHandle onSceneNodeChildrenRemoved(SHEventPtr eventPtr); SHEventHandle onSceneDestroyed(SHEventPtr eventPtr); + SHEventHandle onAnimatorRemoved(SHEventPtr eventPtr); + SHEventHandle onAnimatorClipStartedPlaying(SHEventPtr eventPtr); + SHEventHandle onAnimatorClipPaused(SHEventPtr eventPtr); + SHEventHandle onAnimatorClipFinished(SHEventPtr eventPtr); /*-----------------------------------------------------------------------------*/ /* Helper Functions */ diff --git a/SHADE_Managed/src/Components/Animator.cxx b/SHADE_Managed/src/Components/Animator.cxx index 364ce046..f7fb92e3 100644 --- a/SHADE_Managed/src/Components/Animator.cxx +++ b/SHADE_Managed/src/Components/Animator.cxx @@ -17,6 +17,7 @@ of DigiPen Institute of Technology is prohibited. #include "Animator.hxx" #include "Assets/NativeAsset.hxx" #include "Utility/Convert.hxx" +#include "Utility/Debug.hxx" namespace SHADE { @@ -69,6 +70,65 @@ namespace SHADE return Convert::ToCLI(CURR_NODE->Name); return nullptr; } + bool Animator::IsPlaying::get() + { + return GetNativeComponent()->IsPlaying(); + } + float Animator::CurrentClipPlaybackTime::get() + { + return GetNativeComponent()->GetCurrentClipPlaybackTime(); + } + CallbackEvent^ Animator::OnClipStartedPlaying::get() + { + // Create map if it wasn't before + if (onClipStartPlayingEventMap == nullptr) + { + onClipStartPlayingEventMap = gcnew System::Collections::Generic::Dictionary^>(); + } + + // Create event if it wasn't before + if (!onClipStartPlayingEventMap->ContainsKey(owner.EntityId)) + { + onClipStartPlayingEventMap->Add(owner.EntityId, gcnew CallbackEvent()); + } + + // Return the event + return onClipStartPlayingEventMap[owner.EntityId]; + } + CallbackEvent^ Animator::OnClipPaused::get() + { + // Create map if it wasn't before + if (onClipPausedEventMap == nullptr) + { + onClipPausedEventMap = gcnew System::Collections::Generic::Dictionary^>(); + } + + // Create event if it wasn't before + if (!onClipPausedEventMap->ContainsKey(owner.EntityId)) + { + onClipPausedEventMap->Add(owner.EntityId, gcnew CallbackEvent()); + } + + // Return the event + return onClipPausedEventMap[owner.EntityId]; + } + CallbackEvent^ Animator::OnClipFinished::get() + { + // Create map if it wasn't before + if (onClipFinishedEventMap == nullptr) + { + onClipFinishedEventMap = gcnew System::Collections::Generic::Dictionary^>(); + } + + // Create event if it wasn't before + if (!onClipFinishedEventMap->ContainsKey(owner.EntityId)) + { + onClipFinishedEventMap->Add(owner.EntityId, gcnew CallbackEvent()); + } + + // Return the event + return onClipFinishedEventMap[owner.EntityId]; + } /*---------------------------------------------------------------------------------*/ /* Usage Functions */ @@ -156,4 +216,82 @@ namespace SHADE { return GetBoolParameter(paramName); } + + /*---------------------------------------------------------------------------------*/ + /* Static Clear Functions */ + /*---------------------------------------------------------------------------------*/ + void Animator::ClearStaticEventData() + { + if (onClipStartPlayingEventMap != nullptr) + onClipStartPlayingEventMap->Clear(); + if (onClipPausedEventMap != nullptr) + onClipPausedEventMap->Clear(); + if (onClipFinishedEventMap != nullptr) + onClipFinishedEventMap->Clear(); + } + + /*---------------------------------------------------------------------------------*/ + /* Event Handling Functions */ + /*---------------------------------------------------------------------------------*/ + void Animator::OnComponentRemoved(EntityID entity) + { + SAFE_NATIVE_CALL_BEGIN + // Remove the event if it contained an event + if (onClipStartPlayingEventMap != nullptr && onClipStartPlayingEventMap->ContainsKey(entity)) + { + onClipStartPlayingEventMap->Remove(entity); + } + if (onClipPausedEventMap != nullptr && onClipPausedEventMap->ContainsKey(entity)) + { + onClipPausedEventMap->Remove(entity); + } + if (onClipFinishedEventMap != nullptr && onClipFinishedEventMap->ContainsKey(entity)) + { + onClipFinishedEventMap->Remove(entity); + } + SAFE_NATIVE_CALL_END("Animator.OnComponentRemoved") + } + + void Animator::RaiseOnClipStartedPlaying(EntityID entity, AssetID animAssetId, float currTimeStamp) + { + SAFE_NATIVE_CALL_BEGIN + // Remove the event if it contained an event + if (onClipStartPlayingEventMap != nullptr && onClipStartPlayingEventMap->ContainsKey(entity)) + { + ClipStartPlayingEventData data; + data.PlayingClip = AnimationClipAsset(animAssetId); + data.CurrentTimeStamp = currTimeStamp; + onClipStartPlayingEventMap[entity]->Invoke(data); + } + SAFE_NATIVE_CALL_END("Animator.OnClipStartedPlaying") + } + + void Animator::RaiseOnClipPaused(EntityID entity, AssetID animAssetId, float currTimeStamp) + { + SAFE_NATIVE_CALL_BEGIN + // Remove the event if it contained an event + if (onClipPausedEventMap != nullptr && onClipPausedEventMap->ContainsKey(entity)) + { + ClipPausedEventData data; + data.PausedClip = AnimationClipAsset(animAssetId); + data.PauseTimeStamp = currTimeStamp; + onClipPausedEventMap[entity]->Invoke(data); + } + SAFE_NATIVE_CALL_END("Animator.OnClipPaused") + } + + void Animator::RaiseOnClipFinished(EntityID entity, AssetID animAssetId) + { + + SAFE_NATIVE_CALL_BEGIN + // Remove the event if it contained an event + if (onClipFinishedEventMap != nullptr && onClipFinishedEventMap->ContainsKey(entity)) + { + ClipFinishedEventData data; + data.FinishedClip = AnimationClipAsset(animAssetId); + onClipFinishedEventMap[entity]->Invoke(data); + } + SAFE_NATIVE_CALL_END("Animator.OnClipFinished") + } + } diff --git a/SHADE_Managed/src/Components/Animator.hxx b/SHADE_Managed/src/Components/Animator.hxx index 7100c54b..39b97053 100644 --- a/SHADE_Managed/src/Components/Animator.hxx +++ b/SHADE_Managed/src/Components/Animator.hxx @@ -25,6 +25,47 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { + /// + /// Simple struct that holds event data for the Animator.OnClipStartedPlaying event. + /// + public value struct ClipStartPlayingEventData + { + /// + /// Animation Clip that started playing. + /// + AnimationClipAsset PlayingClip; + /// + /// Timestamp that the Animation Clip started playing at. This is relative to + /// the first frame of the Animation Clip. + /// + float CurrentTimeStamp; + }; + /// + /// Simple struct that holds event data for the Animator.OnClipPaused event. + /// + public value struct ClipPausedEventData + { + /// + /// Animation Clip that was paused. + /// + AnimationClipAsset PausedClip; + /// + /// Timestamp that the Animation Clip was paused at. This is relative to + /// the first frame of the Animation Clip. + /// + float PauseTimeStamp; + }; + /// + /// Simple struct that holds event data for the Animator.OnClipFinished event. + /// + public value struct ClipFinishedEventData + { + /// + /// Animation Clip that just finished. + /// + AnimationClipAsset FinishedClip; + }; + /// /// CLR version of the SHADE Engine's SHAnimatorComponent. /// @@ -70,6 +111,45 @@ namespace SHADE { System::String^ get(); } + /// + /// Whether an animation is currently playing. + /// + property bool IsPlaying + { + bool get(); + } + /// + /// Retrieves the current timestamp of the currently playing clip. This is + /// relative to the start frame of the Animation Clip. This will retrieve the + /// correct playback time depending on the current playback mode + /// (Animation Controller or via direct Animation Clip). + /// + property float CurrentClipPlaybackTime + { + float get(); + } + /// + /// Event which is raised on the frame that an animation has started playing. + /// + property CallbackEvent^ OnClipStartedPlaying + { + CallbackEvent^ get(); + } + /// + /// Event which is raised on the frame that an animation is paused. + /// + property CallbackEvent^ OnClipPaused + { + CallbackEvent^ get(); + } + /// + /// Event which is raised on the frame that an animation has finished playing. + /// This will not be called for any animation that loops. + /// + property CallbackEvent^ OnClipFinished + { + CallbackEvent^ get(); + } /*-----------------------------------------------------------------------------*/ /* Usage Functions */ @@ -159,6 +239,55 @@ namespace SHADE /// Name of the parameter. /// True if the trigger is set. System::Nullable GetTriggerState(System::String^ paramName); + + internal: + /*-----------------------------------------------------------------------------*/ + /* Static Clear Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Disposes static event data which may contains data from SHADE_Scripting. + /// + static void ClearStaticEventData(); + + private: + /*-----------------------------------------------------------------------------*/ + /* Event Handling Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// To be called from native code when this component is removed. + /// + /// The entity which has it's component removed. + static void OnComponentRemoved(EntityID entity); + /// + /// To be called from native code when a clip for this component has started + /// playing. + /// + /// The entity which has a clip start playing. + /// AssetID of the animation clip. + /// Timestamp that the clip was started at. + static void RaiseOnClipStartedPlaying(EntityID entity, AssetID animAssetId, float currTimeStamp); + /// + /// To be called from native code when a clip for this component has been paused. + /// + /// The entity which a clip has paused. + /// AssetID of the animation clip. + /// Timestamp that the clip was paused at. + static void RaiseOnClipPaused(EntityID entity, AssetID animAssetId, float currTimeStamp); + /// + /// To be called from native code when a clip for this component has finished + /// playing. + /// + /// The entity which a clip has finished playing. + /// AssetID of the animation clip. + static void RaiseOnClipFinished(EntityID entity, AssetID animAssetId); + + /*-----------------------------------------------------------------------------*/ + /* Static Data Members */ + /*-----------------------------------------------------------------------------*/ + // As these hold references to code in SHADE_Scripting, we must remember to dispose of them when changing scenes + static System::Collections::Generic::Dictionary^>^ onClipStartPlayingEventMap; + static System::Collections::Generic::Dictionary^>^ onClipPausedEventMap; + static System::Collections::Generic::Dictionary^>^ onClipFinishedEventMap; }; } diff --git a/SHADE_Managed/src/Components/UIElement.cxx b/SHADE_Managed/src/Components/UIElement.cxx index 8e6134e1..9e4a2459 100644 --- a/SHADE_Managed/src/Components/UIElement.cxx +++ b/SHADE_Managed/src/Components/UIElement.cxx @@ -28,18 +28,6 @@ namespace SHADE : Component(entity) {} - void UIElement::ClearStaticEventData() - { - if (onClickEventMap != nullptr) - onClickEventMap->Clear(); - if (onReleasedEventMap != nullptr) - onReleasedEventMap->Clear(); - if (onHoverEnterEventMap != nullptr) - onHoverEnterEventMap->Clear(); - if (onHoverExitEventMap != nullptr) - onHoverExitEventMap->Clear(); - } - /*---------------------------------------------------------------------------------*/ /* Properties */ /*---------------------------------------------------------------------------------*/ @@ -112,9 +100,24 @@ namespace SHADE return onHoverExitEventMap[owner.EntityId]; } + /*---------------------------------------------------------------------------------*/ + /* Static Clear Functions */ + /*---------------------------------------------------------------------------------*/ + void UIElement::ClearStaticEventData() + { + if (onClickEventMap != nullptr) + onClickEventMap->Clear(); + if (onReleasedEventMap != nullptr) + onReleasedEventMap->Clear(); + if (onHoverEnterEventMap != nullptr) + onHoverEnterEventMap->Clear(); + if (onHoverExitEventMap != nullptr) + onHoverExitEventMap->Clear(); + } + /*---------------------------------------------------------------------------------*/ /* Event Handling Functions */ - /*-----------------------------------------------------------------------------a----*/ + /*---------------------------------------------------------------------------------*/ void UIElement::OnComponentRemoved(EntityID entity) { SAFE_NATIVE_CALL_BEGIN diff --git a/SHADE_Managed/src/Engine/EngineInterface.cxx b/SHADE_Managed/src/Engine/EngineInterface.cxx index 50f8fbc2..ca7c6e77 100644 --- a/SHADE_Managed/src/Engine/EngineInterface.cxx +++ b/SHADE_Managed/src/Engine/EngineInterface.cxx @@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Debug.hxx" #include "Scripts/ScriptStore.hxx" #include "Components/UIElement.hxx" +#include "Components/Animator.hxx" namespace SHADE { @@ -46,6 +47,7 @@ namespace SHADE // Unload static data of components that have access to the assembly UIElement::ClearStaticEventData(); + Animator::ClearStaticEventData(); // Unload the script scriptContext->Unload(); From 6be5895ec715d75d2fdec39bc072552e0f30e824 Mon Sep 17 00:00:00 2001 From: maverickdgg Date: Fri, 10 Mar 2023 18:56:04 +0800 Subject: [PATCH 2/3] Fixed Camera Collision --- SHADE_Engine/src/Camera/SHCameraSystem.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/SHADE_Engine/src/Camera/SHCameraSystem.cpp b/SHADE_Engine/src/Camera/SHCameraSystem.cpp index 921f6f80..bd4252f3 100644 --- a/SHADE_Engine/src/Camera/SHCameraSystem.cpp +++ b/SHADE_Engine/src/Camera/SHCameraSystem.cpp @@ -206,6 +206,16 @@ namespace SHADE camera->dirtyView = true; } + camera->offset = offset; + + SHVec3 tOffset = pivot.GetTargetOffset(); + + + tOffset = SHVec3::RotateY(tOffset, SHMath::DegreesToRadians(pivot.GetYaw())); + + + if (pivot.lookAtCameraOrigin) + CameraLookAt(*camera, camera->position + pivot.GetTargetOffset()); @@ -250,7 +260,7 @@ namespace SHADE if (camera.isActive == false) return; - if (SHComponentManager::HasComponent(camera.GetEID()) == true && &camera != &editorCamera) + if (SHComponentManager::HasComponent(camera.GetEID()) == true && SHComponentManager::HasComponent(camera.GetEID()) == false && &camera != &editorCamera) { auto transform = SHComponentManager::GetComponent(camera.GetEID()); SHVec3 rotation = transform->GetWorldRotation(); @@ -273,14 +283,14 @@ namespace SHADE { camera.offset = arm->GetOffset(); - SHVec3 tOffset = arm->GetTargetOffset(); + /*SHVec3 tOffset = arm->GetTargetOffset(); tOffset = SHVec3::RotateY(tOffset, SHMath::DegreesToRadians(arm->GetYaw())); if (arm->lookAtCameraOrigin) - CameraLookAt(camera, camera.position + tOffset); + CameraLookAt(camera, camera.position + arm->GetTargetOffset());*/ } From d1e1a2e31c1f27d636ca358ff02fe156ef675725 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Fri, 10 Mar 2023 18:57:09 +0800 Subject: [PATCH 3/3] Fixed bug where animations would revert to bind pose when paused --- SHADE_Engine/src/Animation/SHAnimatorComponent.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp index fb866cbb..294d8098 100644 --- a/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp +++ b/SHADE_Engine/src/Animation/SHAnimatorComponent.cpp @@ -124,9 +124,13 @@ namespace SHADE std::fill(boneMatrices.begin(), boneMatrices.end(), SHMatrix::Identity); // Do not do anything if is not playing or there's nothing to animate - if (!isPlaying || !rig || !rig->GetRootNode()) + if (!rig || !rig->GetRootNode()) return; + // We want to still display a paused pose, so we only prevent progression + if (!isPlaying) + dt = 0.0f; + // Update the animation controller if any, this will set the currClip if (animController) { @@ -270,7 +274,7 @@ namespace SHADE void SHAnimatorComponent::updateCurrentAnimatorState(Handle clip, float playbackTime) { // Nothing to animate - if (!clip || !isPlaying || !rig || !rig->GetRootNode()) + if (!clip || !rig || !rig->GetRootNode()) return; // Check that we have animation data