// Copyright Epic Games, Inc. All Rights Reserved. #include "Annotations/SmartObjectSlotEntranceAnnotation.h" #include "Engine/HitResult.h" #include "Engine/OverlapResult.h" #include "SmartObjectDefinition.h" #include "SmartObjectVisualizationContext.h" #include "PrimitiveDrawingUtils.h" // FPrimitiveDrawInterface #include "NavigationSystem.h" #include "NavigationData.h" #include "NavFilters/NavigationQueryFilter.h" #include "AI/Navigation/NavAgentInterface.h" #include "SmartObjectUserComponent.h" #if WITH_GAMEPLAY_DEBUGGER #include "GameplayDebuggerCategory.h" #endif #include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectSlotEntranceAnnotation) namespace UE::SmartObject::Annotations { const ANavigationData* GetDefaultNavData(const UWorld& World) { const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(&World); if (!NavSys) { return nullptr; } return NavSys->GetDefaultNavDataInstance(); } const ANavigationData* GetNavDataForActor(const UWorld& World, const AActor* UserActor) { if (const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent(&World)) { const INavAgentInterface* NavAgent = Cast(UserActor); if (NavAgent) { const FNavAgentProperties& NavAgentProps = NavAgent->GetNavAgentPropertiesRef(); return NavSys->GetNavDataForProps(NavAgentProps, NavAgent->GetNavAgentLocation()); } return NavSys->GetDefaultNavDataInstance(); } return nullptr; } bool ProjectNavigationLocation(const ANavigationData& NavData, const FVector Location, const FBox& SearchBounds, const FSharedConstNavQueryFilter& NavigationFilter, const AActor* InstigatorActor, FNavLocation& OutNavLocation) { return NavData.ProjectPoint(Location, OutNavLocation, SearchBounds.GetExtent(), NavigationFilter, InstigatorActor) && OutNavLocation.HasNodeRef() && SearchBounds.IsInsideOrOn(OutNavLocation.Location); } bool TraceGroundLocation(const UWorld& World, const FVector Location, const FBox& SearchBounds, const FSmartObjectTraceParams& TraceParameters, const FCollisionQueryParams& CollisionQueryParams, FVector& OutGroundLocation) { const FVector TraceStart(Location.X, Location.Y, SearchBounds.Max.Z); const FVector TraceEnd(Location.X, Location.Y, SearchBounds.Min.Z); FHitResult HitResult; bool bHasHit = false; constexpr float SphereRadius = 1.f; if (TraceParameters.Type == ESmartObjectTraceType::ByChannel) { bHasHit = World.SweepSingleByChannel( HitResult, TraceStart, TraceEnd, FQuat::Identity, UEngineTypes::ConvertToCollisionChannel(TraceParameters.TraceChannel), FCollisionShape::MakeSphere(SphereRadius), CollisionQueryParams); } else if (TraceParameters.Type == ESmartObjectTraceType::ByProfile) { bHasHit = World.SweepSingleByProfile( HitResult, TraceStart, TraceEnd, FQuat::Identity, TraceParameters.CollisionProfile.Name, FCollisionShape::MakeSphere(SphereRadius), CollisionQueryParams); } else if (TraceParameters.Type == ESmartObjectTraceType::ByObjectTypes) { const FCollisionObjectQueryParams ObjectQueryParams(TraceParameters.ObjectTypes); bHasHit = World.SweepSingleByObjectType( HitResult, TraceStart, TraceEnd, FQuat::Identity, ObjectQueryParams, FCollisionShape::MakeSphere(SphereRadius), CollisionQueryParams); } if (bHasHit) { OutGroundLocation = HitResult.Location; return true; } return false; } bool TestCollidersOverlap(const UWorld& World, TConstArrayView Colliders, const FSmartObjectTraceParams& TraceParameters, const FCollisionQueryParams& CollisionQueryParams) { bool bHasHit = false; TArray Overlaps; for (const FSmartObjectAnnotationCollider& Collider : Colliders) { Overlaps.Reset(); if (TraceParameters.Type == ESmartObjectTraceType::ByChannel) { bHasHit = World.OverlapMultiByChannel(Overlaps, Collider.Location, Collider.Rotation, UEngineTypes::ConvertToCollisionChannel(TraceParameters.TraceChannel), Collider.CollisionShape, CollisionQueryParams); } else if (TraceParameters.Type == ESmartObjectTraceType::ByProfile) { bHasHit = World.OverlapMultiByProfile(Overlaps, Collider.Location, Collider.Rotation, TraceParameters.CollisionProfile.Name, Collider.CollisionShape, CollisionQueryParams); } else if (TraceParameters.Type == ESmartObjectTraceType::ByObjectTypes) { // Overlap tests with object types will only ever return non-blocking results (due to historical reasons), so using the any variant here (blocking does not exist). const FCollisionObjectQueryParams ObjectQueryParams(TraceParameters.ObjectTypes); bHasHit = World.OverlapMultiByObjectType(Overlaps, Collider.Location, Collider.Rotation, ObjectQueryParams, Collider.CollisionShape, CollisionQueryParams); } if (bHasHit) { break; } } return bHasHit; } static constexpr FColor EntryColor(0, 64, 192); static constexpr FColor CollisionColor(255, 255, 255, 128); static constexpr FColor InvalidEntryColor(192, 32, 16); struct FVisualizationData { FVector SlotLocation; FVector MarkerLocation; FVector MarkerAxisX; FVector MarkerAxisY; FVector ValidatedLocation; FVector GroundLocationStart; FVector GroundLocationEnd; TArray Colliders; FLinearColor MarkerColor = EntryColor; FLinearColor ColliderColor = CollisionColor; }; void UpdateVisualizationLogic(const UWorld& World, const FSmartObjectSlotEntranceAnnotation& Annotation, const FTransform& SlotTransform, const FTransform& AnnotationTransform, const AActor* PreviewSmartObjectActor, const AActor* PreviewUserActor, TSubclassOf PreviewValidationFilterClass, FVisualizationData& OutData) { const USmartObjectSlotValidationFilter* PreviewValidationFilter = PreviewValidationFilterClass.GetDefaultObject(); if (!PreviewValidationFilter) { PreviewValidationFilter = GetDefault(); } const ANavigationData* DefaultNavData = GetDefaultNavData(World); const FSmartObjectSlotValidationParams& ValidationParams = PreviewValidationFilter->GetEntryValidationParams(); const FSmartObjectTraceParams& GroundTraceParameters = ValidationParams.GetGroundTraceParameters(); const FSmartObjectTraceParams& TransitionTraceParameters = ValidationParams.GetTransitionTraceParameters(); FCollisionQueryParams GroundTraceQueryParams(SCENE_QUERY_STAT(SmartObjectTrace), GroundTraceParameters.bTraceComplex); FCollisionQueryParams TransitionTraceQueryParams(SCENE_QUERY_STAT(SmartObjectTrace), TransitionTraceParameters.bTraceComplex); GroundTraceQueryParams.bIgnoreTouches = true; TransitionTraceQueryParams.bIgnoreTouches = true; if (PreviewSmartObjectActor) { GroundTraceQueryParams.AddIgnoredActor(PreviewSmartObjectActor); TransitionTraceQueryParams.AddIgnoredActor(PreviewSmartObjectActor); } if (PreviewUserActor) { GroundTraceQueryParams.AddIgnoredActor(PreviewUserActor); TransitionTraceQueryParams.AddIgnoredActor(PreviewUserActor); } const FVector AnnotationWorldLocation = AnnotationTransform.GetTranslation(); const FVector AxisX = AnnotationTransform.GetUnitAxis(EAxis::X); const FVector AxisY = AnnotationTransform.GetUnitAxis(EAxis::Y); OutData.SlotLocation = SlotTransform.GetLocation(); OutData.MarkerColor = EntryColor; OutData.ColliderColor = CollisionColor; OutData.MarkerLocation = AnnotationWorldLocation; OutData.MarkerAxisX = AxisX; OutData.MarkerAxisY = AxisY; const FBox SearchBounds(AnnotationWorldLocation - ValidationParams.GetSearchExtents(), AnnotationWorldLocation + ValidationParams.GetSearchExtents()); OutData.ValidatedLocation = AnnotationWorldLocation; // Validate location on navmesh // @todo: add visualization for missing navdata. if (DefaultNavData) { FSharedConstNavQueryFilter NavigationFilter; if (ValidationParams.GetNavigationFilter()) { NavigationFilter = UNavigationQueryFilter::GetQueryFilter(*DefaultNavData, nullptr, ValidationParams.GetNavigationFilter()); } FNavLocation NavLocation; if (ProjectNavigationLocation(*DefaultNavData, AnnotationWorldLocation, SearchBounds, NavigationFilter, /*RequesterActor*/nullptr, NavLocation)) { OutData.ValidatedLocation = NavLocation.Location; } else { OutData.MarkerColor = InvalidEntryColor; } } // Try to trace the slot on location on ground. if (Annotation.bTraceGroundLocation) { FVector GroundLocation; if (TraceGroundLocation(World, OutData.ValidatedLocation, SearchBounds, GroundTraceParameters, GroundTraceQueryParams, GroundLocation)) { OutData.ValidatedLocation = GroundLocation; } else { OutData.MarkerColor = InvalidEntryColor; } } if (Annotation.bCheckTransitionTrajectory) { Annotation.GetTrajectoryColliders(SlotTransform, OutData.Colliders); if (TestCollidersOverlap(World, OutData.Colliders, TransitionTraceParameters, TransitionTraceQueryParams)) { OutData.ColliderColor = InvalidEntryColor; } } OutData.GroundLocationStart = FVector(OutData.ValidatedLocation.X, OutData.ValidatedLocation.Y, FMath::Max(OutData.ValidatedLocation.Z, OutData.MarkerLocation.Z)); OutData.GroundLocationEnd = FVector(OutData.ValidatedLocation.X, OutData.ValidatedLocation.Y, FMath::Min(OutData.ValidatedLocation.Z, OutData.MarkerLocation.Z)); } } // UE::SmartObject::Annotations #if WITH_EDITOR void FSmartObjectSlotEntranceAnnotation::DrawVisualization(FSmartObjectVisualizationContext& VisContext) const { constexpr FVector::FReal MarkerRadius = 20.0; constexpr FVector::FReal TickSize = 5.0; constexpr FVector::FReal MinArrowDrawDistance = 20.0; constexpr float DepthBias = 2.0f; constexpr bool ScreenSpace = true; const FTransform SlotTransform = VisContext.Definition.GetSlotWorldTransform(VisContext.SlotIndex, VisContext.OwnerLocalToWorld); const FTransform AnnotationTransform = GetAnnotationWorldTransform(SlotTransform); UE::SmartObject::Annotations::FVisualizationData Data; UE::SmartObject::Annotations::UpdateVisualizationLogic(VisContext.World, *this, SlotTransform, AnnotationTransform, VisContext.PreviewActor, nullptr, VisContext.PreviewValidationFilterClass, Data); // Draw validated location in relation to the marker locations. if (FVector::Distance(Data.MarkerLocation, Data.ValidatedLocation) > UE_KINDA_SMALL_NUMBER) { VisContext.PDI->DrawTranslucentLine(Data.GroundLocationStart, Data.GroundLocationEnd, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(Data.ValidatedLocation - Data.MarkerAxisX * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisX * TickSize * 0.5, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(Data.ValidatedLocation - Data.MarkerAxisY * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisY * TickSize * 0.5, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); } if (VisContext.bIsAnnotationSelected) { Data.MarkerColor = VisContext.SelectedColor; } if (bIsEntry) { const FVector V0 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius; const FVector V1 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 + Data.MarkerAxisY * MarkerRadius; const FVector V2 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 - Data.MarkerAxisY * MarkerRadius; const FVector V3 = Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius; const FVector V4 = Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius; VisContext.PDI->DrawTranslucentLine(V0, V1, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(V0, V2, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(V1, V3, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(V2, V4, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); } if (bIsExit) { const FVector V1 = Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.75 + Data.MarkerAxisY * MarkerRadius; const FVector V2 = Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.75 - Data.MarkerAxisY * MarkerRadius; const FVector V3 = Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius; const FVector V4 = Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius; VisContext.PDI->DrawTranslucentLine(V1, V2, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(V1, V3, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(V2, V4, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace); } if (!Data.Colliders.IsEmpty()) { for (const FSmartObjectAnnotationCollider& Collider : Data.Colliders) { if (Collider.CollisionShape.IsCapsule()) { DrawWireCapsule(VisContext.PDI, Collider.Location, Collider.Rotation.GetAxisX(), Collider.Rotation.GetAxisY(), Collider.Rotation.GetAxisZ(), Data.ColliderColor, Collider.CollisionShape.GetCapsuleRadius(), Collider.CollisionShape.GetCapsuleHalfHeight(), 12, SDPG_World, 1.0f, DepthBias, ScreenSpace); } else if (Collider.CollisionShape.IsBox()) { DrawOrientedWireBox(VisContext.PDI, Collider.Location, Collider.Rotation.GetAxisX(), Collider.Rotation.GetAxisY(), Collider.Rotation.GetAxisZ(), Collider.CollisionShape.GetExtent(), Data.ColliderColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); } else if (Collider.CollisionShape.IsSphere()) { DrawWireSphere(VisContext.PDI, Collider.Location, Data.ColliderColor, Collider.CollisionShape.GetSphereRadius(), 12, SDPG_World, 1.0f, DepthBias, ScreenSpace); } } } // Tick at the center. VisContext.PDI->DrawTranslucentLine(Data.MarkerLocation - Data.MarkerAxisX * TickSize, Data.MarkerLocation + Data.MarkerAxisX * TickSize, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); VisContext.PDI->DrawTranslucentLine(Data.MarkerLocation - Data.MarkerAxisY * TickSize, Data.MarkerLocation + Data.MarkerAxisY * TickSize, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace); // Arrow pointing at the the slot, if far enough from the slot. if (FVector::DistSquared(Data.MarkerLocation, Data.SlotLocation) > FMath::Square(MinArrowDrawDistance)) { VisContext.DrawArrow(Data.MarkerLocation, Data.SlotLocation, Data.MarkerColor, 5.0f, 5.0f, /*DepthPrioGroup*/0, /*Thickness*/1.0f, DepthBias, ScreenSpace); } } void FSmartObjectSlotEntranceAnnotation::DrawVisualizationHUD(FSmartObjectVisualizationContext& VisContext) const { // @todo: move this into a setting. constexpr FVector::FReal MaxDrawDistance = 1500.0; constexpr FVector::FReal FadeDrawDistance = MaxDrawDistance * 0.75; const FTransform SlotTransform = VisContext.Definition.GetSlotWorldTransform(VisContext.SlotIndex, VisContext.OwnerLocalToWorld); const FTransform AnnotationTransform = GetAnnotationWorldTransform(SlotTransform); const FVector AnnotationWorldLocation = AnnotationTransform.GetTranslation(); const FVector::FReal Distance = VisContext.GetDistanceToCamera(AnnotationWorldLocation); if (Distance < MaxDrawDistance) { FString Text = FString::Printf(TEXT("S%d NAV%d \n"), VisContext.SlotIndex, VisContext.AnnotationIndex); if (SelectionPriority != ESmartObjectEntrancePriority::Normal) { Text += UEnum::GetDisplayValueAsText(SelectionPriority).ToString(); } if (Tags.IsValid()) { Text += Tags.ToString(); } FLinearColor Color = FLinearColor::White; Color.A = static_cast(FMath::Clamp(1.0 - (Distance - FadeDrawDistance) / (MaxDrawDistance - FadeDrawDistance), 0.0, 1.0)); VisContext.DrawString(AnnotationWorldLocation, *Text, Color); } } void FSmartObjectSlotEntranceAnnotation::AdjustWorldTransform(const FTransform& SlotTransform, const FVector& DeltaTranslation, const FRotator& DeltaRotation) { if (!DeltaTranslation.IsZero()) { const FVector LocalTranslation = SlotTransform.InverseTransformVector(DeltaTranslation); Offset += FVector3f(LocalTranslation); } if (!DeltaRotation.IsZero()) { const FRotator3f LocalRotation = FRotator3f(SlotTransform.InverseTransformRotation(DeltaRotation.Quaternion()).Rotator()); Rotation += LocalRotation; Rotation.Normalize(); } } #endif // WITH_EDITOR void FSmartObjectSlotEntranceAnnotation::GetTrajectoryColliders(const FTransform& SlotTransform, TArray& OutColliders) const { const FVector SlotWorldLocation = SlotTransform.GetLocation(); const FVector AnnotationWorldLocation = SlotTransform.TransformPosition(FVector(Offset)); const FVector SegmentStart = AnnotationWorldLocation + FVector(0, 0, TrajectoryStartHeightOffset); const FVector SegmentEnd = SlotWorldLocation + FVector(0, 0, TrajectorySlotHeightOffset); const FVector Center = (SegmentStart + SegmentEnd) * 0.5; const FVector Dir = SegmentEnd - SegmentStart; FVector Up; float Length = 0.0f; Dir.ToDirectionAndLength(Up, Length); FSmartObjectAnnotationCollider& Collider = OutColliders.AddDefaulted_GetRef(); Collider.Location = Center; Collider.Rotation = FQuat::FindBetweenNormals(FVector::UpVector, Up); Collider.CollisionShape = FCollisionShape::MakeCapsule(TransitionCheckRadius, Length * 0.5f + TransitionCheckRadius); } FTransform FSmartObjectSlotEntranceAnnotation::GetAnnotationWorldTransform(const FTransform& SlotTransform) const { const FTransform LocalTransform = FTransform(FRotator(Rotation), FVector(Offset)); return LocalTransform * SlotTransform; } FVector FSmartObjectSlotEntranceAnnotation::GetWorldLocation(const FTransform& SlotTransform) const { return SlotTransform.TransformPosition(FVector(Offset)); } FRotator FSmartObjectSlotEntranceAnnotation::GetWorldRotation(const FTransform& SlotTransform) const { return SlotTransform.TransformRotation(FQuat(Rotation.Quaternion())).Rotator(); } #if WITH_EDITORONLY_DATA void FSmartObjectSlotEntranceAnnotation::PostSerialize(const FArchive& Ar) { PRAGMA_DISABLE_DEPRECATION_WARNINGS if (Tag_DEPRECATED.IsValid()) { Tags.AddTag(Tag_DEPRECATED); Tag_DEPRECATED = FGameplayTag(); } PRAGMA_ENABLE_DEPRECATION_WARNINGS } #endif #if WITH_GAMEPLAY_DEBUGGER void FSmartObjectSlotEntranceAnnotation::CollectDataForGameplayDebugger(FSmartObjectAnnotationGameplayDebugContext& DebugContext) const { constexpr FVector::FReal MarkerRadius = 20.0; constexpr FVector::FReal TickSize = 5.0; constexpr FVector::FReal MinArrowDrawDistance = 20.0; const UWorld* World = DebugContext.Category.GetWorldFromReplicator(); if (!World) { return; } const FTransform AnnotationTransform = GetAnnotationWorldTransform(DebugContext.SlotTransform); TSubclassOf ValidationFilterClass; if (DebugContext.DebugActor) { // If user actor is present, try to query some data automatically from interfaces and components. if (!ValidationFilterClass.Get()) { if (const USmartObjectUserComponent* UserComponent = DebugContext.DebugActor->GetComponentByClass()) { if (UserComponent->GetValidationFilter().Get()) { ValidationFilterClass = UserComponent->GetValidationFilter(); } } } } UE::SmartObject::Annotations::FVisualizationData Data; UE::SmartObject::Annotations::UpdateVisualizationLogic(*World, *this, DebugContext.SlotTransform, AnnotationTransform, DebugContext.SmartObjectOwnerActor, DebugContext.DebugActor, ValidationFilterClass, Data); // Draw validated location in relation to the marker locations. if (FVector::Distance(Data.MarkerLocation, Data.ValidatedLocation) > UE_KINDA_SMALL_NUMBER) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegmentList( { Data.GroundLocationStart, Data.GroundLocationEnd, Data.ValidatedLocation - Data.MarkerAxisX * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisX * TickSize * 0.5, Data.ValidatedLocation - Data.MarkerAxisY * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisY * TickSize * 0.5 }, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true))); } if (bIsEntry) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePolyline({ Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 + Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius, Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 - Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius }, 4.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true))); } if (bIsExit) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePolyline({ Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.5 + Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.5 - Data.MarkerAxisY * MarkerRadius, Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius }, 4.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true))); } if (!Data.Colliders.IsEmpty()) { for (const FSmartObjectAnnotationCollider& Collider : Data.Colliders) { if (Collider.CollisionShape.IsCapsule()) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeCapsule(Collider.Location, Collider.Rotation.Rotator(), Collider.CollisionShape.GetCapsuleRadius(), Collider.CollisionShape.GetCapsuleHalfHeight(), Data.ColliderColor.ToFColor(/*bSRGB*/true))); } else if (Collider.CollisionShape.IsBox()) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeBox(Collider.Location, Collider.Rotation.Rotator(), Collider.CollisionShape.GetExtent(), Data.ColliderColor.ToFColor(/*bSRGB*/true))); } else if (Collider.CollisionShape.IsSphere()) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePoint(Collider.Location, Collider.CollisionShape.GetSphereRadius(), Data.ColliderColor.ToFColor(/*bSRGB*/true))); } } } // Tick at the center. DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegmentList( { Data.MarkerLocation - Data.MarkerAxisX * TickSize, Data.MarkerLocation + Data.MarkerAxisX * TickSize, Data.MarkerLocation - Data.MarkerAxisY * TickSize, Data.MarkerLocation + Data.MarkerAxisY * TickSize }, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true))); // Arrow pointing at the the slot, if far enough from the slot. if (FVector::DistSquared(Data.MarkerLocation, Data.SlotLocation) > FMath::Square(MinArrowDrawDistance)) { DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegment(Data.MarkerLocation, Data.SlotLocation, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true))); } } #endif // WITH_GAMEPLAY_DEBUGGER