From 49e82bf250318f4dc75c3e3e4d45fb54a96f4225 Mon Sep 17 00:00:00 2001 From: Maxime Date: Sun, 26 Nov 2023 20:32:42 +0100 Subject: [PATCH] Add debug for Character Movement Component --- Pawn_Unreal/Pawn.sln.DotSettings | 3 +- .../PwnCharacterMovementComponent.cpp | 91 +++++++++++++++++-- .../Pawn/Private/Debug/PwnDebugSubsystem.cpp | 9 +- .../Pawn/Private/Utils/PwnImGuiLibrary.cpp | 60 ++++++++---- .../PwnCharacterMovementComponent.h | 2 +- .../Pawn/Public/Utils/PwnImGuiLibrary.h | 39 ++++++-- 6 files changed, 164 insertions(+), 40 deletions(-) diff --git a/Pawn_Unreal/Pawn.sln.DotSettings b/Pawn_Unreal/Pawn.sln.DotSettings index 28ae13f..e1333b1 100644 --- a/Pawn_Unreal/Pawn.sln.DotSettings +++ b/Pawn_Unreal/Pawn.sln.DotSettings @@ -1,3 +1,4 @@  True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp b/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp index 755c2b2..5a353c5 100644 --- a/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp +++ b/Pawn_Unreal/Source/Pawn/Private/Characters/PwnCharacterMovementComponent.cpp @@ -5,8 +5,10 @@ #include "Components/SplineComponent.h" #include "GameplayModes/PwnGameplayModeSubsystem.h" #include "GameplayModes/Combat/PwnCombatPlatformerPath.h" +#include "Kismet/GameplayStatics.h" #include "Kismet/KismetSystemLibrary.h" #include "Utils/EngineUtils.h" +#include "Utils/PwnImGuiLibrary.h" constexpr float LineTraceDistance = 10000.0f; @@ -84,6 +86,76 @@ void UPwnCharacterMovementComponent::PhysCustom(const float DeltaTime, const int } bool UPwnCharacterMovementComponent::DisplayDebug_Implementation() { + UPwnImGuiLibrary::Title("Movement"); + UPwnImGuiLibrary::ColoredVariable(FColor::Purple, "Movement Mode", UEnum::GetValueAsString(MovementMode)); + UPwnImGuiLibrary::ColoredVariable(FColor::Purple, "Custom Movement Mode", + UEnum::GetValueAsString( + static_cast>(CustomMovementMode))); + + UPwnImGuiLibrary::ColoredVariable(FColor::Red, "Is Grounded", BOOL_TO_STR(IsMovingOnGround())); + UPwnImGuiLibrary::ColoredVariable(FColor::Red, "Is Falling", BOOL_TO_STR(IsFalling())); + UPwnImGuiLibrary::ColoredVariable(FColor::Red, "IsFollowingSpline", BOOL_TO_STR(IsFollowingSpline)); + UPwnImGuiLibrary::ColoredVariable(FColor::Yellow, "Velocity", Velocity.ToString()); + + if (CombatPath != nullptr) { + UPwnImGuiLibrary::Title("Spline Following Movement"); + UPwnImGuiLibrary::ColoredVariable(FColor::Cyan, "Combat Path", CombatPath->GetActorNameOrLabel()); + UPwnImGuiLibrary::ColoredVariable(FColor::Green, "Distance Along Spline", FString::SanitizeFloat(DistanceAlongSpline)); + UPwnImGuiLibrary::ColoredVariable(FColor::Green, "Dot Direction", FString::SanitizeFloat(DotDirection)); + UPwnImGuiLibrary::ColoredVariable(FColor::Green, "Tangent 2D", Tangent2D.ToString()); + UPwnImGuiLibrary::ColoredVariable(FColor::Yellow, "Acceleration", Acceleration.ToString()); + + if (UPwnImGuiLibrary::Checkbox(this, "Draw World Debug", true)) { + UPwnImGuiLibrary::Indent(); + UPwnImGuiLibrary::ColoredString(FColor::Yellow, "Velocity"); + UPwnImGuiLibrary::ColoredString(FColor::Green, "Tangent"); + DrawDebugDirectionalArrow(GetWorld(), UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentLocation() + Velocity, + 100.0f, FColor::Yellow, false, -1.0f, 0, 3.0f); + DrawDebugDirectionalArrow(GetWorld(), UpdatedComponent->GetComponentLocation(), + UpdatedComponent->GetComponentLocation() + Tangent2D * 50.0f, + 100.0f, FColor::Green, false, -1.0f, 0, 3.0f); + UPwnImGuiLibrary::Unindent(); + } + if (UPwnImGuiLibrary::Checkbox(this, "Draw Splines", true)) { + UPwnImGuiLibrary::Indent(); + UPwnImGuiLibrary::ColoredString(FColor::Cyan, "Flattened Spline"); + UPwnImGuiLibrary::ColoredString(FColor::Magenta, "Location on Spline"); + + const float Length = CombatPath->FlattenedSpline->GetSplineLength(); + constexpr float Step = 15.0f; + + float ZLocation; + if (UPwnImGuiLibrary::Checkbox(this, "Track Ground", true)) { + FHitResult Hit; + LineTraceToGround(Hit, UpdatedComponent->GetComponentLocation()); + ZLocation = Hit.ImpactPoint.Z + 200.0f; + } else { + ZLocation = UpdatedComponent->GetComponentLocation().Z + 100.0f; + } + + FVector PreviousLocation = CombatPath->FlattenedSpline->GetLocationAtDistanceAlongSpline(0.0f, ESplineCoordinateSpace::World); + PreviousLocation.Z = ZLocation; + for (float Distance = Step; Distance <= Length; Distance += Step) { + FVector Location = CombatPath->FlattenedSpline->GetLocationAtDistanceAlongSpline(Distance, ESplineCoordinateSpace::World); + Location.Z = ZLocation; + DrawDebugLine(GetWorld(), PreviousLocation, Location, FColor::Cyan, false, -1.0f, 0, 3.0f); + PreviousLocation = Location; + } + + FVector LocationOnSpline = CombatPath->FlattenedSpline->GetLocationAtDistanceAlongSpline( + DistanceAlongSpline, ESplineCoordinateSpace::World); + LocationOnSpline.Z = ZLocation; + DrawDebugSphere(GetWorld(), LocationOnSpline, 10.0f, 12, FColor::Magenta, false, -1.0f, 0, + 3.0f); + + const FVector TextLocation = LocationOnSpline + 15.0f; + DrawDebugString(GetWorld(), TextLocation, CombatPath->GetActorNameOrLabel(), nullptr, FColor::Cyan, + 0.008f * UGameplayStatics::GetGlobalTimeDilation(this), false, 1.5f); + + UPwnImGuiLibrary::Unindent(); + } + } + return true; } @@ -104,12 +176,16 @@ bool UPwnCharacterMovementComponent::IsCustomMovementMode(const ECustomMovementM return MovementMode == MOVE_Custom && CustomMovementMode == Mode; } -bool UPwnCharacterMovementComponent::LineTraceToGround(FHitResult& OutHit, const FVector& StartLocation, const FLinearColor& DebugColor) const { +bool UPwnCharacterMovementComponent::LineTraceToGround(FHitResult& OutHit, const FVector& StartLocation) const { const FVector EndLocation = StartLocation + FVector(0.0f, 0.0f, -LineTraceDistance); - return UKismetSystemLibrary::LineTraceSingle(this, StartLocation, EndLocation, UEngineTypes::ConvertToTraceType(ECC_Visibility), false, TArray(), - DebugColor == FLinearColor::White ? EDrawDebugTrace::None : EDrawDebugTrace::None, OutHit, true, DebugColor, FLinearColor::Green, 0.5f); + return UKismetSystemLibrary::LineTraceSingle(this, StartLocation, EndLocation, UEngineTypes::ConvertToTraceType(ECC_Visibility), false, + TArray(), + EDrawDebugTrace::None, OutHit, true, + FColor::White, FLinearColor::Green, 0.5f); } + float AccumulatedTimer = 0.0f; + void UPwnCharacterMovementComponent::UpdateCurrentCombatPath(const bool Force, const bool UpdateLocation) { AccumulatedTimer += GetWorld()->GetDeltaSeconds(); if (AccumulatedTimer < 0.5f && !Force) { @@ -124,13 +200,13 @@ void UPwnCharacterMovementComponent::UpdateCurrentCombatPath(const bool Force, c APwnCombatPlatformerPath* OutCombatPath; FVector ClosestLocation; FHitResult Hit; - if (LineTraceToGround(Hit, UpdatedComponent->GetComponentLocation(), FLinearColor::Red) + if (LineTraceToGround(Hit, UpdatedComponent->GetComponentLocation()) && UPwnGameplayModeSubsystem::Get(this).FindClosestCombatPathLocation(Hit.ImpactPoint, OutCombatPath, ClosestLocation) && OutCombatPath != CombatPath) { CombatPath = OutCombatPath; DotDirection = CombatPath->Reversed ? -1.0f : 1.0f; - if (LineTraceToGround(Hit, ClosestLocation, FLinearColor::Blue)) { + if (LineTraceToGround(Hit, ClosestLocation)) { const float SplineKey = CombatPath->FlattenedSpline->FindInputKeyClosestToWorldLocation(Hit.ImpactPoint); DistanceAlongSpline = CombatPath->FlattenedSpline->GetDistanceAlongSplineAtSplineInputKey(SplineKey); @@ -151,7 +227,8 @@ void UPwnCharacterMovementComponent::UpdateCurrentCombatPath(const bool Force, c void UPwnCharacterMovementComponent::UpdateTangentAndAcceleration() { // Clamp distance because tangents are not defined at the ends of the spline const float ClampedDistanceAlongSpline = FMath::Clamp(DistanceAlongSpline, 0.0001f, CombatPath->FlattenedSpline->GetSplineLength() - 0.0001f); - Tangent2D = CombatPath->FlattenedSpline->GetTangentAtDistanceAlongSpline(ClampedDistanceAlongSpline, ESplineCoordinateSpace::World).GetSafeNormal2D(); + Tangent2D = CombatPath->FlattenedSpline->GetTangentAtDistanceAlongSpline(ClampedDistanceAlongSpline, ESplineCoordinateSpace::World). + GetSafeNormal2D(); // Recalculate acceleration so the input is relative to the spline Acceleration = Tangent2D * Acceleration.Size2D() * FMath::Sign(Acceleration.X) * DotDirection; } @@ -181,7 +258,7 @@ void UPwnCharacterMovementComponent::UpdatePawnVelocity(const float TimeTick) { void UPwnCharacterMovementComponent::UpdateDistanceAlongSpline() { FHitResult Hit; - if (LineTraceToGround(Hit, UpdatedComponent->GetComponentLocation(), FLinearColor::White)) { + if (LineTraceToGround(Hit, UpdatedComponent->GetComponentLocation())) { const FVector ImpactPoint = Hit.ImpactPoint; const float InputKey = CombatPath->FlattenedSpline->FindInputKeyClosestToWorldLocation(ImpactPoint); DistanceAlongSpline = CombatPath->FlattenedSpline->GetDistanceAlongSplineAtSplineInputKey(InputKey); diff --git a/Pawn_Unreal/Source/Pawn/Private/Debug/PwnDebugSubsystem.cpp b/Pawn_Unreal/Source/Pawn/Private/Debug/PwnDebugSubsystem.cpp index 0bb4e42..fe046ba 100644 --- a/Pawn_Unreal/Source/Pawn/Private/Debug/PwnDebugSubsystem.cpp +++ b/Pawn_Unreal/Source/Pawn/Private/Debug/PwnDebugSubsystem.cpp @@ -4,6 +4,7 @@ #include "Utils/PwnImGuiLibrary.h" #include "Kismet/GameplayStatics.h" #include "Kismet/KismetStringLibrary.h" +#include "Utils/EngineUtils.h" UPwnDebugSubsystem& UPwnDebugSubsystem::Get(const UObject* WorldContextObject) { return *WorldContextObject->GetWorld()->GetSubsystem(); @@ -18,7 +19,7 @@ void UPwnDebugSubsystem::SaveVariable(const UObject* Object, const FString& Name ObjectVariables.Add(Object, FVariableList()); } FVariableList& VariableList = ObjectVariables[Object]; - VariableList.Variables.Add(Name, UKismetStringLibrary::Conv_BoolToString(Value)); + VariableList.Variables.Add(Name, BOOL_TO_STR(Value)); } bool UPwnDebugSubsystem::GetVariableAsBool(const UObject* Object, const FString& Name, const bool DefaultValue) { @@ -51,7 +52,7 @@ void UPwnDebugSubsystem::Tick(float DeltaTime) { TArray Components = Actor->GetComponentsByInterface(UIPwnDebuggable::StaticClass()); if (Actor->Implements() || Components.Num() > 0) { bool Show = DebuggableComponents.Contains(Actor); - UPwnImGuiLibrary::CheckboxStringCustom(Actor->GetActorNameOrLabel(), Show); + UPwnImGuiLibrary::CheckboxCustom(Actor->GetActorNameOrLabel(), Show); if (Show) { DebuggableComponents.Add(Actor, Components); } else { @@ -76,11 +77,11 @@ void UPwnDebugSubsystem::DrawDebugWindowForActor(AActor* DebugActor, TArray 0) { - UPwnImGuiLibrary::TitleString("Components"); + UPwnImGuiLibrary::Title("Components"); } for (UActorComponent* Component : DebugComponents) { if (IsValid(Component)) { - if (UPwnImGuiLibrary::TreeNodeString(Component->GetClass()->GetName())) { + if (UPwnImGuiLibrary::TreeNode(Component->GetClass()->GetName())) { IIPwnDebuggable::Execute_DisplayDebug(Component); UPwnImGuiLibrary::TreePop(); } diff --git a/Pawn_Unreal/Source/Pawn/Private/Utils/PwnImGuiLibrary.cpp b/Pawn_Unreal/Source/Pawn/Private/Utils/PwnImGuiLibrary.cpp index 6f681bb..e085639 100644 --- a/Pawn_Unreal/Source/Pawn/Private/Utils/PwnImGuiLibrary.cpp +++ b/Pawn_Unreal/Source/Pawn/Private/Utils/PwnImGuiLibrary.cpp @@ -4,26 +4,36 @@ #include "Debug/PwnDebugSubsystem.h" void UPwnImGuiLibrary::Text(const FText Text) { - TextString(Text.ToString()); + String(Text.ToString()); } -void UPwnImGuiLibrary::TextString(const FString Text) { +void UPwnImGuiLibrary::String(const FString Text) { ImGui::Text(STRING_TO_ANSI(Text)); } void UPwnImGuiLibrary::ColoredText(const FColor Color, const FText Text) { - ColoredTextString(Color, Text.ToString()); + ColoredString(Color, Text.ToString()); } -void UPwnImGuiLibrary::ColoredTextString(const FColor Color, const FString Text) { +void UPwnImGuiLibrary::ColoredString(const FColor Color, const FString Text) { ImGui::TextColored(COLOR_TO_IMVEC4(Color), STRING_TO_ANSI(Text)); } -void UPwnImGuiLibrary::Title(const FText Title) { - TitleString(Title.ToString()); +void UPwnImGuiLibrary::Variable(const FString Name, const FString Value) { + ColoredVariable(FColor::White, Name, Value); } -void UPwnImGuiLibrary::TitleString(const FString Title) { +void UPwnImGuiLibrary::ColoredVariable(const FColor Color, const FString Name, const FString Value) { + String(Name + ": "); + SameLine(); + ColoredString(Color, Value); +} + +void UPwnImGuiLibrary::TitleText(const FText InTitle) { + Title(InTitle.ToString()); +} + +void UPwnImGuiLibrary::Title(const FString Title) { ImGui::SeparatorText(STRING_TO_ANSI(Title)); } @@ -36,35 +46,51 @@ void UPwnImGuiLibrary::EndWindow() { ImGui::End(); } -bool UPwnImGuiLibrary::Checkbox(const UObject* WoldContextObject, const FText Label) { - return CheckboxString(WoldContextObject, Label.ToString()); +bool UPwnImGuiLibrary::CheckboxText(const UObject* WoldContextObject, const FText Label, const bool DefaultValue) { + return Checkbox(WoldContextObject, Label.ToString(), DefaultValue); } -bool UPwnImGuiLibrary::CheckboxString(const UObject* WoldContextObject, const FString Label) { +bool UPwnImGuiLibrary::Checkbox(const UObject* WoldContextObject, const FString Label, const bool DefaultValue) { UPwnDebugSubsystem& SubSystem = UPwnDebugSubsystem::Get(WoldContextObject); - bool Show = SubSystem.GetVariableAsBool(WoldContextObject, Label); + bool Show = SubSystem.GetVariableAsBool(WoldContextObject, Label, DefaultValue); ImGui::Checkbox(STRING_TO_ANSI(Label), &Show); SubSystem.SaveVariable(WoldContextObject, Label, Show); return Show; } -bool UPwnImGuiLibrary::CheckboxCustom(const FText Label, bool& Show) { - return CheckboxStringCustom(Label.ToString(), Show); +bool UPwnImGuiLibrary::CheckboxTextCustom(const FText Label, bool& Show) { + return CheckboxCustom(Label.ToString(), Show); } -bool UPwnImGuiLibrary::CheckboxStringCustom(const FString Label, bool& Show) { +bool UPwnImGuiLibrary::CheckboxCustom(const FString Label, bool& Show) { ImGui::Checkbox(STRING_TO_ANSI(Label), &Show); return Show; } -bool UPwnImGuiLibrary::TreeNode(const FText Label) { - return TreeNodeString(Label.ToString()); +bool UPwnImGuiLibrary::TreeNodeText(const FText Label) { + return TreeNode(Label.ToString()); } -bool UPwnImGuiLibrary::TreeNodeString(const FString Label) { +bool UPwnImGuiLibrary::TreeNode(const FString Label) { return ImGui::TreeNode(STRING_TO_ANSI(Label)); } void UPwnImGuiLibrary::TreePop() { return ImGui::TreePop(); } + +void UPwnImGuiLibrary::SameLine() { + ImGui::SameLine(); +} + +void UPwnImGuiLibrary::Separator() { + ImGui::Separator(); +} + +void UPwnImGuiLibrary::Indent(const float Width) { + ImGui::Indent(Width); +} + +void UPwnImGuiLibrary::Unindent(const float Width) { + ImGui::Unindent(Width); +} diff --git a/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h b/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h index dea7885..34a33a3 100644 --- a/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h +++ b/Pawn_Unreal/Source/Pawn/Public/Characters/PwnCharacterMovementComponent.h @@ -58,7 +58,7 @@ public: bool IsCustomMovementMode(const ECustomMovementMode Mode) const; private: - bool LineTraceToGround(FHitResult& OutHit, const FVector& StartLocation, const FLinearColor& DebugColor) const; + bool LineTraceToGround(FHitResult& OutHit, const FVector& StartLocation) const; void UpdateCurrentCombatPath(bool Force = false, const bool UpdateLocation = false); diff --git a/Pawn_Unreal/Source/Pawn/Public/Utils/PwnImGuiLibrary.h b/Pawn_Unreal/Source/Pawn/Public/Utils/PwnImGuiLibrary.h index 87424aa..7c527f1 100644 --- a/Pawn_Unreal/Source/Pawn/Public/Utils/PwnImGuiLibrary.h +++ b/Pawn_Unreal/Source/Pawn/Public/Utils/PwnImGuiLibrary.h @@ -19,23 +19,29 @@ class PAWN_API UPwnImGuiLibrary : public UBlueprintFunctionLibrary { public: // Widgets: Text + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") + static void TitleText(const FText InTitle); + + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") + static void Title(const FString Title); + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") static void Text(const FText Text); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") - static void TextString(const FString Text); + static void String(const FString Text); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") static void ColoredText(const FColor Color, const FText Text); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") - static void ColoredTextString(const FColor Color, const FString Text); + static void ColoredString(const FColor Color, const FString Text); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") - static void Title(const FText Title); + static void Variable(const FString Name, const FString Value); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Text") - static void TitleString(const FString Title); + static void ColoredVariable(const FColor Color, const FString Name, const FString Value); // Widgets: Main UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Main") @@ -45,24 +51,37 @@ public: static void EndWindow(); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Main", meta=(WorldContext="WorldContextObject")) - static bool Checkbox(const UObject* WoldContextObject, const FText Label); + static bool CheckboxText(const UObject* WoldContextObject, const FText Label, const bool DefaultValue = false); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Main", meta=(WorldContext="WorldContextObject")) - static bool CheckboxString(const UObject* WoldContextObject, const FString Label); + static bool Checkbox(const UObject* WoldContextObject, const FString Label, const bool DefaultValue = false); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Main") - static bool CheckboxCustom(const FText Label, UPARAM(Ref) bool& Show); + static bool CheckboxTextCustom(const FText Label, UPARAM(Ref) bool& Show); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Main") - static bool CheckboxStringCustom(const FString Label, UPARAM(Ref) bool& Show); + static bool CheckboxCustom(const FString Label, UPARAM(Ref) bool& Show); // Widgets: Trees UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Trees") - static bool TreeNode(const FText Label); + static bool TreeNodeText(const FText Label); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Trees") - static bool TreeNodeString(const FString Label); + static bool TreeNode(const FString Label); UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Trees") static void TreePop(); + + // Widgets: Layouts + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Layouts") + static void SameLine(); + + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Layouts") + static void Separator(); + + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Layouts") + static void Indent(const float Width = 0); + + UFUNCTION(BlueprintCallable, Category = "ImGui|Widgets|Layouts") + static void Unindent(const float Width = 0); };