From 2f1361a412016daedbe233c7b0dbe709e867a370 Mon Sep 17 00:00:00 2001 From: Arnaud Jamin Date: Sat, 11 Jan 2025 00:31:53 -0500 Subject: [PATCH] CogEngine: Fix and improve Plot window CogEngine: Fix and improve the Plot window - Fix ImPlot assert - Improve UI CogSample: - Add Inputs and AnimNotify plot events - Fix ability system warning --- .../Private/CogEngineWindow_Plots.cpp | 295 +++++++++++------- .../CogEngine/Public/CogEngineWindow_Plots.h | 30 +- Source/CogSample/CogSampleAnimNotify.cpp | 52 +++ Source/CogSample/CogSampleAnimNotify.h | 23 ++ Source/CogSample/CogSampleAnimNotifyState.cpp | 86 +++++ Source/CogSample/CogSampleAnimNotifyState.h | 27 ++ Source/CogSample/CogSampleCharacter.cpp | 26 +- Source/CogSample/CogSampleCharacter.h | 4 +- Source/CogSample/CogSampleGameState.cpp | 58 +++- Source/CogSample/CogSampleGameState.h | 8 + .../CogSample/CogSamplePlayerController.cpp | 1 - .../CogSampleSpawnPredictionComponent.cpp | 2 +- 12 files changed, 482 insertions(+), 130 deletions(-) create mode 100644 Source/CogSample/CogSampleAnimNotify.cpp create mode 100644 Source/CogSample/CogSampleAnimNotify.h create mode 100644 Source/CogSample/CogSampleAnimNotifyState.cpp create mode 100644 Source/CogSample/CogSampleAnimNotifyState.h diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Plots.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Plots.cpp index 4fc8332..8cb237a 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Plots.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Plots.cpp @@ -1,6 +1,7 @@ #include "CogEngineWindow_Plots.h" #include "CogDebugPlot.h" +#include "CogImguiHelper.h" #include "CogWindowWidgets.h" #include "Engine/World.h" #include "imgui.h" @@ -80,7 +81,7 @@ void FCogEngineWindow_Plots::RenderMenu() { if (ImGui::BeginMenu("Options")) { - if (ImGui::MenuItem("Clear Data")) + if (ImGui::MenuItem("Clear data")) { FCogDebugPlot::Clear(); } @@ -101,11 +102,27 @@ void FCogEngineWindow_Plots::RenderMenu() } FCogWindowWidgets::SetNextItemToShortWidth(); - if (ImGui::SliderFloat("Time Range", &Config->TimeRange, 1.0f, 30.0f, "%0.1f")) + if (ImGui::SliderFloat("Time range", &Config->TimeRange, 1.0f, 30.0f, "%0.1f")) { bApplyTimeScale = true; } - + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::SliderFloat("Drag view sensitivity", &Config->DragViewSensitivity, 1.0f, 50.0f, "%0.0f"); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::Checkbox("Show time bar at game time", &Config->ShowTimeBarAtGameTime); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::Checkbox("Show time bar at cursor", &Config->ShowTimeBarAtCursor); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::Checkbox("Show value at cursor", &Config->ShowValueAtCursor); + + constexpr ImGuiColorEditFlags ColorEditFlags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf; + FCogImguiHelper::ColorEdit4("Pause background color", Config->PauseBackgroundColor, ColorEditFlags); + ImGui::SetItemTooltip("Background color of the plot when paused."); + ImGui::EndMenu(); } @@ -124,22 +141,35 @@ void FCogEngineWindow_Plots::RenderPlotsList(TArray& Visibl ImGui::Indent(6); - for (FCogDebugPlotEntry& Entry : FCogDebugPlot::Plots) + for (FCogDebugPlotEntry& Plot : FCogDebugPlot::Plots) { - if (Entry.CurrentYAxis != ImAxis_COUNT && Entry.CurrentRow != INDEX_NONE) + if (Plot.CurrentYAxis != ImAxis_COUNT && Plot.CurrentRow != INDEX_NONE) { - VisiblePlots.Add(&Entry); + VisiblePlots.Add(&Plot); } ImGui::PushID(Index); - ImGui::PushStyleColor(ImGuiCol_Text, Entry.IsEventPlot ? IM_COL32(128, 128, 255, 255) : IM_COL32(255, 255, 255, 255)); - ImGui::Selectable(TCHAR_TO_ANSI(*Entry.Name.ToString()), false, 0); + ImGui::PushStyleColor(ImGuiCol_Text, Plot.IsEventPlot ? IM_COL32(128, 128, 255, 255) : IM_COL32(255, 255, 255, 255)); + + const bool IsAssignedToRow = Plot.CurrentRow != INDEX_NONE; + if (ImGui::Selectable(TCHAR_TO_ANSI(*Plot.Name.ToString()), IsAssignedToRow, ImGuiSelectableFlags_AllowDoubleClick)) + { + if (IsAssignedToRow) + { + Plot.ResetAxis(); + } + else + { + Plot.AssignAxis(0, ImAxis_Y1); + } + } + ImGui::PopStyleColor(); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - const auto EntryName = StringCast(*Entry.Name.ToString()); + const auto EntryName = StringCast(*Plot.Name.ToString()); ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1); ImGui::Text("%s", EntryName.Get()); ImGui::EndDragDropSource(); @@ -174,6 +204,13 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray& Visi static float RowRatios[] = { 1, 1, 1, 1, 1, 1 }; static float ColRatios[] = { 1 }; static ImPlotSubplotFlags SubplotsFlags = ImPlotSubplotFlags_LinkCols; + + const bool PushPlotBgStyle = FCogDebugPlot::Pause; + if (PushPlotBgStyle) + { + ImPlot::PushStyleColor(ImPlotCol_PlotBg, FCogImguiHelper::ToImVec4(Config->PauseBackgroundColor)); + } + if (ImPlot::BeginSubplots("", Config->Rows, 1, ImVec2(-1, -1), SubplotsFlags, RowRatios, ColRatios)) { for (int PlotIndex = 0; PlotIndex < Config->Rows; ++PlotIndex) @@ -209,94 +246,136 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray& Visi Config->TimeRange = TimeRange; } + const float Time = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0; + + //------------------------------------------------------------------ + // Setup all the Z and Y axis limits. Must be done before calling + // ImPlot::GetPlotPos or ImPlot::GetPlotSize as it calls SetupLock() + //------------------------------------------------------------------ + { + //-------------------------------------------------------------------------------- + // Make the time axis move forward automatically, unless the user pauses or zoom. + //-------------------------------------------------------------------------------- + if (FCogDebugPlot::Pause == false && ImGui::GetIO().MouseWheel == 0) + { + ImPlot::SetupAxisLimits(ImAxis_X1, Time - TimeRange, Time, ImGuiCond_Always); + } + + if (bApplyTimeScale) + { + ImPlot::SetupAxisLimits(ImAxis_X1, Time - Config->TimeRange, Time, ImGuiCond_Always); + } + + //-------------------------------------------------------------------------------- + // Set the Y axis limit for Events. + //-------------------------------------------------------------------------------- + for (const FCogDebugPlotEntry* PlotPtr : VisiblePlots) + { + if (PlotPtr == nullptr) + { continue; } + + if (PlotPtr->CurrentRow != PlotIndex) + { continue; } + + if (PlotPtr->IsEventPlot) + { + ImPlot::SetupAxisLimits(PlotPtr->CurrentYAxis, 0, PlotPtr->MaxRow + 2, ImGuiCond_Always); + } + } + } + const ImVec2 PlotMin = ImPlot::GetPlotPos(); const ImVec2 PlotSize = ImPlot::GetPlotSize(); const ImVec2 PlotMax = PlotMin + PlotSize; //---------------------------------------------------------------- - // Draw a vertical lines representing the current time and the mouse time + // Pause the scrolling if the user drag inside //---------------------------------------------------------------- - RenderTimeMarker(); + const ImVec2 Mouse = ImGui::GetMousePos(); + if (Mouse.x > PlotMin.x + && Mouse.y > PlotMin.y + && Mouse.x < PlotMax.x + && Mouse.y < PlotMax.y + && ImGui::GetDragDropPayload() == nullptr) + { + const ImVec2 Drag = ImGui::GetMouseDragDelta(0); + if (FMath::Abs(Drag.x) > Config->DragViewSensitivity) + { + FCogDebugPlot::Pause = true; + } + } + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + { + FCogDebugPlot::Pause = false; + } + + //--------------------------------------------------------------------------- + // Draw a vertical lines representing the current time and the mouse time + //--------------------------------------------------------------------------- + if (Config->ShowTimeBarAtGameTime || Config->ShowTimeBarAtGameTime) + { + ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList(); + + const float PlotTop = PlotMin.y; + const float TimeBarBottom = PlotTop + PlotSize.y; + + ImPlot::PushPlotClipRect(); + if (Config->ShowTimeBarAtCursor) + { + PlotDrawList->AddLine(ImVec2(ImGui::GetMousePos().x, PlotTop), ImVec2(ImGui::GetMousePos().x, TimeBarBottom), IM_COL32(128, 128, 128, 64)); + } + + if (Config->ShowTimeBarAtGameTime && FCogDebugPlot::Pause) + { + const float TimeBarX = ImPlot::PlotToPixels(Time, 0.0f).x; + PlotDrawList->AddLine(ImVec2(TimeBarX, PlotTop), ImVec2(TimeBarX, TimeBarBottom), IM_COL32(255, 255, 255, 64)); + } + ImPlot::PopPlotClipRect(); + } + + //----------------------------------------------------------- + // Draw all the plots assigned to this row + //----------------------------------------------------------- for (FCogDebugPlotEntry* PlotPtr : VisiblePlots) { if (PlotPtr == nullptr) + { continue; } + + FCogDebugPlotEntry& Plot = *PlotPtr; + if (Plot.CurrentRow != PlotIndex) + { continue; } + + ImPlot::SetAxis(Plot.CurrentYAxis); + + ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL); + const auto Label = StringCast(*Plot.Name.ToString()); + + //------------------------------------------------------- + // Plot Events + //------------------------------------------------------- + if (Plot.IsEventPlot) { - continue; + RenderEvents(Plot, Label.Get(), PlotMin, PlotMax); + } + //------------------------------------------------------- + // Plot Values + //------------------------------------------------------- + else if (Plot.Values.empty() == false) + { + RenderValues(Plot, Label.Get()); } - FCogDebugPlotEntry& Entry = *PlotPtr; - - if (Entry.Values.empty()) + //------------------------------------------------------- + // Allow legend item labels to be drag and drop sources + //------------------------------------------------------- + if (ImPlot::BeginDragDropSourceItem(Label.Get())) { - continue; + const auto EntryName = StringCast(*Plot.Name.ToString()); + ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1); + ImGui::TextUnformatted(EntryName.Get()); + ImPlot::EndDragDropSource(); } - - if (Entry.CurrentRow == PlotIndex) - { - //-------------------------------------------------------------------------------- - // Make the time axis move forward automatically, unless the user pauses or zoom. - //-------------------------------------------------------------------------------- - if (FCogDebugPlot::Pause == false && ImGui::GetIO().MouseWheel == 0) - { - ImPlot::SetupAxisLimits(ImAxis_X1, Entry.Time - TimeRange, Entry.Time, ImGuiCond_Always); - } - - if (bApplyTimeScale) - { - ImPlot::SetupAxisLimits(ImAxis_X1, Entry.Time - Config->TimeRange, Entry.Time, ImGuiCond_Always); - } - - ImPlot::SetAxis(Entry.CurrentYAxis); - - ImPlot::SetNextLineStyle(IMPLOT_AUTO_COL); - const auto Label = StringCast(*Entry.Name.ToString()); - - //---------------------------------------------------------------- - // Pause the scrolling if the user drag inside - //---------------------------------------------------------------- - const ImVec2 Mouse = ImGui::GetMousePos(); - if (Mouse.x > PlotMin.x - && Mouse.y > PlotMin.y - && Mouse.x < PlotMax.x - && Mouse.y < PlotMax.y - && ImGui::GetDragDropPayload() == nullptr) - { - const ImVec2 Drag = ImGui::GetMouseDragDelta(0); - if (FMath::Abs(Drag.x) > 10) - { - FCogDebugPlot::Pause = true; - } - } - - //------------------------------------------------------- - // Plot Events - //------------------------------------------------------- - const bool IsEventPlot = Entry.Events.Num() > 0; - if (IsEventPlot) - { - RenderEvents(Entry, Label.Get(), PlotMin, PlotMax); - } - //------------------------------------------------------- - // Plot Values - //------------------------------------------------------- - else - { - RenderValues(Entry, Label.Get()); - } - - //------------------------------------------------------- - // Allow legend item labels to be drag and drop sources - //------------------------------------------------------- - if (ImPlot::BeginDragDropSourceItem(Label.Get())) - { - const auto EntryName = StringCast(*Entry.Name.ToString()); - ImGui::SetDragDropPayload("DragAndDrop", EntryName.Get(), EntryName.Length() + 1); - ImGui::TextUnformatted(EntryName.Get()); - ImPlot::EndDragDropSource(); - } - } - } //------------------------------------------------------- @@ -352,45 +431,37 @@ void FCogEngineWindow_Plots::RenderPlots(const TArray& Visi } ImPlot::EndSubplots(); } + + if (PushPlotBgStyle) + { + ImPlot::PopStyleColor(); + } } ImGui::EndChild(); } //-------------------------------------------------------------------------------------------------------------------------- -void FCogEngineWindow_Plots::RenderTimeMarker() const -{ - const ImVec2 PlotMin = ImPlot::GetPlotPos(); - const ImVec2 PlotSize = ImPlot::GetPlotSize(); - ImDrawList* PlotDrawList = ImPlot::GetPlotDrawList(); - - const float PlotTop = PlotMin.y; - const float TimeBarBottom = PlotTop + PlotSize.y; - - ImPlot::PushPlotClipRect(); - PlotDrawList->AddLine(ImVec2(ImGui::GetMousePos().x, PlotTop), ImVec2(ImGui::GetMousePos().x, TimeBarBottom), IM_COL32(128, 128, 128, 64)); - if (FCogDebugPlot::Pause) - { - const float Time = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0; - const float TimeBarX = ImPlot::PlotToPixels(Time, 0.0f).x; - PlotDrawList->AddLine(ImVec2(TimeBarX, PlotTop), ImVec2(TimeBarX, TimeBarBottom), IM_COL32(255, 255, 255, 64)); - } - ImPlot::PopPlotClipRect(); -} - -//-------------------------------------------------------------------------------------------------------------------------- -void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char* Label) +void FCogEngineWindow_Plots::RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const { //---------------------------------------------------------------- - // Custom tooltip + // Value at cursor tooltip //---------------------------------------------------------------- - if (ImPlot::IsPlotHovered()) + if (Config->ShowValueAtCursor && ImPlot::IsPlotHovered()) { float Value; if (Entry.FindValue(ImPlot::GetPlotMousePos().x, Value)) { - ImGui::BeginTooltip(); - ImGui::Text("%s: %0.2f", Label, Value); - ImGui::EndTooltip(); + FCogWindowWidgets::BeginTableTooltip(); + if (ImGui::BeginTable("Params", 2, ImGuiTableFlags_Borders)) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s", Label); + ImGui::TableNextColumn(); + ImGui::Text("%0.2f", Value); + ImGui::EndTable(); + } + FCogWindowWidgets::EndTableTooltip(); } } @@ -423,8 +494,6 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char* //-------------------------------------------------------------------- Entry.UpdateTime(GetWorld()); - ImPlot::SetupAxisLimits(Entry.CurrentYAxis, 0, Entry.MaxRow + 2, ImGuiCond_Always); - ImPlot::PushPlotClipRect(); @@ -447,7 +516,7 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char* const bool IsInstant = Event.StartTime == Event.EndTime; if (IsInstant) { - constexpr float Radius = 10.0f; + constexpr float Radius = 10.0f; PlotDrawList->AddNgon(PosMid, 10, Event.BorderColor, 4); PlotDrawList->AddNgonFilled(PosMid, 10, Event.FillColor, 4); PlotDrawList->AddText(ImVec2(PosMid.x + 15, PosMid.y - 6), IM_COL32(255, 255, 255, 255), TCHAR_TO_ANSI(*Event.DisplayName)); @@ -494,7 +563,7 @@ void FCogEngineWindow_Plots::RenderEvents(FCogDebugPlotEntry& Entry, const char* } //-------------------------------------------------------------------------------------------------------------------------- -void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, FCogDebugPlotEntry& Entry) +void FCogEngineWindow_Plots::RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry) { if (ImPlot::IsPlotHovered() && HoveredEvent != nullptr) { diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Plots.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Plots.h index 377bec3..51df8b3 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Plots.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Plots.h @@ -32,13 +32,11 @@ protected: void RenderMenu(); - void RenderTimeMarker() const; - - static void RenderValues(FCogDebugPlotEntry& Entry, const char* Label); + void RenderValues(FCogDebugPlotEntry& Entry, const char* Label) const; void RenderEvents(FCogDebugPlotEntry& Entry, const char* Label, const ImVec2& PlotMin, const ImVec2& PlotMax) const; - static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, FCogDebugPlotEntry& Entry); + static void RenderEventTooltip(const FCogDebugPlotEvent* HoveredEvent, const FCogDebugPlotEntry& Entry); TObjectPtr Config = nullptr; @@ -60,11 +58,31 @@ public: int Rows = 1; UPROPERTY(Config) - float TimeRange = 10.0f; + float TimeRange = 20.0f; + + UPROPERTY(Config) + bool ShowTimeBarAtGameTime = true; + + UPROPERTY(Config) + bool ShowTimeBarAtCursor = true; + + UPROPERTY(Config) + bool ShowValueAtCursor = true; + + UPROPERTY(Config) + float DragViewSensitivity = 10.0f; + + UPROPERTY(Config) + FColor PauseBackgroundColor = FColor(10, 0, 0, 255); virtual void Reset() override { Rows = 1; - TimeRange = 10.0f; + TimeRange = 20.0f; + ShowTimeBarAtGameTime = true; + ShowTimeBarAtCursor = true; + ShowValueAtCursor = true; + DragViewSensitivity = 10.0f; + PauseBackgroundColor = FColor(10, 0, 0, 255); } }; \ No newline at end of file diff --git a/Source/CogSample/CogSampleAnimNotify.cpp b/Source/CogSample/CogSampleAnimNotify.cpp new file mode 100644 index 0000000..0f9580c --- /dev/null +++ b/Source/CogSample/CogSampleAnimNotify.cpp @@ -0,0 +1,52 @@ +#include "CogSampleAnimNotify.h" + +#include "CogCommon.h" + +#include "Animation/AnimSequenceBase.h" +#include "Components/SkeletalMeshComponent.h" + +#if ENABLE_COG +#include "CogDebugPlot.h" +#endif + +//-------------------------------------------------------------------------------------------------------------------------- +UCogSampleAnimNotify::UCogSampleAnimNotify(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + bIsNativeBranchingPoint = true; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) +{ + Super::Notify(MeshComp, Animation, EventReference); + +#if ENABLE_COG + FCogDebugPlot::PlotEventInstant(MeshComp->GetOwner(), "Anim Notify", GetFName()) + .AddParam("Name", GetNameSafe(this)) + .AddParam("Animation", GetNameSafe(Animation)) + .AddParam("Debug Info", GetDebugInfo()); +#endif +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotify::BranchingPointNotify(FBranchingPointNotifyPayload& BranchingPointPayload) +{ + //-------------------------------------------------------------------------------------------------------- + // Replace UAnimNotify::BranchingPointNotify to fill the EventReference with the NotifyEvent for + // UGPCoreAnimMotifyFunctionLibrary::AnimNotifyEventReferenceGetMontage to work properly + //-------------------------------------------------------------------------------------------------------- + // Super::BranchingPointNotify(BranchingPointPayload); + const FAnimNotifyEventReference EventReference(BranchingPointPayload.NotifyEvent, + BranchingPointPayload.SequenceAsset); + + Notify(BranchingPointPayload.SkelMeshComponent, BranchingPointPayload.SequenceAsset, EventReference); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString UCogSampleAnimNotify::GetDebugInfo_Implementation() const +{ + FString DebugInfo; + return DebugInfo; +} + \ No newline at end of file diff --git a/Source/CogSample/CogSampleAnimNotify.h b/Source/CogSample/CogSampleAnimNotify.h new file mode 100644 index 0000000..1578581 --- /dev/null +++ b/Source/CogSample/CogSampleAnimNotify.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" + +#include "Animation/AnimNotifies/AnimNotify.h" + +#include "CogSampleAnimNotify.generated.h" + +UCLASS() +class UCogSampleAnimNotify : public UAnimNotify +{ + GENERATED_BODY() + +public: + UCogSampleAnimNotify(const FObjectInitializer& ObjectInitializer); + + UFUNCTION(BlueprintNativeEvent) + FString GetDebugInfo() const; + + void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override; + + void BranchingPointNotify(FBranchingPointNotifyPayload& BranchingPointPayload) override; +}; diff --git a/Source/CogSample/CogSampleAnimNotifyState.cpp b/Source/CogSample/CogSampleAnimNotifyState.cpp new file mode 100644 index 0000000..1469fd1 --- /dev/null +++ b/Source/CogSample/CogSampleAnimNotifyState.cpp @@ -0,0 +1,86 @@ +#include "CogSampleAnimNotifyState.h" + +#include "CogCommon.h" + +#include "Animation/AnimNotifies/AnimNotify.h" +#include "Animation/AnimNotifyEndDataContext.h" +#include "Animation/AnimSequenceBase.h" +#include "Components/SkeletalMeshComponent.h" + +#if ENABLE_COG +#include "CogDebugPlot.h" +#endif + +//-------------------------------------------------------------------------------------------------------------------------- +UCogSampleAnimNotifyState::UCogSampleAnimNotifyState(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + bIsNativeBranchingPoint = false; +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotifyState::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) +{ +#if ENABLE_COG + FCogDebugPlot::PlotEventStart(MeshComp->GetOwner(), "Anim Notify", GetFName()) + .AddParam("Name", GetNameSafe(this)) + .AddParam("Animation", GetNameSafe(Animation)) + .AddParam("Debug Info", GetDebugInfo()); +#endif + + Super::NotifyBegin(MeshComp, Animation, TotalDuration, EventReference); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotifyState::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) +{ +#if ENABLE_COG + FCogDebugPlot::PlotEventStop(MeshComp->GetOwner(), "Anim Notify", GetFName()); +#endif + + Super::NotifyEnd(MeshComp, Animation, EventReference); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotifyState::BranchingPointNotifyBegin(FBranchingPointNotifyPayload& BranchingPointPayload) +{ + //-------------------------------------------------------------------------------------------------------- + // Replace UAnimNotifyState::BranchingPointNotifyBegin to fill the EventReference with the NotifyEvent for + // UGPCoreAnimMotifyFunctionLibrary::AnimNotifyEventReferenceGetMontage to work properly + //-------------------------------------------------------------------------------------------------------- + // Super::BranchingPointNotifyBegin(BranchingPointPayload); + + const FAnimNotifyEventReference EventReference(BranchingPointPayload.NotifyEvent, + BranchingPointPayload.SequenceAsset); + + NotifyBegin(BranchingPointPayload.SkelMeshComponent, + BranchingPointPayload.SequenceAsset, + BranchingPointPayload.NotifyEvent + ? BranchingPointPayload.NotifyEvent->GetDuration() + : 0.f, + EventReference); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void UCogSampleAnimNotifyState::BranchingPointNotifyEnd(FBranchingPointNotifyPayload& BranchingPointPayload) +{ + //-------------------------------------------------------------------------------------------------------- + // Replace UAnimNotifyState::BranchingPointNotifyEnd to fill the EventReference with the NotifyEvent for + // UGPCoreAnimMotifyFunctionLibrary::AnimNotifyEventReferenceGetMontage to work properly + //-------------------------------------------------------------------------------------------------------- + // Super::BranchingPointNotifyEnd(BranchingPointPayload); + + FAnimNotifyEventReference EventReference(BranchingPointPayload.NotifyEvent, BranchingPointPayload.SequenceAsset); + if (BranchingPointPayload.bReachedEnd) + { + EventReference.AddContextData(true); + } + NotifyEnd(BranchingPointPayload.SkelMeshComponent, BranchingPointPayload.SequenceAsset, EventReference); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString UCogSampleAnimNotifyState::GetDebugInfo_Implementation() const +{ + FString DebugInfo; + return DebugInfo; +} diff --git a/Source/CogSample/CogSampleAnimNotifyState.h b/Source/CogSample/CogSampleAnimNotifyState.h new file mode 100644 index 0000000..9897118 --- /dev/null +++ b/Source/CogSample/CogSampleAnimNotifyState.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" + +#include "Animation/AnimNotifies/AnimNotifyState.h" + +#include "CogSampleAnimNotifyState.generated.h" + +UCLASS() +class UCogSampleAnimNotifyState : public UAnimNotifyState +{ + GENERATED_BODY() + +public: + UCogSampleAnimNotifyState(const FObjectInitializer& ObjectInitializer); + + UFUNCTION(BlueprintNativeEvent) + FString GetDebugInfo() const; + + void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override; + + void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override; + + void BranchingPointNotifyBegin(FBranchingPointNotifyPayload& BranchingPointPayload) override; + + void BranchingPointNotifyEnd(FBranchingPointNotifyPayload& BranchingPointPayload) override; +}; diff --git a/Source/CogSample/CogSampleCharacter.cpp b/Source/CogSample/CogSampleCharacter.cpp index b115b36..a74b44c 100644 --- a/Source/CogSample/CogSampleCharacter.cpp +++ b/Source/CogSample/CogSampleCharacter.cpp @@ -370,8 +370,18 @@ void ACogSampleCharacter::SetupPlayerInputComponent(class UInputComponent* Playe int32 AbilityIndex = 0; for (const FActiveAbilityInfo& AbilityInfo : ActiveAbilities) { - EnhancedInputComponent->BindAction(AbilityInfo.InputAction, ETriggerEvent::Started, this, &ACogSampleCharacter::OnAbilityInputStarted, AbilityIndex); - EnhancedInputComponent->BindAction(AbilityInfo.InputAction, ETriggerEvent::Completed, this, &ACogSampleCharacter::OnAbilityInputCompleted, AbilityIndex); + EnhancedInputComponent->BindActionValueLambda(AbilityInfo.InputAction, ETriggerEvent::Started, + [this, &AbilityInfo, AbilityIndex](const FInputActionValue& InputActionValue) + { + OnAbilityInputStarted(AbilityInfo.InputAction, InputActionValue, AbilityIndex); + }); + + EnhancedInputComponent->BindActionValueLambda(AbilityInfo.InputAction, ETriggerEvent::Completed, + [this, &AbilityInfo, AbilityIndex](const FInputActionValue& InputActionValue) + { + OnAbilityInputCompleted(AbilityInfo.InputAction, InputActionValue, AbilityIndex); + }); + AbilityIndex++; } @@ -385,10 +395,14 @@ void ACogSampleCharacter::SetupPlayerInputComponent(class UInputComponent* Playe } //-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleCharacter::OnAbilityInputStarted(const FInputActionValue& Value, int32 Index) +void ACogSampleCharacter::OnAbilityInputStarted(const UInputAction* InputAction, const FInputActionValue& Value, int32 Index) { COG_LOG_OBJECT(LogCogInput, ELogVerbosity::Verbose, this, TEXT("%d"), Index); +#if ENABLE_COG + FCogDebugPlot::PlotEventStart(this, "Input", InputAction->GetFName()); +#endif + if (ActiveAbilityHandles.IsValidIndex(Index) == false) { return; @@ -429,10 +443,14 @@ void ACogSampleCharacter::OnAbilityInputStarted(const FInputActionValue& Value, } //-------------------------------------------------------------------------------------------------------------------------- -void ACogSampleCharacter::OnAbilityInputCompleted(const FInputActionValue& Value, int32 Index) +void ACogSampleCharacter::OnAbilityInputCompleted(const UInputAction* InputAction, const FInputActionValue& Value, int32 Index) { COG_LOG_OBJECT(LogCogInput, ELogVerbosity::Verbose, this, TEXT("%d"), Index); +#if ENABLE_COG + FCogDebugPlot::PlotEventStop(this, "Input", InputAction->GetFName()); +#endif + if (ActiveAbilityHandles.IsValidIndex(Index) == false) { return; diff --git a/Source/CogSample/CogSampleCharacter.h b/Source/CogSample/CogSampleCharacter.h index d40ed24..0d4d6ce 100644 --- a/Source/CogSample/CogSampleCharacter.h +++ b/Source/CogSample/CogSampleCharacter.h @@ -310,9 +310,9 @@ protected: void Look(const FInputActionValue& Value); - void OnAbilityInputStarted(const FInputActionValue& Value, int32 Index); + void OnAbilityInputStarted(const UInputAction* InputAction, const FInputActionValue& Value, int32 Index); - void OnAbilityInputCompleted(const FInputActionValue& Value, int32 Index); + void OnAbilityInputCompleted(const UInputAction* InputAction, const FInputActionValue& Value, int32 Index); void ActivateItem(const FInputActionValue& Value, int32 Index); diff --git a/Source/CogSample/CogSampleGameState.cpp b/Source/CogSample/CogSampleGameState.cpp index cdd0881..f4bc76e 100644 --- a/Source/CogSample/CogSampleGameState.cpp +++ b/Source/CogSample/CogSampleGameState.cpp @@ -2,7 +2,9 @@ #include "CogSampleAbilitySystemComponent.h" #include "GameFramework/GameState.h" +#include "GameFramework/PlayerState.h" #include "Modules/ModuleManager.h" +#include "Net/UnrealNetwork.h" #if ENABLE_COG #include "CogAll.h" @@ -25,6 +27,13 @@ ACogSampleGameState::ACogSampleGameState(const FObjectInitializer & ObjectInitia AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal); } +//-------------------------------------------------------------------------------------------------------------------------- +void ACogSampleGameState::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + DOREPLIFETIME(ThisClass, _ServerFramerateRaw); +} + //-------------------------------------------------------------------------------------------------------------------------- void ACogSampleGameState::BeginPlay() { @@ -66,9 +75,52 @@ void ACogSampleGameState::Tick(float DeltaSeconds) #if ENABLE_COG extern ENGINE_API float GAverageFPS; - extern ENGINE_API float GAverageMS; - FCogDebugPlot::PlotValue(this, "Frame Rate", GAverageFPS); - FCogDebugPlot::PlotValue(this, "Frame Time", GAverageMS); + + constexpr float smoothing = 10.0f; + + _ClientFramerateSmooth = _ClientFramerateSmooth >= 0.0f + ? FMath::FInterpTo(_ClientFramerateSmooth, GAverageFPS, DeltaSeconds, smoothing) + : GAverageFPS; + + _ServerFramerateSmooth = _ServerFramerateSmooth >= 0.0f + ? FMath::FInterpTo(_ServerFramerateSmooth, _ServerFramerateRaw, DeltaSeconds, smoothing) + : _ServerFramerateRaw; + + if (GetLocalRole() != ROLE_Authority) + { + FCogDebugPlot::PlotValue(this, "Frame Rate Client Raw", GAverageFPS); + FCogDebugPlot::PlotValue(this, "Frame Rate Client Smooth", _ClientFramerateSmooth); + FCogDebugPlot::PlotValue(this, "Frame Rate Server Raw", _ServerFramerateRaw); + FCogDebugPlot::PlotValue(this, "Frame Rate Server Smooth", _ServerFramerateSmooth); + + if (const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController()) + { + if (const APlayerController* PlayerController = LocalPlayer->PlayerController) + { + if (const APlayerState* PlayerState = PlayerController->GetPlayerState()) + { + FCogDebugPlot::PlotValue(this, "Ping", PlayerState->GetPingInMilliseconds()); + } + + if (const UNetConnection* Connection = PlayerController->GetNetConnection()) + { + FCogDebugPlot::PlotValue(this, + "Packet Loss In", + Connection->GetInLossPercentage().GetAvgLossPercentage() * 100.0f); + + FCogDebugPlot::PlotValue(this, + "Packet Loss Out", + Connection->GetOutLossPercentage().GetAvgLossPercentage() * 100.0f); + } + } + } + } + else + { + FCogDebugPlot::PlotValue(this, "Frame Rate Raw", GAverageFPS); + FCogDebugPlot::PlotValue(this, "Frame Rate Smooth", _ClientFramerateSmooth); + } + if (CogWindowManager != nullptr) { diff --git a/Source/CogSample/CogSampleGameState.h b/Source/CogSample/CogSampleGameState.h index ba071e1..654b3f7 100644 --- a/Source/CogSample/CogSampleGameState.h +++ b/Source/CogSample/CogSampleGameState.h @@ -36,11 +36,19 @@ private: UPROPERTY() TObjectPtr CogWindowManagerRef = nullptr; +protected: + UPROPERTY(Replicated) + float _ServerFramerateRaw = 0.0f; + #if ENABLE_COG void InitializeCog(); TObjectPtr CogWindowManager = nullptr; + float _ClientFramerateSmooth = 0.0f; + + float _ServerFramerateSmooth = 0.0f; + #endif //ENABLE_COG }; diff --git a/Source/CogSample/CogSamplePlayerController.cpp b/Source/CogSample/CogSamplePlayerController.cpp index 6c4dccb..d297933 100644 --- a/Source/CogSample/CogSamplePlayerController.cpp +++ b/Source/CogSample/CogSamplePlayerController.cpp @@ -12,7 +12,6 @@ #if ENABLE_COG #include "CogAbilityReplicator.h" #include "CogDebugDraw.h" -#include "CogDebugPlot.h" #include "CogDebugReplicator.h" #include "CogEngineReplicator.h" #include "Framework/Application/NavigationConfig.h" diff --git a/Source/CogSample/CogSampleSpawnPredictionComponent.cpp b/Source/CogSample/CogSampleSpawnPredictionComponent.cpp index 63907e6..d251b0c 100644 --- a/Source/CogSample/CogSampleSpawnPredictionComponent.cpp +++ b/Source/CogSample/CogSampleSpawnPredictionComponent.cpp @@ -47,7 +47,7 @@ FCogSampleSpawnPredictionKey FCogSampleSpawnPredictionKey::MakeFromAbility(const FCogSampleSpawnPredictionKey Key; Key.Creator = InAbility.GetAvatarActorFromActorInfo() != nullptr ? InAbility.GetAvatarActorFromActorInfo()->GetFName() : FName(); Key.Ability = InAbility.GetFName(); - Key.PredictionKey = Spec->ActivationInfo.GetActivationPredictionKey(); + Key.PredictionKey = InAbility.GetCurrentActivationInfo().GetActivationPredictionKey(); Key.InstanceIndex = InInstanceIndex; Key.GameTime = InAbility.GetWorld()->GetTimeSeconds(); return Key;