// Copyright ExByte Studios. Item Interaction Ecosystem. #include "InteractionTraceComponent.h" #include "InteractableComponent.h" #include "GameFramework/Pawn.h" #include "GameFramework/PlayerController.h" #include "Engine/World.h" UInteractionTraceComponent::UInteractionTraceComponent() { PrimaryComponentTick.bCanEverTick = true; PrimaryComponentTick.TickInterval = 0.1f; // 10 Hz is plenty for prompts SetIsReplicatedByDefault(false); // purely local } void UInteractionTraceComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); TickTrace(); } bool UInteractionTraceComponent::GetViewPoint(FVector& OutLocation, FRotator& OutRotation) const { const APawn* Pawn = Cast(GetOwner()); const APlayerController* PC = Pawn ? Cast(Pawn->GetController()) : nullptr; if (!PC || !PC->IsLocalController()) { return false; } PC->GetPlayerViewPoint(OutLocation, OutRotation); return true; } void UInteractionTraceComponent::TickTrace() { FVector ViewLoc; FRotator ViewRot; if (!GetViewPoint(ViewLoc, ViewRot)) { SetTarget(nullptr, nullptr); return; } const FVector End = ViewLoc + ViewRot.Vector() * TraceDistance; FCollisionQueryParams Params(SCENE_QUERY_STAT(InteractionTrace), false, GetOwner()); Params.bTraceComplex = false; FHitResult Hit; const bool bHit = GetWorld()->SweepSingleByChannel( Hit, ViewLoc, End, FQuat::Identity, TraceChannel, FCollisionShape::MakeSphere(TraceRadius), Params); AActor* HitActor = bHit ? Hit.GetActor() : nullptr; UInteractableComponent* Interactable = HitActor ? HitActor->FindComponentByClass() : nullptr; if (Interactable) { // Distance gate against the component's own reach. const float DistSq = FVector::DistSquared(ViewLoc, Hit.ImpactPoint); if (DistSq > FMath::Square(Interactable->InteractionDistance)) { Interactable = nullptr; HitActor = nullptr; } } else { HitActor = nullptr; } LastHitLocation = Hit.ImpactPoint; LastHitNormal = Hit.ImpactNormal; SetTarget(HitActor, Interactable); } void UInteractionTraceComponent::SetTarget(AActor* NewTarget, UInteractableComponent* NewInteractable) { if (CurrentTarget == NewTarget && CurrentInteractable == NewInteractable) { return; } CurrentTarget = NewTarget; CurrentInteractable = NewInteractable; OnTargetChanged.Broadcast(NewTarget, NewInteractable); }