KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
luggage.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
3
4#include "luggage.h"
6#include "ALingoGameMode.h"
7#include "ALingoPlayerState.h"
8#include "APlayerActor.h"
10#include "GameLogging.h"
11#include "UGameDataManager.h"
12#include "UHookComponent.h"
14#include "ULingoGameHelper.h"
15
16#include "Components/BoxComponent.h"
17#include "Components/WidgetComponent.h"
18#include "Net/UnrealNetwork.h"
19#include "GameFramework/PlayerController.h"
20#include "Engine/World.h"
21#include "Materials/MaterialParameterCollection.h"
22#include "Materials/MaterialParameterCollectionInstance.h"
23
24#define LUGGAGE_INTERACT_WIDGET_PATH TEXT("/Game/CustomContents/UI/Widgets/WBP_InteractWidget_Luggage.WBP_InteractWidget_Luggage_C")
25
27{
28 PrimaryActorTick.bCanEverTick = true;
29
30
31 Mesh1Comp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh1Comp"));
32 SetRootComponent(Mesh1Comp);
33
34 BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
35 BoxComp->SetupAttachment(GetRootComponent());
36 BoxComp->SetBoxExtent(FVector(55));
37
38 Mesh2Comp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh2Comp"));
39 Mesh2Comp->SetupAttachment(Mesh1Comp);
40
41 Mesh3Comp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh3Comp"));
42 Mesh3Comp->SetupAttachment(Mesh1Comp);
43
44 InteractableComp = CreateDefaultSubobject<UInteractableComponent>(TEXT("Interactable"));
45 InteractableComp->InteractionType = EInteractionType::PickUp;
46 InteractableComp->InteractionPrompt = TEXT("Pick Up");
47
48 HookComp = CreateDefaultSubobject<UHookComponent>(TEXT("Hook"));
49
50 // Initial settings (Physics 설정은 BeginPlay에서 수행)
51 Mesh1Comp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
52 Mesh1Comp->SetCollisionProfileName(TEXT("PhysicsActor"));
53
54 // 물리 복제 설정
55 Mesh1Comp->SetIsReplicated(true);
56
57 // Replication
58 bReplicates = true;
59 SetNetUpdateFrequency(100.0f); // 높은 업데이트 빈도로 부드러운 네트워크 동기화
60 SetMinNetUpdateFrequency(33.0f); // 최소 30fps 업데이트 보장
61
62 WidgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("WidgetComp"));
63 ConstructorHelpers::FClassFinder<ULuggageInfoWidget> boxWidgetRef(LUGGAGE_INTERACT_WIDGET_PATH);
64 if (boxWidgetRef.Succeeded())
65 {
66 WidgetComp->SetWidgetClass(boxWidgetRef.Class);
67 WidgetComp->SetupAttachment(GetRootComponent());
68 WidgetComp->SetWidgetSpace(EWidgetSpace::Screen);
69 WidgetComp->SetDrawSize(FVector2D(2048.0f, 1024.0f));
70 }
71
72 ConstructorHelpers::FObjectFinder<UMaterialInterface> cubeBodyMaterialRef(TEXT("/Script/Engine.MaterialInstanceConstant'/Game/CustomContents/Platfrom/Assets/portal_block/Materials/M_Cube_Body.M_Cube_Body'"));
73 if (cubeBodyMaterialRef.Succeeded())
74 {
75 cubeBodyMaterial = cubeBodyMaterialRef.Object;
76 }
77
78 ConstructorHelpers::FObjectFinder<UMaterialInterface> cubeStickerMaterialRef(TEXT("/Script/Engine.Material'/Game/CustomContents/Platfrom/Assets/portal_block/Materials/M_Cube_Sticker.M_Cube_Sticker'"));
79 if (cubeStickerMaterialRef.Succeeded())
80 {
81 cubeStickerMaterial = cubeStickerMaterialRef.Object;
82 }
83
84 ConstructorHelpers::FObjectFinder<UMaterialInterface> cubeCoverMaterialRef(TEXT("/Script/Engine.MaterialInstanceConstant'/Game/CustomContents/Platfrom/Assets/portal_block/Materials/M_Cube_Cover.M_Cube_Cover'"));
85 if (cubeCoverMaterialRef.Succeeded())
86 {
87 cubeCoverMaterial = cubeCoverMaterialRef.Object;
88 }
89}
90
92{
93 Super::BeginPlay();
94
95 cubeBodyMID = UMaterialInstanceDynamic::Create(cubeBodyMaterial, this);
96 Mesh1Comp->SetMaterial(0, cubeBodyMID);
97
98 // Physics 설정 (생성자에서 이동)
99 if (Mesh1Comp)
100 {
101 Mesh1Comp->SetSimulatePhysics(true);
102 Mesh1Comp->SetEnableGravity(true);
103 Mesh1Comp->SetMassOverrideInKg(NAME_None, 5.f, true);
104 }
105
106 SetReplicateMovement(true);
107
108 // 위젯 초기화
109 InteractableComp->InitWidget(WidgetComp);
110 // 아웃라인 상태 변경 델리게이트 바인딩
111 InteractableComp->OnOutlineStateChanged.AddDynamic(this, &Aluggage::OnOutlineStateChanged);
112}
113
114void Aluggage::Tick(float DeltaTime)
115{
116 Super::Tick(DeltaTime);
117
118 // 위젯이 아직 초기화되지 않았고, ColorIdx와 PatternIdx가 유효하다면
119 if (!bWidgetInitialized && ColorIdx >= 0 && PatternIdx >= 0)
120 {
121 if (auto InfoWidget = Cast<ULuggageInfoWidget>(WidgetComp->GetWidget()))
122 {
123 InfoWidget->InitLuggage(Pattern, Color);
124 bWidgetInitialized = true; // 한 번만 실행
125 }
126 }
127
128 // BillboardInfoWidget();
129
130 // Pattern 이름을 luggage 위에 표시
131
132 // if (!Pattern.IsEmpty())
133 // {
134 // FVector TextLocation = GetActorLocation() + FVector(0, 0, 100);
135 // DrawDebugString(GetWorld(), TextLocation, Pattern, nullptr, FColor::White, 0.f, true);
136 // }
137}
138
139void Aluggage::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
140{
141 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
142
143 DOREPLIFETIME(Aluggage, ColorIdx);
144 DOREPLIFETIME(Aluggage, PatternIdx);
145
150 DOREPLIFETIME(Aluggage, bIsBeingHooked);
151 DOREPLIFETIME(Aluggage, HookedBy);
152 DOREPLIFETIME(Aluggage, bCollisionEnabled);
153}
154
155void Aluggage::SetLuggageInfo(int32 InIdx, FString InColor, FString InPattern)
156{
157 SpawnIdx = InIdx;
158 Color = InColor;
159 Pattern = InPattern;
160}
161
166
171
178{
179 if (bIsBeingHooked)
180 {
181 // 훅 중 - Outline 켜기
183 PRINTLOG(TEXT("OnRep_IsBeingHooked: %s is being hooked by %s"),
184 *GetName(), HookedBy ? *HookedBy->GetName() : TEXT("Unknown"));
185 }
186 else
187 {
188 // 훅 해제 - Outline 상태는 InteractableComponent나 다른 시스템이 관리
189 // 여기서는 명시적으로 끄지 않음 (픽업 상태 등을 고려)
190 PRINTLOG(TEXT("OnRep_IsBeingHooked: %s hook released"), *GetName());
191 }
192}
193
198
199void Aluggage::ApplyColorToMesh(int32 InColorIdx)
200{
201 // 게임 스레드가 아니면 게임 스레드로 dispatch
202 if (!IsInGameThread())
203 {
204 AsyncTask(ENamedThreads::GameThread, [this, InColorIdx]()
205 {
206 ApplyColorToMesh(InColorIdx);
207 });
208 return;
209 }
210
211 ColorIdx = InColorIdx;
212
213 FColorData ColorData;
214 if (UGameDataManager::Get(GetWorld())->GetColorData(InColorIdx, ColorData))
215 {
216 // Color 변수 설정 (모든 클라이언트에서 데이터 테이블에서 가져옴)
217 Color = ColorData.Desc;
218
219 // if ( auto InfoWidget = Cast<ULuggageInfoWidget>(WidgetComp->GetWidget()))
220 // InfoWidget->UpdateType1Data(ColorData.Desc);
221
222 cubeCoverMID = UMaterialInstanceDynamic::Create(cubeCoverMaterial, this);
223 if (cubeCoverMID && Mesh3Comp)
224 {
225 // BaseColorFactor로 변경
226 cubeCoverMID->SetVectorParameterValue(FName("BaseColorFactor"), ColorData.GetLinearColor());
227 Mesh3Comp->SetMaterial(0, cubeCoverMID);
228 }
229 }
230}
231
232void Aluggage::ApplyPatternToMesh(int32 InPatternIdx)
233{
234 // 게임 스레드가 아니면 게임 스레드로 dispatch
235 if (!IsInGameThread())
236 {
237 AsyncTask(ENamedThreads::GameThread, [this, InPatternIdx]()
238 {
239 ApplyPatternToMesh(InPatternIdx);
240 });
241 return;
242 }
243
244 PatternIdx = InPatternIdx;
245
246 // 데칼 바꾸기
247
248 FReadData ReadData;
249 UGameDataManager::Get(GetWorld())->GetReadData(InPatternIdx, ReadData);
250
251 // Pattern 변수 설정 (모든 클라이언트에서 데이터 테이블에서 가져옴)
252 Pattern = ReadData.Word;
253
254 // if ( auto InfoWidget = Cast<ULuggageInfoWidget>(WidgetComp->GetWidget()))
255 // InfoWidget->UpdateType2Data(ReadData.Word);
256
257 UTexture2D* LoadedTexture = nullptr;
258 if (ReadData.Texture.IsValid())
259 LoadedTexture = ReadData.Texture.Get();
260 else
261 LoadedTexture = ReadData.Texture.LoadSynchronous();
262
263 cubeStickerMID = UMaterialInstanceDynamic::Create(cubeStickerMaterial, this);
264 if (cubeStickerMID)
265 {
266 // BaseColorFactor로 변경
267 cubeStickerMID->SetTextureParameterValue(FName("TextureParam"), LoadedTexture);
268 Mesh2Comp->SetMaterial(0, cubeStickerMID);
269 }
270}
271
272
274{
275 // Tick에서 호출되는 지연 초기화 로직으로 대체됨
276 // 필요시 수동으로 위젯을 업데이트할 때만 사용
277 if (auto InfoWidget = Cast<ULuggageInfoWidget>(WidgetComp->GetWidget()))
278 {
279 InfoWidget->InitLuggage(Pattern, Color);
280 }
281}
282
283void Aluggage::OnOutlineStateChanged(bool bShouldShowOutline)
284{
285 Mesh1Comp->SetRenderCustomDepth(bShouldShowOutline);
286 Mesh2Comp->SetRenderCustomDepth(bShouldShowOutline);
287 Mesh3Comp->SetRenderCustomDepth(bShouldShowOutline);
288}
289
291{
292 // visible value 높이기
294
295 cubeBodyMID->SetScalarParameterValue(FName("VisibleValue"), dissolveVal);
296 cubeStickerMID->SetScalarParameterValue(FName("VisibleValue"), dissolveVal);
297 cubeCoverMID->SetScalarParameterValue(FName("VisibleValue"), dissolveVal);
298
299 PRINTLOG(TEXT("UpdateDissolve: dissolveVal: %f"), dissolveVal);
300
301 return dissolveVal >= dissolveMaxVal;
302}
303
305{
306 if (bEnable)
307 {
308 // 충돌 활성화
309 if (BoxComp)
310 {
311 BoxComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
312 }
313
314 if (Mesh1Comp)
315 {
316 Mesh1Comp->SetSimulatePhysics(true);
317 Mesh1Comp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
318 }
319
320 if (Mesh2Comp)
321 {
322 Mesh2Comp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
323 }
324
325 if (Mesh3Comp)
326 {
327 Mesh3Comp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
328 }
329
330 // InteractableComp의 DetectionRange 활성화
331 if (InteractableComp && InteractableComp->DetectionRange)
332 {
333 InteractableComp->DetectionRange->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
334 InteractableComp->DetectionRange->SetGenerateOverlapEvents(true);
335 }
336 }
337 else
338 {
339 // 충돌 비활성화
340 if (BoxComp)
341 {
342 BoxComp->SetSimulatePhysics(false);
343 BoxComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
344 }
345
346 if (Mesh1Comp)
347 {
348 Mesh1Comp->SetSimulatePhysics(false);
349 Mesh1Comp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
350 }
351
352 if (Mesh2Comp)
353 {
354 Mesh2Comp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
355 }
356
357 if (Mesh3Comp)
358 {
359 Mesh3Comp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
360 }
361
362 // InteractableComp의 DetectionRange 비활성화 (위젯 노출 방지)
363 if (InteractableComp && InteractableComp->DetectionRange)
364 {
365 InteractableComp->DetectionRange->SetCollisionEnabled(ECollisionEnabled::NoCollision);
366 InteractableComp->DetectionRange->SetGenerateOverlapEvents(false);
367 }
368
369 PRINTLOG(TEXT("Aluggage::SetAllCollision - Collisions disabled for %s"), *GetName());
370 }
371}
372
374{
375 ApplyCollisionState(bEnable);
376
377 if (HasAuthority())
378 {
379 bCollisionEnabled = bEnable;
380 }
381}
382
383// void Aluggage::InfoWidgetOn()
384// {
385// WidgetComp->GetWidget()->SetVisibility(ESlateVisibility::Visible);
386// }
387//
388// void Aluggage::InfoWidgetOff()
389// {
390// WidgetComp->GetWidget()->SetVisibility(ESlateVisibility::Hidden);
391// }
392//
393// void Aluggage::BillboardInfoWidget()
394// {
395// // 카메라 가져오기
396// AActor* cam = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0);
397//
398// if ( cam == nullptr)
399// return;
400//
401// // 카메라 backward 벡터, up 벡터를 이용하여 Rotator 계산
402// FRotator rot = UKismetMathLibrary::MakeRotFromXZ(-cam->GetActorForwardVector(), cam->GetActorUpVector());
403// rot.Pitch = 0;
404// // 회전값으로 설정
405// WidgetComp->SetWorldRotation(rot);
406// }
407
408
409//--------------------------------------------------------------//
410// Read Quest Interaction
411//--------------------------------------------------------------//
412
413void Aluggage::OnInteract(AActor* Interactor)
414{
415 if (!Interactor)
416 {
417 PRINTLOG(TEXT("[Luggage] OnInteract - Interactor is null"));
418 return;
419 }
420
421 // 플레이어 컨트롤러와 PlayerState 가져오기
422 APlayerController* PC = Cast<APlayerController>(Interactor->GetInstigatorController());
423 if (!PC)
424 {
425 PRINTLOG(TEXT("[Luggage] OnInteract - PlayerController is null"));
426 return;
427 }
428
429 APlayerState* PS = PC->GetPlayerState<APlayerState>();
430 if (!PS)
431 {
432 PRINTLOG(TEXT("[Luggage] OnInteract - PlayerState is null"));
433 return;
434 }
435
436 //PRINTLOG(TEXT("[Luggage] OnInteract - Player selected luggage with Target1: %s, Target2: %s"), *Target1, *Target2);
437
438 // 서버에 선택 알림
440}
441
442void Aluggage::ServerNotifySelection_Implementation(APlayerState* Player)
443{
444 if (!Player)
445 {
446 PRINTLOG(TEXT("[Luggage] ServerNotifySelection - Player is null"));
447 return;
448 }
449
450 PRINTLOG(TEXT("[Luggage] ServerNotifySelection - Processing selection for player"));
451
452 // GameMode에 캐리어 선택 알림
453 ALingoGameMode* GameMode = GetWorld()->GetAuthGameMode<ALingoGameMode>();
454 if (GameMode)
455 {
456 GameMode->HandleLuggageSelection(Player, this);
457 }
458 else
459 {
460 PRINTLOG(TEXT("[Luggage] ServerNotifySelection - GameMode is null or not ALingoGameMode"));
461 }
462}
463
464bool Aluggage::ServerNotifySelection_Validate(APlayerState* Player)
465{
466 // 기본적인 검증: Player가 유효한지 확인
467 return Player != nullptr;
468}
469
470
471
473{
474 RequestListenAudio( FString::Printf(TEXT("%s %s"), *Color, *Pattern) );
475}
476
477void Aluggage::RequestListenAudio(const FString& AudioText)
478{
479 if (bIsRequest)
480 return;
481
482 if (auto KLingoNetwork = UKLingoNetworkSystem::Get(GetWorld()))
483 {
484 bIsRequest = true;
485
486 KLingoNetwork->RequestListenAudio(
487 AudioText,
488 FResponseListenAudioDelegate::CreateUObject(this, &Aluggage::OnResponseListenAudio)
489 );
490 }
491}
492
493void Aluggage::OnResponseListenAudio(FResponseListenAudio& ResponseData, bool bWasSuccessful)
494{
495 bIsRequest = false;
496
497 if (bWasSuccessful)
498 {
499 if (auto PlayerActor = ULingoGameHelper::GetPlayerActor(this))
500 PlayerActor->PlayTTSAudio(ResponseData.audio_base64);
501 }
502}
Declares the player-controlled character actor.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
Definition GameLogging.h:30
UGameDataManager 클래스를 선언합니다.
Hook 대상 표시 컴포넌트
KLingo API 요청을 담당하는 서브시스템을 선언합니다.
void HandleLuggageSelection(class APlayerState *Player, class Aluggage *luggage)
상호작용 가능한 수하물 액터
Definition luggage.h:15
virtual void GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > &OutLifetimeProps) const override
Definition luggage.cpp:139
TObjectPtr< class UMaterialInstanceDynamic > cubeCoverMID
Definition luggage.h:179
void UpdateWidget()
Definition luggage.cpp:273
TObjectPtr< class UStaticMeshComponent > Mesh1Comp
Definition luggage.h:31
TObjectPtr< class UMaterialInstanceDynamic > cubeStickerMID
Definition luggage.h:176
void PlayTTSAudio()
Definition luggage.cpp:472
TObjectPtr< class UStaticMeshComponent > Mesh2Comp
Definition luggage.h:33
virtual void BeginPlay() override
Definition luggage.cpp:91
bool bIsRequest
Definition luggage.h:189
bool bIsBeingHooked
훅 중 플래그 (복제됨)
Definition luggage.h:55
TObjectPtr< class UWidgetComponent > WidgetComp
Definition luggage.h:40
FString Color
Definition luggage.h:88
TObjectPtr< class UMaterialInterface > cubeStickerMaterial
Definition luggage.h:167
const float dissolveMaxVal
Definition luggage.h:181
TObjectPtr< class UHookComponent > HookComp
Definition luggage.h:43
void ApplyPatternToMesh(int32 InPatternIdx)
Definition luggage.cpp:232
void ApplyCollisionState(bool bEnable)
Definition luggage.cpp:304
TObjectPtr< class UInteractableComponent > InteractableComp
Definition luggage.h:38
int32 PatternIdx
Definition luggage.h:106
void OnRep_PatternIdx()
Definition luggage.cpp:167
TObjectPtr< class UMaterialInstanceDynamic > cubeBodyMID
Definition luggage.h:173
bool UpdateDissolve()
Definition luggage.cpp:290
int32 ColorIdx
Definition luggage.h:103
TObjectPtr< class UStaticMeshComponent > Mesh3Comp
Definition luggage.h:35
int32 SpawnIdx
Definition luggage.h:86
FString Pattern
Definition luggage.h:90
void OnOutlineStateChanged(bool bShouldShowOutline)
InteractableComponent의 아웃라인 상태 변경 이벤트 핸들러
Definition luggage.cpp:283
bool bWidgetInitialized
Definition luggage.h:128
virtual void Tick(float DeltaTime) override
Definition luggage.cpp:114
TObjectPtr< class UBoxComponent > BoxComp
Definition luggage.h:28
void ApplyColorToMesh(int32 InColorIdx)
Definition luggage.cpp:199
TObjectPtr< class UMaterialInterface > cubeBodyMaterial
Definition luggage.h:164
void OnRep_CollisionEnabled()
Definition luggage.cpp:194
void OnRep_ColorIdx()
Definition luggage.cpp:162
void RequestListenAudio(const FString &AudioText)
Definition luggage.cpp:477
void OnRep_IsBeingHooked()
bIsBeingHooked 복제 시 호출되는 콜백
Definition luggage.cpp:177
void OnResponseListenAudio(FResponseListenAudio &ResponseData, bool bWasSuccessful)
Definition luggage.cpp:493
float dissolveSpeed
Definition luggage.h:183
TObjectPtr< AActor > HookedBy
현재 이 Luggage를 훅하고 있는 플레이어
Definition luggage.h:62
void SetLuggageInfo(int32 InIdx, FString InColor, FString InPattern)
Definition luggage.cpp:155
float dissolveVal
Definition luggage.h:182
bool bCollisionEnabled
Definition luggage.h:74
void SetAllCollision(bool bEnable)
모든 컴포넌트의 충돌과 물리를 설정합니다.
Definition luggage.cpp:373
void OnInteract(AActor *Interactor)
플레이어가 캐리어를 선택했을 때 호출됩니다.
Definition luggage.cpp:413
void ServerNotifySelection(class APlayerState *Player)
서버에 캐리어 선택을 알립니다.
TObjectPtr< class UMaterialInterface > cubeCoverMaterial
Definition luggage.h:170
static class APlayerActor * GetPlayerActor(const UObject *WorldContextObject)
첫 번째 플레이어의 PlayerActor를 가져옵니다.
#define LUGGAGE_INTERACT_WIDGET_PATH
Definition luggage.cpp:24
색상 데이터를 정의하는 구조체
Definition FColorData.h:19
FString Desc
색상 설명 (한글명)
Definition FColorData.h:32
FLinearColor GetLinearColor() const
HexColor 문자열을 FLinearColor로 변환하여 반환
Definition FColorData.h:43
읽기 학습 데이터를 정의하는 구조체
Definition FReadData.h:19
FString Word
학습 단어
Definition FReadData.h:32
TSoftObjectPtr< UTexture2D > Texture
Definition FReadData.h:35
TArray< uint8 > audio_base64