KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
UHookSystem.cpp
이 파일의 문서화 페이지로 가기
1// Copyright (c) 2025 Doppleddiggong. All rights reserved. Unauthorized copying, modification, or distribution of this file, via any medium is strictly prohibited. Proprietary and confidential.
2
8#include "UHookSystem.h"
9#include "APlayerActor.h"
10#include "UInteractionSystem.h"
11#include "UHookComponent.h"
12#include "UMainWidget.h"
13#include "GameLogging.h"
14#include "DrawDebugHelpers.h"
16#include "luggage.h"
17#include "TutorialComponent.h"
18#include "UGameSoundManager.h"
19#include "Math/RotationMatrix.h"
20#include "Components/StaticMeshComponent.h"
21#include "Engine/StaticMesh.h"
22#include "Net/UnrealNetwork.h"
23
24UHookSystem::UHookSystem()
25{
26 PrimaryComponentTick.bCanEverTick = true;
27 PrimaryComponentTick.TickGroup = TG_PostUpdateWork;
28 SetIsReplicatedByDefault(true);
29}
30
31void UHookSystem::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
32{
33 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
34
35 DOREPLIFETIME(UHookSystem, HookState);
36 DOREPLIFETIME(UHookSystem, HookedTarget);
37 DOREPLIFETIME(UHookSystem, HookProjectileLocation);
38 DOREPLIFETIME(UHookSystem, HookLaunchDirection);
39}
40
41void UHookSystem::BeginPlay()
42{
43 Super::BeginPlay();
44
45 // Owner PlayerActor 캐싱
46 OwnerPlayer = Cast<APlayerActor>(GetOwner());
47 if (!OwnerPlayer)
48 {
49 PRINTLOG(TEXT("UHookSystem: Owner is not APlayerActor!"));
50 SetComponentTickEnabled(false);
51 return;
52 }
53}
54
55void UHookSystem::InitSystem(UStaticMeshComponent* InCableMesh, UStaticMeshComponent* InProjectileMesh)
56{
57 CableMesh = InCableMesh;
58 ProjectileMesh = InProjectileMesh;
59
60 if (CableMesh)
61 {
62 // Cable Component 초기 설정 - 직선으로 나가도록
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);
69
70 if (UStaticMesh* Mesh = CableMesh->GetStaticMesh())
71 {
72 CableMeshBaseLength = FMath::Max(Mesh->GetBounds().BoxExtent.Z * 2.0f, 1.0f);
73 CableMeshBaseRadius = FMath::Max(Mesh->GetBounds().BoxExtent.X, Mesh->GetBounds().BoxExtent.Y);
74 }
75
76 PRINTLOG(TEXT("UHookSystem: InitSystem completed with Cable Mesh (straight line mode)"));
77 }
78 else
79 {
80 PRINTLOG(TEXT("UHookSystem: InitSystem - Cable Mesh is null!"));
81 }
82
83 if (ProjectileMesh)
84 {
85 ProjectileMesh->SetVisibility(false);
86 PRINTLOG(TEXT("UHookSystem: InitSystem completed with ProjectileMesh"));
87 }
88 else
89 {
90 PRINTLOG(TEXT("UHookSystem: InitSystem - ProjectileMesh is null!"));
91 }
92}
93
94void UHookSystem::TickComponent(float DeltaTime, ELevelTick TickType,
95 FActorComponentTickFunction* ThisTickFunction)
96{
97 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
98
99 // 상태별 업데이트
100 switch (HookState)
101 {
102 case EHookState::Idle:
103 DetectHookTarget();
104 UpdateHookTargetUI();
105 break;
106
107 case EHookState::Launching:
108 UpdateHookLaunching(DeltaTime);
109 UpdateCable();
110 break;
111
112 case EHookState::Pulling:
113 UpdateHookPulling(DeltaTime);
114 UpdateCable();
115 break;
116 }
117}
118
119void UHookSystem::TryHook()
120{
121 if (!OwnerPlayer || !OwnerPlayer->InteractionSystem)
122 {
123 PRINTLOG(TEXT("UHookSystem: OwnerPlayer or InteractionSystem is null!"));
124 return;
125 }
126
127 // 이미 Hook 중이면 무시
128 if (HookState != EHookState::Idle)
129 {
130 PRINTLOG(TEXT("UHookSystem: Already in hook state!"));
131 return;
132 }
133
134 // InteractionSystem의 LineTrace 사용
135 FHitResult HitResult;
136 if (!PerformCenterLineTrace(HitResult))
137 {
138 PRINTLOG(TEXT("UHookSystem: LineTrace failed!"));
139 return;
140 }
141
142 // Hit된 Actor가 UHookComponent를 가지고 있는지 확인
143 AActor* HitActor = HitResult.GetActor();
144 if (!HitActor)
145 {
146 PRINTLOG(TEXT("UHookSystem: No actor hit!"));
147 return;
148 }
149
150 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
151 if (!HookComp || !HookComp->bIsHookable)
152 {
153 PRINTLOG(TEXT("UHookSystem: Target is not hookable!"));
154 return;
155 }
156
157 // Server에 Hook 요청
158 ServerTryHook(HitResult);
159
160 UGameSoundManager::Get(GetWorld())->PlaySound2D( EGameSoundType::Hook );
161}
162
175void UHookSystem::ServerTryHook_Implementation(const FHitResult& ClientHint)
176{
177 // 서버 권한 체크
178 if (!OwnerPlayer || !OwnerPlayer->HasAuthority())
179 {
180 PRINTLOG(TEXT("ServerTryHook: No authority"));
181 return;
182 }
183
184 // [개선 1] 서버에서 라인트레이스 재실행
185 FHitResult ServerHit;
186 if (!PerformServerLineTrace(ServerHit))
187 {
188 PRINTLOG(TEXT("ServerTryHook: Server-side line trace failed"));
189 return;
190 }
191
192 // [개선 2] 유효성 검증
193 AActor* HitActor = ServerHit.GetActor();
194 if (!HitActor)
195 {
196 PRINTLOG(TEXT("ServerTryHook: No actor hit"));
197 return;
198 }
199
200 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
201 if (!HookComp || !HookComp->bIsHookable)
202 {
203 PRINTLOG(TEXT("ServerTryHook: Target is not hookable"));
204 return;
205 }
206
207 // [개선 3] 거리 검증
208 float Distance = FVector::Dist(OwnerPlayer->GetActorLocation(), HitActor->GetActorLocation());
209 if (Distance > MaxHookDistance)
210 {
211 PRINTLOG(TEXT("ServerTryHook: Target too far! Distance: %.2f, Max: %.2f"), Distance, MaxHookDistance);
212 return;
213 }
214
215 // [개선 4] Luggage인 경우 추가 검증
216 Aluggage* Luggage = Cast<Aluggage>(HitActor);
217 if (Luggage)
218 {
219 // 픽업 중인 Luggage는 훅 불가
220 if (Luggage->InteractableComp && Luggage->InteractableComp->IsPickedUp())
221 {
222 PRINTLOG(TEXT("ServerTryHook: Luggage is already picked up"));
223 return;
224 }
225
226 // [개선 5] 이미 다른 플레이어가 훅 중인지 확인
227 if (Luggage->bIsBeingHooked && Luggage->HookedBy != OwnerPlayer)
228 {
229 PRINTLOG(TEXT("ServerTryHook: Luggage is already hooked by %s"),
230 Luggage->HookedBy ? *Luggage->HookedBy->GetName() : TEXT("Unknown"));
231 return;
232 }
233
234 // 훅 플래그 설정 (복제됨)
235 Luggage->bIsBeingHooked = true;
236 Luggage->HookedBy = OwnerPlayer;
237
238 PRINTLOG(TEXT("ServerTryHook: %s set as hooked by %s"), *Luggage->GetName(), *OwnerPlayer->GetName());
239 }
240
241 // Hook 시작 (서버 HitResult 사용)
242 StartHook(ServerHit);
243}
244
245void UHookSystem::StartHook(const FHitResult& Hit)
246{
247 AActor* TargetActor = Hit.GetActor();
248 if (!TargetActor)
249 {
250 PRINTLOG(TEXT("UHookSystem: StartHook - Target actor is null!"));
251 return;
252 }
253
254 // 발사 상태로 전환
255 HookState = EHookState::Launching;
256 HookedTarget = TargetActor;
257
258 // 물리 객체인 경우 물리 및 충돌 끄기 (Hook 중에는 방해 없이 부드럽게 이동)
259 UPrimitiveComponent* RootPrimitive = Cast<UPrimitiveComponent>(HookedTarget->GetRootComponent());
260 if (RootPrimitive)
261 {
262 // 원래 충돌 상태 저장
263 OriginalCollisionEnabled = RootPrimitive->GetCollisionEnabled();
264
265 // 물리와 충돌 모두 끄기
266 if (RootPrimitive->IsSimulatingPhysics())
267 {
268 RootPrimitive->SetSimulatePhysics(false);
269 }
270 RootPrimitive->SetCollisionEnabled(ECollisionEnabled::NoCollision);
271
272 PRINTLOG(TEXT("UHookSystem: Physics and collision disabled for %s"), *HookedTarget->GetName());
273 }
274
275 // 발사 위치 및 방향 설정
276 HookProjectileLocation = OwnerPlayer->GetActorLocation();
277 HookLaunchDirection = (Hit.Location - HookProjectileLocation).GetSafeNormal();
278
279 // Cable 및 Projectile Mesh 표시
280 if (CableMesh)
281 {
282 CableMesh->SetVisibility(true);
283 }
284
285 if (ProjectileMesh)
286 {
287 ProjectileMesh->SetVisibility(true);
288 ProjectileMesh->SetWorldLocation(HookProjectileLocation);
289 }
290
291 PRINTLOG(TEXT("UHookSystem: Hook launched toward %s"), *HookedTarget->GetName());
292}
293
298void UHookSystem::ReleaseHook()
299{
300 // 클라이언트에서 호출 시 Server에 요청
301 if (OwnerPlayer && !OwnerPlayer->HasAuthority())
302 {
303 ServerReleaseHook();
304 return;
305 }
306
307 // 서버에서 실행
308 if (HookState != EHookState::Idle)
309 {
310 // [개선] Luggage 훅 플래그 해제
311 if (HookedTarget && IsValid(HookedTarget))
312 {
313 Aluggage* Luggage = Cast<Aluggage>(HookedTarget);
314 if (Luggage)
315 {
316 // 훅 플래그 해제 (복제됨)
317 Luggage->bIsBeingHooked = false;
318 Luggage->HookedBy = nullptr;
319
320 Luggage->PlayTTSAudio();
321 }
322
323 // 물리와 충돌 복원
324 UPrimitiveComponent* RootPrimitive = Cast<UPrimitiveComponent>(HookedTarget->GetRootComponent());
325 if (RootPrimitive)
326 {
327 // 속도를 초기화한 후 물리 켜기 (튕겨나가는 것 방지)
328 RootPrimitive->SetPhysicsLinearVelocity(FVector::ZeroVector, false);
329 RootPrimitive->SetPhysicsAngularVelocityInDegrees(FVector::ZeroVector, false);
330
331 // 충돌 복원
332 RootPrimitive->SetCollisionEnabled(OriginalCollisionEnabled);
333
334 // 물리 복원
335 RootPrimitive->SetSimulatePhysics(true);
336 }
337 }
338
339 HookState = EHookState::Idle;
340 HookedTarget = nullptr;
341
342 // Cable 및 Projectile Mesh 숨김
343 if (CableMesh)
344 CableMesh->SetVisibility(false);
345
346 if (ProjectileMesh)
347 ProjectileMesh->SetVisibility(false);
348 }
349}
350
351void UHookSystem::ServerReleaseHook_Implementation()
352{
353 ReleaseHook();
354}
355
360void UHookSystem::DetectHookTarget()
361{
362 if (!OwnerPlayer || !OwnerPlayer->InteractionSystem)
363 return;
364
365 // LineTrace로 타겟 감지
366 FHitResult HitResult;
367 if (OwnerPlayer->InteractionSystem->PerformCenterLineTrace(HitResult))
368 {
369 AActor* HitActor = HitResult.GetActor();
370 if (HitActor)
371 {
372 UHookComponent* HookComp = HitActor->FindComponentByClass<UHookComponent>();
373 if (HookComp && HookComp->bIsHookable)
374 {
375 // [개선] 타겟이 바뀌면 이전 타겟의 Outline 끄기
376 if (CurHookTarget_Luggage && CurHookTarget != HitActor)
377 {
378 CurHookTarget_Luggage->OnOutlineStateChanged(false);
379 PRINTLOG(TEXT("DetectHookTarget: Previous target %s OutlineOff"), *CurHookTarget_Luggage->GetName());
380 }
381
382 CurHookTarget = HitActor;
383 CurHookTarget_Luggage = Cast<Aluggage>(HitActor);
384 return;
385 }
386 }
387 }
388
389 // [개선] 타겟을 잃었을 때 이전 타겟의 Outline 끄기
390 if (CurHookTarget_Luggage)
391 {
392 CurHookTarget_Luggage->OnOutlineStateChanged(false);
393 PRINTLOG(TEXT("DetectHookTarget: Lost target %s, OutlineOff"), *CurHookTarget_Luggage->GetName());
394 }
395
396 CurHookTarget = nullptr;
397 CurHookTarget_Luggage = nullptr;
398}
399
400
401bool UHookSystem::PerformCenterLineTrace(FHitResult& OutHit)
402{
403 if (!OwnerPlayer)
404 return false;
405
406 // 플레이어 컨트롤러 가져오기
407 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
408 if (!PC)
409 return false;
410
411 APlayerCameraManager* CameraManager = PC->PlayerCameraManager;
412 if (!CameraManager)
413 return false;
414
415 FVector CameraLocation = CameraManager->GetCameraLocation();
416 FVector CameraForward = CameraManager->GetCameraRotation().Vector();
417
418 // 레이 트레이스 시작/끝 지점
419 FVector TraceStart = CameraLocation;
420 FVector TraceEnd = TraceStart + (CameraForward * InteractionDistance);
421
422 // Ray trace 실행
423 bool bHit = GetWorld()->LineTraceSingleByChannel(
424 OutHit, TraceStart, TraceEnd, ECC_Visibility
425 );
426
427 // 디버그 라인
428 if (bShowDebugInfo)
429 {
430 DrawDebugLine(
431 GetWorld(), TraceStart, TraceEnd,
432 bHit ? FColor::Green : FColor::Red,
433 false, 0.0f, 0, 0.5f
434 );
435 }
436
437 return bHit;
438}
439
446bool UHookSystem::PerformServerLineTrace(FHitResult& OutHit)
447{
448 if (!OwnerPlayer)
449 {
450 PRINTLOG(TEXT("PerformServerLineTrace: OwnerPlayer is null"));
451 return false;
452 }
453
454 // 플레이어 컨트롤러 가져오기
455 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
456 if (!PC)
457 {
458 PRINTLOG(TEXT("PerformServerLineTrace: PlayerController is null"));
459 return false;
460 }
461
462 // 서버 기준 카메라 위치/방향 사용
463 FVector CameraLocation;
464 FRotator CameraRotation;
465 PC->GetPlayerViewPoint(CameraLocation, CameraRotation);
466
467 FVector TraceStart = CameraLocation;
468 FVector TraceEnd = TraceStart + (CameraRotation.Vector() * InteractionDistance);
469
470 // LineTrace 실행
471 bool bHit = GetWorld()->LineTraceSingleByChannel(
472 OutHit, TraceStart, TraceEnd, ECC_Visibility
473 );
474
475 // 디버그 라인 (서버)
476 if (bShowDebugInfo)
477 {
478 DrawDebugLine(
479 GetWorld(), TraceStart, TraceEnd,
480 bHit ? FColor::Green : FColor::Red,
481 false, 1.0f, 0, 2.0f
482 );
483
484 if (bHit)
485 {
486 DrawDebugSphere(
487 GetWorld(), OutHit.Location,
488 20.0f, 8,
489 FColor::Yellow,
490 false, 1.0f, 0, 2.0f
491 );
492 }
493 }
494
495 if (bHit)
496 {
497 PRINTLOG(TEXT("PerformServerLineTrace: Hit %s at distance %.2f"),
498 *OutHit.GetActor()->GetName(),
499 FVector::Dist(CameraLocation, OutHit.Location));
500 }
501
502 return bHit;
503}
504
509void UHookSystem::UpdateHookTargetUI()
510{
511 if (!OwnerPlayer)
512 return;
513
514 // MainWidget에 훅 타겟 표시 업데이트
515 if (auto MainWidget = OwnerPlayer->GetMainWidget())
516 {
517 // 에임 상태에 따라 이미지 변경
518 MainWidget->UpdateHookState(CurHookTarget != nullptr);
519 }
520
521 if ( CurHookTarget )
522 {
523 // [개선] OutlineOn은 DetectHookTarget에서 타겟 감지 시 이미 호출됨
524 // 여기서는 현재 타겟에 대해서만 켜주면 됨
525 if ( CurHookTarget_Luggage != nullptr )
526 {
527 CurHookTarget_Luggage->OnOutlineStateChanged(true);
528 }
529
530 // 디버그 표시
531 if (bShowDebugInfo)
532 {
533 FVector TargetLocation = CurHookTarget->GetActorLocation();
534 DrawDebugSphere(
535 GetWorld(),
536 TargetLocation,
537 50.0f,
538 12,
539 FColor::Yellow,
540 false,
541 0.0f,
542 0,
543 2.0f
544 );
545 }
546 }
547}
548
549
550void UHookSystem::UpdateHookLaunching(float DeltaTime)
551{
552 if (!HookedTarget || !IsValid(HookedTarget))
553 {
554 PRINTLOG(TEXT("UHookSystem: Target lost during launch"));
555 ReleaseHook();
556 return;
557 }
558
559 // 서버 권한 체크 - 상태 변경은 서버에서만
560 if (!OwnerPlayer || !OwnerPlayer->HasAuthority())
561 {
562 return;
563 }
564
565 // 훅 발사체 이동
566 HookProjectileLocation += HookLaunchDirection * LaunchSpeed * DeltaTime;
567
568 // 타겟 위치
569 FVector TargetLocation = HookedTarget->GetActorLocation();
570
571 // 타겟에 도달했는지 확인
572 float DistanceToTarget = FVector::Dist(HookProjectileLocation, TargetLocation);
573
574 if (DistanceToTarget < 50.0f)
575 {
576 // 타겟에 도달 - Pulling 상태로 전환
577 HookState = EHookState::Pulling;
578 PRINTLOG(TEXT("UHookSystem: Hook reached target, start pulling"));
579 }
580 else
581 {
582 // 최대 거리 체크
583 float DistanceFromPlayer = FVector::Dist(HookProjectileLocation, OwnerPlayer->GetActorLocation());
584 if (DistanceFromPlayer > MaxHookDistance)
585 {
586 PRINTLOG(TEXT("UHookSystem: Hook exceeded max distance"));
587 ReleaseHook();
588 }
589 }
590
591 // 디버그 표시
592 if (bShowDebugInfo)
593 {
594 DrawDebugSphere(
595 GetWorld(),
596 HookProjectileLocation,
597 20.0f,
598 8,
599 FColor::Orange,
600 false,
601 0.0f,
602 0,
603 2.0f
604 );
605 }
606}
607
608void UHookSystem::UpdateHookPulling(float DeltaTime)
609{
610 // 안전 체크: 대상이 유효하지 않으면 자동 해제
611 if (!HookedTarget || !IsValid(HookedTarget))
612 {
613 PRINTLOG(TEXT("UHookSystem: Hooked target became invalid, releasing hook"));
614 ReleaseHook();
615 return;
616 }
617
618 if (!OwnerPlayer)
619 {
620 ReleaseHook();
621 return;
622 }
623
624 // 플레이어 앞 목표 위치 계산
625 FVector PlayerLocation = OwnerPlayer->GetActorLocation();
626 FVector PlayerForward = OwnerPlayer->GetActorForwardVector();
627 FVector TargetLocation = PlayerLocation + (PlayerForward * DesiredDistance);
628
629 // 현재 대상 위치
630 FVector CurrentLocation = HookedTarget->GetActorLocation();
631
632 // 목표 위치의 높이를 현재 Luggage 높이로 유지 (바닥에 떨어지도록)
633 TargetLocation.Z = CurrentLocation.Z;
634
635 // 목표 위치까지의 거리 확인
636 float DistanceToTarget = FVector::Dist(CurrentLocation, TargetLocation);
637
638 // 서버에서만 실제 위치 업데이트 및 완료 체크
639 if (OwnerPlayer->HasAuthority())
640 {
641 // 너무 가까우면 Hook 완료
642 if (DistanceToTarget < CompleteThreshold)
643 {
644 // 완료 전 정확한 목표 위치에 배치 (튕겨나가는 것 방지)
645 HookedTarget->SetActorLocation(TargetLocation);
646 PRINTLOG(TEXT("UHookSystem: Hook completed, placed at exact target location"));
647
648 ReleaseHook();
649 return;
650 }
651
652 // 부드럽게 위치 보간 (물리는 이미 꺼진 상태)
653 FVector NewLocation = FMath::VInterpTo(
654 CurrentLocation,
655 TargetLocation,
656 DeltaTime,
657 HookSpeed
658 );
659
660 // 위치 업데이트 (서버에서만 실행됨)
661 HookedTarget->SetActorLocation(NewLocation);
662 }
663
664 // Tutorial: GrabGun 단계에서 Luggage를 훅으로 가져왔을 때 알림
665 if (OwnerPlayer)
666 {
667 APlayerController* PC = Cast<APlayerController>(OwnerPlayer->GetController());
668 if (PC)
669 {
670 if (UTutorialComponent* Tutorial = PC->FindComponentByClass<UTutorialComponent>())
671 {
672 Tutorial->OnObjectGrabbed(HookedTarget);
673 }
674 }
675 }
676
677 // 디버그 표시
678 if (bShowDebugInfo)
679 {
680 DrawDebugLine(
681 GetWorld(),
682 CurrentLocation,
683 TargetLocation,
684 FColor::Green,
685 false,
686 0.0f,
687 0,
688 2.0f
689 );
690
691 DrawDebugSphere(
692 GetWorld(),
693 TargetLocation,
694 50.0f,
695 12,
696 FColor::Green,
697 false,
698 0.0f,
699 0,
700 1.0f
701 );
702 }
703}
704
705void UHookSystem::UpdateCable()
706{
707 if (!OwnerPlayer)
708 return;
709
710 // Cable 시작 위치 (플레이어)
711 FVector CableStart = OwnerPlayer->GetActorLocation();
712
713 // Cable 끝 위치
714 FVector CableEnd;
715 if (HookState == EHookState::Launching)
716 {
717 CableEnd = HookProjectileLocation;
718 }
719 else if (HookState == EHookState::Pulling && HookedTarget)
720 {
721 CableEnd = HookedTarget->GetActorLocation();
722 }
723 else
724 {
725 if (CableMesh)
726 {
727 CableMesh->SetVisibility(false);
728 }
729 return;
730 }
731
732 // Cable Component 업데이트
733 if (CableMesh)
734 {
735 UpdateCableMeshTransform(CableStart, CableEnd);
736 }
737
738 // Projectile Mesh 위치 업데이트
739 if (ProjectileMesh)
740 {
741 ProjectileMesh->SetWorldLocation(CableEnd);
742 }
743
744 // 항상 보이는 Debug Line 추가
745 DrawDebugLine(
746 GetWorld(),
747 CableStart,
748 CableEnd,
749 FColor::Cyan,
750 false,
751 0.0f,
752 0,
753 3.0f
754 );
755
756 // // 발사체 위치 표시 (Launching 중)
757 // if (HookState == EHookState::Launching)
758 // {
759 // DrawDebugSphere(
760 // GetWorld(),
761 // HookProjectileLocation,
762 // 15.0f,
763 // 8,
764 // FColor::Red,
765 // false,
766 // 0.0f,
767 // 0,
768 // 2.0f
769 // );
770 // }
771}
772
773void UHookSystem::UpdateCableMeshTransform(const FVector& CableStart, const FVector& CableEnd)
774{
775 if (!CableMesh)
776 {
777 return;
778 }
779
780 FVector CableDelta = CableEnd - CableStart;
781 float CableLength = CableDelta.Size();
782 if (CableLength <= KINDA_SMALL_NUMBER)
783 {
784 CableMesh->SetVisibility(false);
785 return;
786 }
787
788 FVector CableMidpoint = CableStart + (CableDelta * 0.5f);
789 CableMesh->SetWorldLocation(CableMidpoint);
790
791 FVector Direction = CableDelta / CableLength;
792 FRotator CableRotation = FRotationMatrix::MakeFromZ(Direction).Rotator();
793 CableMesh->SetWorldRotation(CableRotation);
794
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;
799
800 CableMesh->SetWorldScale3D(FVector(RadiusScale, RadiusScale, LengthScale));
801 CableMesh->SetVisibility(true);
802}
803
804void UHookSystem::OnRep_HookState()
805{
806 // HookState가 변경되면 Visual 업데이트
807 switch (HookState)
808 {
809 case EHookState::Idle:
810 // Hook이 해제되면 Cable과 Projectile 숨김
811 if (CableMesh)
812 {
813 CableMesh->SetVisibility(false);
814 }
815 if (ProjectileMesh)
816 {
817 ProjectileMesh->SetVisibility(false);
818 }
819 break;
820
821 case EHookState::Launching:
822 case EHookState::Pulling:
823 // Hook이 시작되면 Cable과 Projectile 표시
824 if (CableMesh)
825 {
826 CableMesh->SetVisibility(true);
827 }
828 if (ProjectileMesh)
829 {
830 ProjectileMesh->SetVisibility(true);
831 }
832 break;
833 }
834
835 PRINTLOG(TEXT("UHookSystem: OnRep_HookState - State changed to %d"), (int32)HookState);
836}
837
838void UHookSystem::OnRep_HookProjectileLocation()
839{
840 // HookProjectileLocation이 변경되면 Projectile Mesh 위치 업데이트
841 if (ProjectileMesh && HookState == EHookState::Launching)
842 {
843 ProjectileMesh->SetWorldLocation(HookProjectileLocation);
844 }
845}
Declares the player-controlled character actor.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
Definition GameLogging.h:30
UGameSoundManager 클래스를 선언합니다.
Hook 대상 표시 컴포넌트
그래플링 훅 시스템
플레이어의 상호작용 감지 및 처리 시스템
상호작용 가능한 수하물 액터
Definition luggage.h:15
void PlayTTSAudio()
Definition luggage.cpp:472
bool bIsBeingHooked
훅 중 플래그 (복제됨)
Definition luggage.h:55
TObjectPtr< class UInteractableComponent > InteractableComp
Definition luggage.h:38
TObjectPtr< AActor > HookedBy
현재 이 Luggage를 훅하고 있는 플레이어
Definition luggage.h:62