16#include "GameFramework/PlayerState.h"
17#include "Net/UnrealNetwork.h"
19#include "Components/BoxComponent.h"
20#include "Components/StaticMeshComponent.h"
21#include "Components/WidgetComponent.h"
23#include "Kismet/GameplayStatics.h"
24#include "Kismet/KismetMathLibrary.h"
26#define WHEATLY_MESH_PATH TEXT("/Game/CustomContents/Platfrom/Assets/Wheatly_Talk/Wheatly_Talk")
27#define WHEATLY_MATERIAL_0 TEXT("/Script/Engine.Material'/Game/CustomContents/Platfrom/Assets/Wheatly_Talk/M_Wheatly_01.M_Wheatly_01'")
28#define WHEATLY_MATERIAL_1 TEXT("/Script/Engine.Material'/Game/CustomContents/Platfrom/Assets/Wheatly_Talk/M_Wheatly_02.M_Wheatly_02'")
29#define WHEATLY_MATERIAL_2 TEXT("/Script/Engine.Material'/Game/CustomContents/Platfrom/Assets/Wheatly_Talk/M_Wheatly_03.M_Wheatly_03'")
33 PrimaryActorTick.bCanEverTick =
true;
36 SetReplicateMovement(
true);
41 RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT(
"Root"));
43 MeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT(
"MeshComp"));
45 MeshComponent->SetCollisionProfileName(TEXT(
"BlockAllDynamic"));
47 EyeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT(
"EyeMesh"));
55 EyeSightComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT(
"EyeSightComp"));
63 static ConstructorHelpers::FObjectFinder<UStaticMesh> CylinderMesh(TEXT(
"/Engine/BasicShapes/Cylinder.Cylinder"));
64 if (CylinderMesh.Succeeded())
66 UStaticMesh* Mesh = CylinderMesh.Object;
72 IndicatorBaseRadius = FMath::Max(Mesh->GetBounds().BoxExtent.X, Mesh->GetBounds().BoxExtent.Y);
76 EyeSightComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
83 InteractableComp = CreateDefaultSubobject<UInteractableComponent>(TEXT(
"Interactable"));
88 WidgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT(
"WidgetComp"));
91 WidgetComp->SetupAttachment(GetRootComponent());
92 WidgetComp->SetWidgetSpace(EWidgetSpace::Screen);
93 WidgetComp->SetDrawSize(FVector2D(2048.0f, 1024.0f));
103 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
116 UMaterialInterface* BaseMaterial =
MeshComponent->GetMaterial(2);
118 EyeMaterial = UMaterialInstanceDynamic::Create(BaseMaterial,
this);
124 UMaterialInterface* BaseMaterial =
EyeSightComp->GetMaterial(0);
143 UGameplayStatics::GetActorOfClass(GetWorld(), ASpeakStageActor::StaticClass())
149 PRINTLOG(TEXT(
"[AWheatly] SpeakStage auto-connected"));
153 PRINTLOG(TEXT(
"[AWheatly] Warning: No SpeakStageActor found in world"));
162 Super::Tick(DeltaSeconds);
172 if (
auto SpeakerPawn = CurrentSpeaker->GetPawn())
180 FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(GetActorLocation(), SpeakerPawn->GetActorLocation());
181 FRotator TargetRotation( LookAtRotation.Pitch, LookAtRotation.Yaw, 0);
182 SetActorRotation(FMath::RInterpTo(GetActorRotation(), TargetRotation, DeltaSeconds, 5.0f));
196 TArray<AActor*> OverlappingActors;
200 APawn* NearestPawn =
nullptr;
201 double MinDistanceSquared = MAX_dbl;
203 for (AActor* OverlappingActor : OverlappingActors)
205 if (APawn* Pawn = Cast<APawn>(OverlappingActor))
207 double DistanceSquared = FVector::DistSquared(GetActorLocation(), Pawn->GetActorLocation());
208 if (DistanceSquared < MinDistanceSquared)
210 MinDistanceSquared = DistanceSquared;
219 FHitResult HitResult;
220 FVector StartLocation =
EyeMesh->GetComponentLocation();
221 FVector EndLocation = NearestPawn->GetActorLocation();
222 FCollisionQueryParams CollisionParams;
223 CollisionParams.AddIgnoredActor(
this);
225 bool bHit = GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_Visibility, CollisionParams);
228 if (!bHit || (bHit && HitResult.GetActor() == NearestPawn))
231 FRotator LookAtRotation = UKismetMathLibrary::FindLookAtRotation(
EyeMesh->GetComponentLocation(), NearestPawn->GetActorLocation());
232 FRotator TargetRotation( LookAtRotation.Pitch, LookAtRotation.Yaw, 0);
233 SetActorRotation(FMath::RInterpTo(GetActorRotation(), TargetRotation, DeltaSeconds, 2.0f));
253void AWheatly::Multicast_PlayAnimation_Implementation(
EWheatlyAnim InAnimType)
289 if (!HasAuthority() || !Player)
298 BroadcastMgr->SendTutorMessage(FText::FromString(
"SPEAK COMPLETE"));
309 PRINTLOG(TEXT(
"[AWheatly] SyncSpeakScenarioData: Player is null"));
316 PS->SpeakScenarioData = Data;
317 PS->OnUpdateSpeakScenarioData();
319 PRINTLOG(TEXT(
"[AWheatly] SyncSpeakScenarioData: Successfully synced scenario data for %s"),
324 PRINTLOG(TEXT(
"[AWheatly] SyncSpeakScenarioData: Failed to get PlayerState"));
341 APlayerActor* InteractingPlayer = Cast<APlayerActor>(InteractingActor);
342 if (!InteractingPlayer)
345 auto PC = Cast<APlayerControl>(InteractingPlayer->GetController());
350 if (PS->IsSpeakQuestCompleted())
353 PC->Client_ToastMessage(TEXT(
"Already Clear SpeakQuest"));
369 PC->Client_RequestSpeakScenario(
this);
374 GS->SetAllCompassVisibility(
false);
375 GS->SetCompassVisibilityByTag(
"WriteQuest",
true);
393 ? FLinearColor(1.0f, 1.0f, 0.0f, 1.0f)
394 : FLinearColor(0.0f, 0.5f, 1.0f, 1.0f);
409 if (LocalPlayerState && NewSpeaker && LocalPlayerState == NewSpeaker)
413 PRINTLOG(TEXT(
"[AWheatly] Local player is speaker - hiding widget"));
423 PRINTLOG(TEXT(
"[AWheatly] Local player not speaker and in range - showing widget"));
432 TArray<AActor*> OverlappingActors;
433 InteractableComp->DetectionRange->GetOverlappingActors(OverlappingActors, ACharacter::StaticClass());
435 for (AActor* Actor : OverlappingActors)
437 if (Actor == LocalPawn)
461 const FVector StartLocation =
EyeMesh->GetComponentLocation();
479 EyeMaterial->SetVectorParameterValue(TEXT(
"EyeColor"), newColor);
485 FVector Delta = End - Start;
486 float Length = Delta.Size();
487 if (Length <= KINDA_SMALL_NUMBER)
493 FVector Midpoint = Start + (Delta * 0.5f);
496 FVector Direction = Delta / Length;
497 FRotator Rotation = FRotationMatrix::MakeFromZ(Direction).Rotator();
501 const float TargetThickness = 10.f;
503 const float RadiusScale = (TargetThickness / MeshDiameter)*2.5f;
505 EyeSightComp->SetWorldScale3D(FVector(RadiusScale, RadiusScale, LengthScale));
#define INTERACT_WIDGET_PATH
Declares the player-controlled character actor.
APlayerControl 선언에 대한 Doxygen 주석을 제공합니다.
#define WHEATLY_MESH_PATH
#define WHEATLY_MATERIAL_1
#define WHEATLY_MATERIAL_0
#define WHEATLY_MATERIAL_2
EWheatlyAnim
Wheatly 애니메이션 타입
FComponentHelper 구조체를 선언합니다.
YiSan 전반에서 사용하는 공용 인터페이스를 선언합니다.
#define PRINTLOG(fmt,...)
KLingo API 요청을 담당하는 서브시스템을 선언합니다.
Main character driven directly by the player.
FOnSpeakerChangedDelegate OnSpeakerChanged
현재 발화자가 변경될 때 호출되는 이벤트입니다.
bool IsInRange(const class APawn *LocalPawn) const
TObjectPtr< class UWidgetComponent > WidgetComp
위젯 컴포넌트 (상호작용 UI 표시)
TMap< EWheatlyAnim, TObjectPtr< class UAnimSequence > > AnimSequences
애니메이션 시퀀스 맵
float CurAnimDuration
현재 재생 중인 애니메이션 길이 (초)
TObjectPtr< class UInteractableComponent > InteractableComp
상호작용 컴포넌트
void OnOutlineStateChanged(bool bShouldShowOutline)
Outline 상태 변경 핸들러
virtual void Tick(float DeltaSeconds) override
void UpdateEyeSight(const FVector &Start, const FVector &End)
void Multicast_PlayAnimation(EWheatlyAnim InAnimType)
애니메이션 재생 (멀티캐스트 RPC)
float IndicatorBaseRadius
void PlayAnimation(EWheatlyAnim InAnimType)
특정 애니메이션 재생 (서버에서 호출)
void CompleteSpeakQuest(class APlayerActor *Player)
SpeakQuest 완료 처리 (서버에서만 호출)
TObjectPtr< class USkeletalMeshComponent > MeshComponent
스켈레탈 메시 컴포넌트
void ChangeEyeColor(FLinearColor newColor)
눈 색상 변경
void OnInteractionTriggered(class AActor *InteractingActor)
플레이어 상호작용 핸들러
void SetSpeakStageActor(class ASpeakStageActor *InSpeakStageActor)
SpeakStage 설정 (GameMode에서 호출)
TObjectPtr< class UMaterialInstanceDynamic > EyeMaterial
동적 머티리얼 인스턴스 (런타임 색상 변경)
void SyncSpeakScenarioData(class APlayerActor *Player, const struct FResponseSpeakScenario &Data)
Client에서 받은 시나리오 데이터를 동기화 (Server에서 호출됨)
FLinearColor ReplicatedEyeColor
TObjectPtr< class UMaterialInstanceDynamic > EyeTraceMaterial
TObjectPtr< class UStaticMeshComponent > EyeSightComp
상호작용 중인 플레이어 표시기
TObjectPtr< class UStaticMeshComponent > EyeMesh
상호작용 중인 플레이어 표시기
virtual void SetCompassMarkerInto(ECompassMarkerType InMarkerType) override
FORCEINLINE void SetAnimationType(EWheatlyAnim InAnimType)
애니메이션 타입 설정
TObjectPtr< class UBoxComponent > PlayerDetectionZone
플레이어 감지 영역
void OnSpeakStageSpeakerChanged(class APlayerState *NewSpeaker)
SpeakStage의 발화자 변경 이벤트 핸들러
void OnRep_EyeSightState()
virtual void BeginPlay() override
virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty > &OutLifetimeProps) const override
float IndicatorBaseLength
TObjectPtr< class ASpeakStageActor > SpeakStageActor
FVector ReplicatedEyeSightEnd
EWheatlyAnim AnimType
현재 애니메이션 타입
ECompassMarkerType MarkerType
게임 내 전역 이벤트를 중계하는 중앙 이벤트 버스(Event Bus) 서브시스템입니다.
static FString GetPlayerNameFromState(const class ALingoPlayerState *PlayerState)
PlayerState에서 PlayerControl을 통해 사용자 이름 가져오기
static APawn * GetLocalPawn(const UObject *WorldContextObject)
static T * LoadAsset(const TCHAR *Path)