11#include "Net/UnrealNetwork.h"
12#include "Components/BoxComponent.h"
13#include "Components/WidgetComponent.h"
15#include "GameFramework/Character.h"
16#include "Kismet/GameplayStatics.h"
17#include "Kismet/KismetMathLibrary.h"
20UInteractableComponent::UInteractableComponent()
22 PrimaryComponentTick.bCanEverTick =
true;
25 bOriginalSimulatePhysics =
false;
26 OriginalCollisionType = ECollisionEnabled::NoCollision;
29 DetectionRange = CreateDefaultSubobject<UBoxComponent>(TEXT(
"DetectionRange"));
32 DetectionRange->SetBoxExtent(FVector(250.f));
33 DetectionRange->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
34 DetectionRange->SetCollisionResponseToAllChannels(ECR_Ignore);
35 DetectionRange->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
38 SetIsReplicatedByDefault(
true);
41void UInteractableComponent::BeginPlay()
45 AActor* Owner = GetOwner();
48 PRINTLOG( TEXT(
"UInteractableComponent::BeginPlay - GetOwner() returned nullptr!"));
52 this->InitDetectionRange();
55void UInteractableComponent::InitDetectionRange()
58 if (!DetectionRange->IsRegistered())
60 DetectionRange->RegisterComponent();
63 AActor* Owner = GetOwner();
66 if (!DetectionRange->IsAttachedTo(Owner->GetRootComponent()))
68 DetectionRange->AttachToComponent(
69 Owner->GetRootComponent(),
70 FAttachmentTransformRules::KeepRelativeTransform
75 FVector BoxExtent(DetectionDistance, DetectionDistance, DetectionDistance);
76 DetectionRange->SetBoxExtent(BoxExtent);
79 DetectionRange->OnComponentBeginOverlap.AddDynamic(
this, &UInteractableComponent::OnDetectionBeginOverlap);
80 DetectionRange->OnComponentEndOverlap.AddDynamic(
this, &UInteractableComponent::OnDetectionEndOverlap);
83void UInteractableComponent::TickComponent(
float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
85 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
88 BillboardInteractWidget();
91 if (bShowDetectionDebug && DetectionRange)
93 AActor* Owner = GetOwner();
94 if (Owner && IsValid(Owner))
96 FVector Center = Owner->GetActorLocation();
97 FVector BoxExtent = DetectionRange->GetScaledBoxExtent();
98 FQuat Rotation = Owner->GetActorQuat();
106 bCanInteract ? FColor::Green : FColor::
Yellow,
114 FString DebugText = FString::Printf(
115 TEXT(
"Detection: %.0f cm\n%s"),
122 Center + FVector(0, 0, BoxExtent.Z + 20.f),
134void UInteractableComponent::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps)
const
136 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
138 DOREPLIFETIME(UInteractableComponent, HoldingOwner);
144 DOREPLIFETIME(UInteractableComponent, bIsPickedUp);
147void UInteractableComponent::OnRep_HoldingOwner()
151 APlayerActor* MyPlayer = Cast<APlayerActor>(HoldingOwner);
155 GetOwner()->AttachToComponent(MyPlayer->
HoldPosition, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
158 if (GetOwner()->IsA(Aluggage::StaticClass()))
160 APlayerController* PC =
nullptr;
163 if (APawn* Pawn = Cast<APawn>(HoldingOwner))
165 PC = Cast<APlayerController>(Pawn->GetController());
170 PC = Cast<APlayerController>(HoldingOwner);
175 if (UTutorialComponent* Tutorial = PC->FindComponentByClass<UTutorialComponent>())
177 Tutorial->OnObjectPickedUp(GetOwner());
185 GetOwner()->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
195void UInteractableComponent::OnRep_IsPickedUp()
200 SetOutlineState(
false);
201 HideInteractWidget();
203 PRINTLOG(TEXT(
"OnRep_IsPickedUp: %s picked up (visual update)"), *GetOwner()->GetName());
208 SetOutlineState(
true);
210 PRINTLOG(TEXT(
"OnRep_IsPickedUp: %s dropped (visual update)"), *GetOwner()->GetName());
214void UInteractableComponent::PickUp(AActor* NewHoldingOwner)
217 if (!NewHoldingOwner)
219 PRINTLOG(TEXT(
"InteractableComponent::PickUp - NewHoldingOwner is null"));
225 PRINTLOG(TEXT(
"InteractableComponent::PickUp - Already picked up"));
230 Server_PickUp(NewHoldingOwner);
246bool UInteractableComponent::Server_PickUp_Validate(AActor* NewHoldingOwner)
249 if (!NewHoldingOwner)
251 PRINTLOG(TEXT(
"Server_PickUp_Validate: NewHoldingOwner is null"));
256 APlayerActor* PlayerActor = Cast<APlayerActor>(NewHoldingOwner);
257 if (!PlayerActor || !PlayerActor->GetController())
259 PRINTLOG(TEXT(
"Server_PickUp_Validate: Not a valid PlayerActor or no Controller"));
266 PRINTLOG(TEXT(
"Server_PickUp_Validate: Already picked up"));
271 if (HoldingOwner && HoldingOwner != NewHoldingOwner)
273 PRINTLOG(TEXT(
"Server_PickUp_Validate: Already held by %s"), *HoldingOwner->GetName());
278 AActor* Owner = GetOwner();
281 PRINTLOG(TEXT(
"Server_PickUp_Validate: Owner is null"));
285 float Distance = FVector::Dist(Owner->GetActorLocation(), NewHoldingOwner->GetActorLocation());
286 float MaxDistance = DetectionDistance * 1.5f;
288 if (Distance > MaxDistance)
290 PRINTLOG(TEXT(
"Server_PickUp_Validate: Too far! Distance: %.2f, Max: %.2f"), Distance, MaxDistance);
301void UInteractableComponent::Server_PickUp_Implementation(AActor* NewHoldingOwner)
304 HoldingOwner = NewHoldingOwner;
307 UPrimitiveComponent* PrimComp = GetOwnerPrimitiveComponent();
312 bOriginalSimulatePhysics = PrimComp->IsSimulatingPhysics();
313 OriginalCollisionType = PrimComp->GetCollisionEnabled();
316 PrimComp->SetSimulatePhysics(
false);
319 OnRep_HoldingOwner();
324 PRINTLOG(TEXT(
"Server_PickUp: %s picked up by %s"), *GetOwner()->GetName(), *NewHoldingOwner->GetName());
327void UInteractableComponent::Drop()
332 PRINTLOG(TEXT(
"InteractableComponent::Drop - Not picked up"));
345bool UInteractableComponent::Server_Drop_Validate()
350 PRINTLOG(TEXT(
"Server_Drop_Validate: Not picked up"));
357 PRINTLOG(TEXT(
"Server_Drop_Validate: HoldingOwner is null"));
368void UInteractableComponent::Server_Drop_Implementation()
371 GetOwner()->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
374 UPrimitiveComponent* PrimComp = GetOwnerPrimitiveComponent();
377 PrimComp->SetSimulatePhysics(bOriginalSimulatePhysics);
378 PrimComp->SetCollisionEnabled(OriginalCollisionType);
383 HoldingOwner =
nullptr;
385 PRINTLOG(TEXT(
"Server_Drop: %s dropped"), *GetOwner()->GetName());
389UPrimitiveComponent* UInteractableComponent::GetOwnerPrimitiveComponent()
const
391 AActor* Owner = GetOwner();
395 UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(Owner->GetRootComponent());
400 TArray<UActorComponent*> Components;
401 Owner->GetComponents(UPrimitiveComponent::StaticClass(), Components);
403 if (Components.Num() > 0)
406 PrimComp = Cast<UPrimitiveComponent>(Components[0]);
413void UInteractableComponent::ShowDebugInfo(AActor* ViewerActor)
415 if (!GetOwner() || !ViewerActor)
return;
418 FVector DebugLocation = GetOwner()->GetActorLocation() + FVector(0.f, 0.f, 100.f);
421 FString DebugMessage = InteractionPrompt;
429 bIsPickedUp ? FColor::Yellow : FColor::
Green,
436void UInteractableComponent::TriggerInteraction(AActor* Interactor)
438 if (!bCanInteract || !Interactor)
442 OnInteractionTriggered.Broadcast(Interactor);
444 PRINTLOG( TEXT(
"InteractableComponent::TriggerInteraction - %s triggered by %s"), *GetOwner()->GetName(), *Interactor->GetName());
447void UInteractableComponent::SetOutlineState(
bool bEnabled)
450 OnOutlineStateChanged.Broadcast(bEnabled);
453void UInteractableComponent::OnDetectionBeginOverlap(
454 UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
455 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
456 bool bFromSweep,
const FHitResult& SweepResult)
462 if (
auto Character = Cast<ACharacter>(OtherActor) )
464 if ( Character->IsPlayerControlled())
467 if (
auto InteractionSystem = Character->FindComponentByClass<UInteractionSystem>())
468 InteractionSystem->RegisterInteractable(
this);
470 bool ShowState = IsWidgetShowEnable(Character);
472 ShowInteractWidget();
476 SetOutlineState(
true);
478 PRINTLOG( TEXT(
"InteractableComponent: Player entered detection range - %s"), *GetOwner()->GetName());
483bool UInteractableComponent::IsWidgetShowEnable(
const ACharacter* Character)
const
486 return Character && Character->IsLocallyControlled();
489void UInteractableComponent::OnDetectionEndOverlap(
490 UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
491 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
497 ACharacter* Character = Cast<ACharacter>(OtherActor);
498 if (Character && Character->IsPlayerControlled())
501 UInteractionSystem* InteractionSystem = Character->FindComponentByClass<UInteractionSystem>();
502 if (InteractionSystem)
504 InteractionSystem->UnregisterInteractable(
this);
507 PRINTLOG( TEXT(
"InteractableComponent: Player left detection range - %s"), *GetOwner()->GetName());
510 FTimerHandle TimerHandle;
511 GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda([
this]
513 HideInteractWidget();
517 SetOutlineState(
false);
523void UInteractableComponent::InitWidget(UWidgetComponent* InWidgetComp)
525 this->WidgetComp = InWidgetComp;
530 WidgetComp->SetVisibility(
false);
532 if (
auto InteractWidget = Cast<UInteractWidget>(WidgetComp->GetWidget()))
533 InteractWidget->InitInfo(TEXT(
"E"), InteractionPrompt);
536void UInteractableComponent::ShowInteractWidget()
541 if (!WidgetComp->GetWidget())
544 WidgetComp->SetVisibility(
true);
547void UInteractableComponent::HideInteractWidget()
552 WidgetComp->SetVisibility(
false);
555void UInteractableComponent::BillboardInteractWidget()
562 if (!WidgetComp->IsVisible())
566 AActor* Camera = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0);
571 FRotator Rotation = UKismetMathLibrary::MakeRotFromXZ( -Camera->GetActorForwardVector(), Camera->GetActorUpVector() );
575 WidgetComp->SetWorldRotation(Rotation);
578void UInteractableComponent::UpdateInteractPrompt(
const FString& NewPrompt)
581 InteractionPrompt = NewPrompt;
588 if (
auto InteractWidget = Cast<UInteractWidget>(WidgetComp->GetWidget()))
589 InteractWidget->UpdateDesc(NewPrompt);
592void UInteractableComponent::SetWidgetVisibility(
bool bVisible)
598 ShowInteractWidget();
600 HideInteractWidget();
Declares the player-controlled character actor.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
Main character driven directly by the player.
TObjectPtr< class USceneComponent > HoldPosition