14#include "DrawDebugHelpers.h"
19#include "Math/RotationMatrix.h"
20#include "Components/StaticMeshComponent.h"
21#include "Engine/StaticMesh.h"
22#include "Net/UnrealNetwork.h"
24UHookSystem::UHookSystem()
26 PrimaryComponentTick.bCanEverTick =
true;
27 PrimaryComponentTick.TickGroup = TG_PostUpdateWork;
28 SetIsReplicatedByDefault(
true);
31void UHookSystem::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps)
const
33 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
35 DOREPLIFETIME(UHookSystem, HookState);
36 DOREPLIFETIME(UHookSystem, HookedTarget);
37 DOREPLIFETIME(UHookSystem, HookProjectileLocation);
38 DOREPLIFETIME(UHookSystem, HookLaunchDirection);
41void UHookSystem::BeginPlay()
46 OwnerPlayer = Cast<APlayerActor>(GetOwner());
49 PRINTLOG(TEXT(
"UHookSystem: Owner is not APlayerActor!"));
50 SetComponentTickEnabled(
false);
55void UHookSystem::InitSystem(UStaticMeshComponent* InCableMesh, UStaticMeshComponent* InProjectileMesh)
57 CableMesh = InCableMesh;
58 ProjectileMesh = InProjectileMesh;
63 CableMesh->SetVisibility(
false);
64 CableMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
65 CableMesh->SetCastShadow(
false);
66 CableMesh->SetMobility(EComponentMobility::Movable);
67 CableMesh->SetUsingAbsoluteLocation(
true);
68 CableMesh->SetUsingAbsoluteRotation(
true);
70 if (UStaticMesh* Mesh = CableMesh->GetStaticMesh())
72 CableMeshBaseLength = FMath::Max(Mesh->GetBounds().BoxExtent.Z * 2.0f, 1.0f);
73 CableMeshBaseRadius = FMath::Max(Mesh->GetBounds().BoxExtent.X, Mesh->GetBounds().BoxExtent.Y);
76 PRINTLOG(TEXT(
"UHookSystem: InitSystem completed with Cable Mesh (straight line mode)"));
80 PRINTLOG(TEXT(
"UHookSystem: InitSystem - Cable Mesh is null!"));
85 ProjectileMesh->SetVisibility(
false);
86 PRINTLOG(TEXT(
"UHookSystem: InitSystem completed with ProjectileMesh"));
90 PRINTLOG(TEXT(
"UHookSystem: InitSystem - ProjectileMesh is null!"));
94void UHookSystem::TickComponent(
float DeltaTime, ELevelTick TickType,
95 FActorComponentTickFunction* ThisTickFunction)
97 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
102 case EHookState::Idle:
104 UpdateHookTargetUI();
107 case EHookState::Launching:
108 UpdateHookLaunching(DeltaTime);
112 case EHookState::Pulling:
113 UpdateHookPulling(DeltaTime);
119void UHookSystem::TryHook()
121 if (!OwnerPlayer || !OwnerPlayer->InteractionSystem)
123 PRINTLOG(TEXT(
"UHookSystem: OwnerPlayer or InteractionSystem is null!"));
128 if (HookState != EHookState::Idle)
130 PRINTLOG(TEXT(
"UHookSystem: Already in hook state!"));
135 FHitResult HitResult;
136 if (!PerformCenterLineTrace(HitResult))
138 PRINTLOG(TEXT(
"UHookSystem: LineTrace failed!"));
143 AActor* HitActor = HitResult.GetActor();
146 PRINTLOG(TEXT(
"UHookSystem: No actor hit!"));
150 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
151 if (!HookComp || !HookComp->bIsHookable)
153 PRINTLOG(TEXT(
"UHookSystem: Target is not hookable!"));
158 ServerTryHook(HitResult);
175void UHookSystem::ServerTryHook_Implementation(
const FHitResult& ClientHint)
178 if (!OwnerPlayer || !OwnerPlayer->HasAuthority())
180 PRINTLOG(TEXT(
"ServerTryHook: No authority"));
185 FHitResult ServerHit;
186 if (!PerformServerLineTrace(ServerHit))
188 PRINTLOG(TEXT(
"ServerTryHook: Server-side line trace failed"));
193 AActor* HitActor = ServerHit.GetActor();
196 PRINTLOG(TEXT(
"ServerTryHook: No actor hit"));
200 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
201 if (!HookComp || !HookComp->bIsHookable)
203 PRINTLOG(TEXT(
"ServerTryHook: Target is not hookable"));
208 float Distance = FVector::Dist(OwnerPlayer->GetActorLocation(), HitActor->GetActorLocation());
209 if (Distance > MaxHookDistance)
211 PRINTLOG(TEXT(
"ServerTryHook: Target too far! Distance: %.2f, Max: %.2f"), Distance, MaxHookDistance);
216 Aluggage* Luggage = Cast<Aluggage>(HitActor);
222 PRINTLOG(TEXT(
"ServerTryHook: Luggage is already picked up"));
229 PRINTLOG(TEXT(
"ServerTryHook: Luggage is already hooked by %s"),
238 PRINTLOG(TEXT(
"ServerTryHook: %s set as hooked by %s"), *Luggage->GetName(), *OwnerPlayer->GetName());
242 StartHook(ServerHit);
245void UHookSystem::StartHook(
const FHitResult&
Hit)
247 AActor* TargetActor =
Hit.GetActor();
250 PRINTLOG(TEXT(
"UHookSystem: StartHook - Target actor is null!"));
255 HookState = EHookState::Launching;
256 HookedTarget = TargetActor;
259 UPrimitiveComponent* RootPrimitive = Cast<UPrimitiveComponent>(HookedTarget->GetRootComponent());
263 OriginalCollisionEnabled = RootPrimitive->GetCollisionEnabled();
266 if (RootPrimitive->IsSimulatingPhysics())
268 RootPrimitive->SetSimulatePhysics(
false);
270 RootPrimitive->SetCollisionEnabled(ECollisionEnabled::NoCollision);
272 PRINTLOG(TEXT(
"UHookSystem: Physics and collision disabled for %s"), *HookedTarget->GetName());
276 HookProjectileLocation = OwnerPlayer->GetActorLocation();
277 HookLaunchDirection = (
Hit.Location - HookProjectileLocation).GetSafeNormal();
282 CableMesh->SetVisibility(
true);
287 ProjectileMesh->SetVisibility(
true);
288 ProjectileMesh->SetWorldLocation(HookProjectileLocation);
291 PRINTLOG(TEXT(
"UHookSystem: Hook launched toward %s"), *HookedTarget->GetName());
298void UHookSystem::ReleaseHook()
301 if (OwnerPlayer && !OwnerPlayer->HasAuthority())
308 if (HookState != EHookState::Idle)
311 if (HookedTarget && IsValid(HookedTarget))
313 Aluggage* Luggage = Cast<Aluggage>(HookedTarget);
324 UPrimitiveComponent* RootPrimitive = Cast<UPrimitiveComponent>(HookedTarget->GetRootComponent());
328 RootPrimitive->SetPhysicsLinearVelocity(FVector::ZeroVector,
false);
329 RootPrimitive->SetPhysicsAngularVelocityInDegrees(FVector::ZeroVector,
false);
332 RootPrimitive->SetCollisionEnabled(OriginalCollisionEnabled);
335 RootPrimitive->SetSimulatePhysics(
true);
339 HookState = EHookState::Idle;
340 HookedTarget =
nullptr;
344 CableMesh->SetVisibility(
false);
347 ProjectileMesh->SetVisibility(
false);
351void UHookSystem::ServerReleaseHook_Implementation()
360void UHookSystem::DetectHookTarget()
362 if (!OwnerPlayer || !OwnerPlayer->InteractionSystem)
366 FHitResult HitResult;
367 if (OwnerPlayer->InteractionSystem->PerformCenterLineTrace(HitResult))
369 AActor* HitActor = HitResult.GetActor();
372 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
373 if (HookComp && HookComp->bIsHookable)
376 if (CurHookTarget_Luggage && CurHookTarget != HitActor)
378 CurHookTarget_Luggage->OnOutlineStateChanged(
false);
379 PRINTLOG(TEXT(
"DetectHookTarget: Previous target %s OutlineOff"), *CurHookTarget_Luggage->GetName());
382 CurHookTarget = HitActor;
383 CurHookTarget_Luggage = Cast<Aluggage>(HitActor);
390 if (CurHookTarget_Luggage)
392 CurHookTarget_Luggage->OnOutlineStateChanged(
false);
393 PRINTLOG(TEXT(
"DetectHookTarget: Lost target %s, OutlineOff"), *CurHookTarget_Luggage->GetName());
396 CurHookTarget =
nullptr;
397 CurHookTarget_Luggage =
nullptr;
401bool UHookSystem::PerformCenterLineTrace(FHitResult& OutHit)
407 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
411 APlayerCameraManager* CameraManager = PC->PlayerCameraManager;
415 FVector CameraLocation = CameraManager->GetCameraLocation();
416 FVector CameraForward = CameraManager->GetCameraRotation().Vector();
419 FVector TraceStart = CameraLocation;
420 FVector TraceEnd = TraceStart + (CameraForward * InteractionDistance);
423 bool bHit = GetWorld()->LineTraceSingleByChannel(
424 OutHit, TraceStart, TraceEnd, ECC_Visibility
431 GetWorld(), TraceStart, TraceEnd,
432 bHit ? FColor::Green : FColor::
Red,
446bool UHookSystem::PerformServerLineTrace(FHitResult& OutHit)
450 PRINTLOG(TEXT(
"PerformServerLineTrace: OwnerPlayer is null"));
455 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
458 PRINTLOG(TEXT(
"PerformServerLineTrace: PlayerController is null"));
463 FVector CameraLocation;
464 FRotator CameraRotation;
465 PC->GetPlayerViewPoint(CameraLocation, CameraRotation);
467 FVector TraceStart = CameraLocation;
468 FVector TraceEnd = TraceStart + (CameraRotation.Vector() * InteractionDistance);
471 bool bHit = GetWorld()->LineTraceSingleByChannel(
472 OutHit, TraceStart, TraceEnd, ECC_Visibility
479 GetWorld(), TraceStart, TraceEnd,
480 bHit ? FColor::Green : FColor::
Red,
487 GetWorld(), OutHit.Location,
497 PRINTLOG(TEXT(
"PerformServerLineTrace: Hit %s at distance %.2f"),
498 *OutHit.GetActor()->GetName(),
499 FVector::Dist(CameraLocation, OutHit.Location));
509void UHookSystem::UpdateHookTargetUI()
515 if (
auto MainWidget = OwnerPlayer->GetMainWidget())
518 MainWidget->UpdateHookState(CurHookTarget !=
nullptr);
525 if ( CurHookTarget_Luggage !=
nullptr )
527 CurHookTarget_Luggage->OnOutlineStateChanged(
true);
533 FVector TargetLocation = CurHookTarget->GetActorLocation();
550void UHookSystem::UpdateHookLaunching(
float DeltaTime)
552 if (!HookedTarget || !IsValid(HookedTarget))
554 PRINTLOG(TEXT(
"UHookSystem: Target lost during launch"));
560 if (!OwnerPlayer || !OwnerPlayer->HasAuthority())
566 HookProjectileLocation += HookLaunchDirection * LaunchSpeed * DeltaTime;
569 FVector TargetLocation = HookedTarget->GetActorLocation();
572 float DistanceToTarget = FVector::Dist(HookProjectileLocation, TargetLocation);
574 if (DistanceToTarget < 50.0f)
577 HookState = EHookState::Pulling;
578 PRINTLOG(TEXT(
"UHookSystem: Hook reached target, start pulling"));
583 float DistanceFromPlayer = FVector::Dist(HookProjectileLocation, OwnerPlayer->GetActorLocation());
584 if (DistanceFromPlayer > MaxHookDistance)
586 PRINTLOG(TEXT(
"UHookSystem: Hook exceeded max distance"));
596 HookProjectileLocation,
608void UHookSystem::UpdateHookPulling(
float DeltaTime)
611 if (!HookedTarget || !IsValid(HookedTarget))
613 PRINTLOG(TEXT(
"UHookSystem: Hooked target became invalid, releasing hook"));
625 FVector PlayerLocation = OwnerPlayer->GetActorLocation();
626 FVector PlayerForward = OwnerPlayer->GetActorForwardVector();
627 FVector TargetLocation = PlayerLocation + (PlayerForward * DesiredDistance);
630 FVector CurrentLocation = HookedTarget->GetActorLocation();
633 TargetLocation.Z = CurrentLocation.Z;
636 float DistanceToTarget = FVector::Dist(CurrentLocation, TargetLocation);
639 if (OwnerPlayer->HasAuthority())
642 if (DistanceToTarget < CompleteThreshold)
645 HookedTarget->SetActorLocation(TargetLocation);
646 PRINTLOG(TEXT(
"UHookSystem: Hook completed, placed at exact target location"));
653 FVector NewLocation = FMath::VInterpTo(
661 HookedTarget->SetActorLocation(NewLocation);
667 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
670 if (UTutorialComponent* Tutorial = PC->FindComponentByClass<UTutorialComponent>())
672 Tutorial->OnObjectGrabbed(HookedTarget);
705void UHookSystem::UpdateCable()
711 FVector CableStart = OwnerPlayer->GetActorLocation();
715 if (HookState == EHookState::Launching)
717 CableEnd = HookProjectileLocation;
719 else if (HookState == EHookState::Pulling && HookedTarget)
721 CableEnd = HookedTarget->GetActorLocation();
727 CableMesh->SetVisibility(
false);
735 UpdateCableMeshTransform(CableStart, CableEnd);
741 ProjectileMesh->SetWorldLocation(CableEnd);
773void UHookSystem::UpdateCableMeshTransform(
const FVector& CableStart,
const FVector& CableEnd)
780 FVector CableDelta = CableEnd - CableStart;
781 float CableLength = CableDelta.Size();
782 if (CableLength <= KINDA_SMALL_NUMBER)
784 CableMesh->SetVisibility(
false);
788 FVector CableMidpoint = CableStart + (CableDelta * 0.5f);
789 CableMesh->SetWorldLocation(CableMidpoint);
791 FVector Direction = CableDelta / CableLength;
792 FRotator CableRotation = FRotationMatrix::MakeFromZ(Direction).Rotator();
793 CableMesh->SetWorldRotation(CableRotation);
795 const float LengthScale = CableLength / FMath::Max(CableMeshBaseLength, KINDA_SMALL_NUMBER);
796 const float TargetThickness = FMath::Max(CableThickness, 1.0f);
797 const float MeshDiameter = FMath::Max(CableMeshBaseRadius * 2.0f, KINDA_SMALL_NUMBER);
798 const float RadiusScale = TargetThickness / MeshDiameter;
800 CableMesh->SetWorldScale3D(FVector(RadiusScale, RadiusScale, LengthScale));
801 CableMesh->SetVisibility(
true);
804void UHookSystem::OnRep_HookState()
809 case EHookState::Idle:
813 CableMesh->SetVisibility(
false);
817 ProjectileMesh->SetVisibility(
false);
821 case EHookState::Launching:
822 case EHookState::Pulling:
826 CableMesh->SetVisibility(
true);
830 ProjectileMesh->SetVisibility(
true);
835 PRINTLOG(TEXT(
"UHookSystem: OnRep_HookState - State changed to %d"), (int32)HookState);
838void UHookSystem::OnRep_HookProjectileLocation()
841 if (ProjectileMesh && HookState == EHookState::Launching)
843 ProjectileMesh->SetWorldLocation(HookProjectileLocation);
Declares the player-controlled character actor.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
UGameSoundManager 클래스를 선언합니다.
bool bIsBeingHooked
훅 중 플래그 (복제됨)
TObjectPtr< class UInteractableComponent > InteractableComp
TObjectPtr< AActor > HookedBy
현재 이 Luggage를 훅하고 있는 플레이어