๋ง์์ ๋ฐฐ์น๋ ๋ฐฐ๊ฒฝ NPC๊ฐ ์คํฐ ์ง์ ์ฃผ๋ณ์ ์์ฐ์ค๋ฝ๊ฒ ๋์๋ค๋๊ฒ ํ๋ค. ์ฐธ๊ณ ์๋ฃ(D3kryption์ UE5 State Tree ์์ฐฐ ๊ฐ์ด๋)์ "Move To Random Location ๋ฐ๋ณต" ๊ฐ๋ ์ ์ด ํ๋ก์ ํธ์ StateTree ์ํคํ ์ฒ์ ๋ง๊ฒ ์ ์ฉํ๋ค.
ARetrieveNPCCharacter(์์ NPC ๋ฑ ๋น์ ํฌ NPC ๊ณต์ฉ ๋ฒ ์ด์ค)๋ AIControllerClass = nullptr๋ก ์ค์ ๋ผ ์์ด ์ปจํธ๋กค๋ฌ ์์ฒด๊ฐ ์๋ ์์ ์ ์ ์ํ์๋ค. Enemy/Lumen ์ชฝ์๋ ์ด๋ฏธ StateTree AI(UStateTreeAIComponent + ์ปค์คํ
Task/Evaluator)๊ฐ ์์์ง๋ง ์ ํฌ ํ์ (๊ณต๊ฒฉ ์ฌ๊ฑฐ๋ฆฌ, ์ธ์นด์ดํฐ ์ฌ๋กฏ, ์กฐ์ฐ ๋ก์ง)์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋ผ ์์ด ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ ์ ์์๋ค. ์ฒ์์ ์ด ์์
์ Enemy ์ชฝ ํด๋์ ์๋ชป ์์ํ๋ค๊ฐ("๋ชฌ์คํฐ ์์ฐฐ"๋ก ์คํด), ์ฌ์ฉ์๊ฐ "NPC์ Enemy๋ ๋ณ๊ฐ"๋ผ๊ณ ์ ์ ํด ๋ฐฉํฅ์ ๋ฐ๋ก์ก์๋ค โ ์ญํ ์ด ๋ค๋ฅธ ์กํฐ๋ ์ฒ์๋ถํฐ ๋ค๋ฅธ ํด๋์ค ๊ณ์ธต์์ ์์ํด์ผ ํ๋ค๋ ๊ฑธ ๋ค์ ํ์ธํ ์๊ฐ์ด์๋ค.
// ARetrieveVillagerCharacter : ARetrieveNPCCharacter
ARetrieveVillagerCharacter::ARetrieveVillagerCharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
AIControllerClass = ANPCPatrolAIController::StaticClass();
if (UCharacterMovementComponent* MovementComp = GetCharacterMovement())
{
MovementComp->bOrientRotationToMovement = true;
MovementComp->RotationRate = FRotator(0.f, 180.f, 0.f);
MovementComp->MaxWalkSpeed = 250.f; // ์ฌ์ ๋กญ๊ฒ ๊ฑท๋ ์๋
MovementComp->MaxAcceleration = 400.f; // ์๋งํ ๊ฐ๊ฐ์
MovementComp->BrakingDecelerationWalking = 400.f;
}
}
ANPCPatrolAIController๋ PerceptionยทTeam ์์ด UStateTreeAIComponent ํ๋๋ง ๊ตฌ๋ํ๋ ์ต์ ์ปจํธ๋กค๋ฌ๋ค. FStateTreeTask_VillagerPatrol์ Evaluator ์์ด Task ํ๋๋ง์ผ๋ก ๋ฌดํ ๋ฃจํ๋ฅผ ๋๋ ๊ฐ์ฅ ๋จ์ํ ๊ตฌ์กฐ๋ก ์งฐ๋ค โ EnterState์์ ์คํฐ ์์น๋ฅผ ํ ๋ฒ๋ง ์บก์ฒํด ์บ์ฑํ๊ณ , PatrolOrigin ๋ฐ๊ฒฝ ๋ด GetRandomReachablePointInRadius๋ก ์ง์ ์ ๋ฝ์ ์ด๋, ๋์ฐฉ(๋๋ MaxMoveTime ์ด๊ณผ๋ก ๋งํ ํ์ )ํ๋ฉด ๋๋ค ์๊ฐ๋งํผ ๋๊ธฐ ํ ์ฌ์ ์ ํ๋ค. State๊ฐ ํ๋๋ฟ์ด๋ผ ์ ์ด(Transition) ์ ์๋ ํ์ ์๋ค.
์ฌ๊ธฐ๊น์ง ์ค๋ ๋์ ์ค์ ๋ก ๊ฒช์ ๋ฒ๊ทธ ์ธ ๊ฐ์ง๊ฐ ๋ฐฐ์ธ ์ ์ด ๋ง์๋ค.
1) ์์ผ๋ก ๋ฏธ๋๋ฌ์ง๋ฏ ๊ฑท๋ ๋ฒ๊ทธ. CharacterMovement->bOrientRotationToMovement = true(์บ๋ฆญํฐ ์ค์ค๋ก ์ด๋ ๋ฐฉํฅ์ผ๋ก ํ์ )๋ก ์ค์ ํด๋๊ณ MoveToLocation์ bCanStrafe๋ false๋ก ๋๊ฒผ๋ค. bCanStrafe=false๋ "AIController๊ฐ ํ์ ์ ๋์ ํต์ ํ๊ฒ ๋ค"๋ ์ ํธ๋ผ, ๋ ํ์ ์ฃผ์ฒด๊ฐ ์ถฉ๋ํด ๋ฏธ๋๋ฌ์ง๋ ์ด๋์ด ๋์๋ค. bCanStrafe=true๋ก ๋ง์ถฐ ํ์ ๊ถํ์ CharacterMovement ํ๋๋ก ํต์ผํ๋ ํด๊ฒฐ๋๋ค.
2) ๊ทธ๋๋ ๋จ์์๋ ์/๋ค ๊ฑธ์ ๋ฒ๊ทธ โ ๋ฉ์ฌ ํ์ ๋ณด์ ๋๋ฝ. bCanStrafe ์์ ํ์๋ ์ฆ์์ด ๋จ์ ์์๋ค. ์ ์ง ์ํ์์ ์ ๋ ๋๋ฌ๋์ง ์๋ ๋ฒ๊ทธ๋ผ, Monolith๋ก ๊ธฐ์กด์ ์ ๋์ํ๋ BP_ShopNPC_General์ Mesh ์ปดํฌ๋ํธ RelativeRotation์ ๋น๊ตํด๋ณด๋ Yaw โ 90ยฐ๋ก ๋ณด์ ๋ผ ์์๋ค. ์๋ก ๋ง๋ Villager BP๋ ์ด ๋ณด์ ๊ฐ์ด Yaw=0์ผ๋ก ๋น์ด ์์๋ ๊ฒ โ ์ด ์บ๋ฆญํฐ ๋ฉ์ฌ(์ค์ผ๋ ํค)๊ฐ ์๋ ์๊ตฌํ๋ ๋ณด์ ๊ฐ์ ๊ทธ๋๋ก ์ ๊ฐ์ ธ์จ ๊ฒ ์ง์ง ์์ธ์ด์๋ค. ๊ฐ์ ๊ฐ์ผ๋ก ๋ง์ถ์ ๋ฐ๋ก ํด๊ฒฐ.
# Monolith Python: Rotator ์์ฑ์ ์ธ์ ์์ ํจ์
# unreal.Rotator(0, 89.999999, 0) โ ์ค์ ๋ก๋ Pitch=90, Yaw=0์ผ๋ก ๋ค์ด๊ฐ!
mesh.set_editor_property('relative_rotation', unreal.Rotator(roll=0.0, pitch=0.0, yaw=89.999999))
3) ์ ์ง ์ํ์์ ๋ฌธ์ ์๋ค๊ฐ ์์ง์ด๋ฉด์๋ง ๋๋ฌ๋๋ ๋ฒ๊ทธ๋ "์ ์ง ์ํ" ์์ฒด๊ฐ ์์ธ ์ํ ์กฐ๊ฑด์ด๋ค. ์์ NPC๋ ์ ๋ ์์ง์ด์ง ์์ผ๋ฏ๋ก ๋ฉ์ฌ ํ์ ๋ณด์ ์ด ํ๋ ค๋ ํฐ๊ฐ ์ ๋ฌ๋ค. ์๋ก์ด ๋์(์ด๋)์ ์น์ ๋๋ ๊ธฐ์กด์ ๊ทธ ๋์์ ํ ๋ฒ๋ ํด๋ณด์ง ์์ ์ ์ ์ ์จ์ ์ ์ (์ค์ผ๋ ํค ๋ฐ์ธ๋ ๋ฐฉํฅ, ํ์ ๋ณด์ ๊ฐ)๋ฅผ ์์ฌํด์ผ ํ๋ค.
์ญํ ์ด ๋ค๋ฅธ ์กํฐ๋ ์ฝ๋ ์์ ์ง์ ๋ถํฐ ๋ถ๋ฆฌํ๋ค. "์์ฐฐ"์ด๋ผ๋ ์์ฒญ๋ง ๋ณด๊ณ ๊ธฐ์กด Enemy StateTree ์ธํ๋ผ์ ์์ ๋๊ธฐ ์์ํ๋ค๊ฐ, ์ค์ ๋ก๋ ์์ ํ ์ ํด๋(
Source/Retrieve/NPC/)์ ๋ ๋ฆฝ๋ Controller/Task๋ฅผ ๋ง๋๋ ๊ฒ ๋ง๋ ๋ฐฉํฅ์ด์๋ค. ์ ํฌ AI์ ๋ฐฐ๊ฒฝ AI๋ ์๊ตฌ์ฌํญ์ด ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฅด๋ฏ๋ก(ํ๊นํ ์ฌ๋ถ, ์ธ์นด์ดํฐ ์ฌ๋กฏ ์ฌ๋ถ) ์ฒ์๋ถํฐ ๊ฐ๋ผ๋๋ฉด ์ดํ ๋ก์ง์ด ํจ์ฌ ๋จ์ํด์ง๋ค.
"์ ์ง ์ํ์์๋ง ์จ์จ ์ ์ "์ ์์ง์ด๋ ์ฉ๋๋ก ์ฌ์ฌ์ฉํ ๋๋ ๊ทธ ์ ์ ์ด ์ด๋์ ํ ๋ฒ๋ ๊ฒ์ฆ๋ฐ์ ์ ์๋ค๋ ๊ฑธ ์ ์ ํด์ผ ํ๋ค. ๋ฉ์ฌ ํ์ ๋ณด์ ์ฒ๋ผ ์๊ฐ์ ์ผ๋ก๋ง ๋๋ฌ๋๋ ๋ฒ๊ทธ๋ ์ฝ๋ ๋ฆฌ๋ทฐ๋ก๋ ๋ชป ์ก๊ณ , ๋ฐ๋์ ์ค์ ๋ก ์์ง์ฌ๋ด์ผ ๋ฐ๊ฒฌ๋๋ค.
์์ NPC์ ์ด๋ฏธ ์๋ ๋ํ ์์คํ ์ ๋ง์ NPC์๋ ์ ์ฉํ๋, NPC๋ง๋ค ๋ค๋ฅธ ๋์ฌยท๋ฐ์ ์ ๋๋ฉ์ด์ ยท๋ณด์์ ๊ฐ์ง ์ ์๊ฒ ํ๊ณ , ์ผ๋ถ NPC๋ ์ฌ๋ฌ ํด์ ๋ํ(๋คํ์ฐจ ๋ํ)์ ๋ํ ์ข ๋ฃ ์ ๋๋ค ์์ดํ ์ง๊ธ๊น์ง ์ง์ํ๋ค.
์กฐ์ฌํด๋ณด๋ DT_Dialogue(SpeakerTagยทTopicIdยทLinesยทFollowUpRows ๊ธฐ๋ฐ ํธ๋ฆฌ ๊ตฌ์กฐ)์ URetrieveDialogueComponent(NPC๋ณ EditAnywhere๋ก ๋์ฌยท์ ๋๋ฉ์ด์
์ง์ )๋ ์ด๋ฏธ ์์ฑ๋ผ ์์ด์ ๋คํ์ฐจ ๋ํ๋ NPC๋ณ ์ฐจ๋ณํ๋ ์ฌ์ค ๋ฐ์ดํฐ ์์
๋ง์ผ๋ก ์ถฉ๋ถํ๋ค. ๋ฐ๋ฉด ์ค์ ๋ก ์๋ ๊ฒ์ ์ธ ๊ฐ์ง: (1) ๋ง์ NPC๋ InteractionResponse/InteractionTarget ์ปดํฌ๋ํธ๊ฐ ์์ด ์ ์ด์ ๋ง์ ๊ฑธ ์์กฐ์ฐจ ์์๊ณ , (2) ๋ํ ์ข
๋ฃ ์ ์์ดํ
์ ์ง๊ธํ๋ ๋ก์ง์ด ์ ํ ์์๊ณ , (3) "๋ชฝํ์ฃผ ์ฌ์"์ ๊ธฐ์กด ์ฝ๋๊ฐ PlayAnimation(์ฑ๊ธ๋
ธ๋, AnimSequence์ฉ)๋ง ์ง์ํด ์ง์ง ๋ชฝํ์ฃผ(์น์
ยท๋ธ๋ ๋)๋ ๋ชป ์ผ๋ค.
// URetrieveDialogueComponent::PlayGreetingAnimation
if (GreetingMontage)
{
// Slot ์ฌ์์ AnimBP ์ํ๋จธ์ (Idle/Run ๋ธ๋ ๋)์ ๊ทธ๋๋ก ๋ ์ฑ ์์ ์นํ๋ค.
// ์์ฐฐ ์ค์ธ Villager๋ ์ด๋ ์ ๋๋ฉ์ด์
์ด ๋๊ธฐ์ง ์๋๋ค.
if (UAnimInstance* AnimInstance = CachedMesh->GetAnimInstance())
{
AnimInstance->Montage_Play(GreetingMontage);
return;
}
}
๊ธฐ์กด PlayAnimation ๋ฐฉ์(๋์ฌ ์์ ์ CachedMesh->PlayAnimation(TalkingAnimation, true))์ AnimBP๋ฅผ Single Node ๋ชจ๋๋ก ๊ฐ์ ์ ํํด๋ฒ๋ ค ์ํ๋จธ์ ์ด ๋ฉ์ถ๋ค. ์ ์ง ์ํ์ธ ์์ NPC๋ ์ด๋๋ ํฐ๊ฐ ์ ๋์ง๋ง, ์์ฐฐ ์ค์ธ Villager์๊ฒ ์ฐ๋ฉด ๋ํ ๋์ค ๊ฑท๊ธฐ ์ ๋๋ฉ์ด์
์ด ์ผ์ด๋ถ๋๋ค. Montage_Play๋ AnimBP๊ฐ ์ด๋ฏธ ๊ฐ๊ณ ์๋ DefaultSlot ๋
ธ๋๋ก ์ฌ์๋๋ฏ๋ก ์ํ๋จธ์ ์ ๊ฑด๋๋ฆฌ์ง ์๋๋ค โ ๊ธฐ์กด Animation/TalkingAnimation ํ๋๋ ๊ทธ๋๋ก ๋๊ณ Montage ํ๋๋ฅผ ์ต์
์ผ๋ก ์ถ๊ฐํด, ๊ฐ์ด ์์ผ๋ฉด Slot ์ฌ์, ์์ผ๋ฉด ๊ธฐ์กด ๋ฐฉ์์ผ๋ก ํด๋ฐฑํ๊ฒ ํด์ ์์ NPC ์ชฝ์ ์ ํ ๊ฑด๋๋ฆฌ์ง ์์๋ค.
bool URetrieveDialogueComponent::TryGrantItemReward(AActor* Instigator)
{
if (ItemRewardPool.Num() == 0 || ItemRewardChance <= 0.f) return false;
if (FMath::FRand() > ItemRewardChance) return false;
// ๊ฐ์ค์น ๋์ ํ ๊ตด๋ ค์ ํ๋ ์ ํ โ Instigator์ InventoryComponent::AddItem ํธ์ถ
}
ItemRewardChance ๊ธฐ๋ณธ๊ฐ์ 0์ผ๋ก ๋ฌ์, ๊ธฐ์กด NPC๋ ์ด ํ๋๋ฅผ ์ ๊ฑด๋๋ฆฌ๋ ํ ์ง๊ธ์ด ์์ ๋ฐ์ํ์ง ์๋๋ค. ํธ์ถ ์ง์ ์ RetrievePlayerController::CloseConversation()(๋ํ ์ข
๋ฃ ์์ )์ผ๋ก, ๊ธฐ์กด์ ์๋ CompleteStepOnConversationEnd ํ์คํธ ์๋ฃ ๋ก์ง๊ณผ ๊ฐ์ ์๋ฆฌ์ ์น์๋ค.
์ฌ๊ธฐ์๋ ์ธ ๊ฐ์ง ๋ฒ๊ทธ๋ฅผ ์ค์ ๋ก ๊ฒช์๋ค.
1) ๋์ฌ๊ฐ ์ ๋จ๋ ๋ฒ๊ทธ โ DataTable ๋ฐฐ์ด ํ๋ ์ฐ๊ธฐ๊ฐ ์กฐ์ฉํ ๊นจ์ง. Monolith์ add_data_table_row๋ก Lines(TArray\<FText>)์ FollowUpRows(TArray\<FName>)๋ฅผ ์ฑ์ ๋๋ฐ, ํด์ "fields_set": 8๋ก ์ฑ๊ณต์ ๋ณด๊ณ ํ์ง๋ง ์ค์ ๋ก ์ ์ฅ๋ ๊ฐ์ ๋น ๋ฌธ์์ด์ด์๋ค. ํธ์ง๊ธฐ ๋ก๊ทธ์ "Json Value of type 'Array' used as a 'String'" ์๋ฌ๊ฐ ์์๋ ๊ฑธ ๋ค๋ฆ๊ฒ ์ฐ๊ฒฐ์ง์๋ค. ๊ฒ์ฆ์ ํญ์ ์ฐ๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ๋ฏฟ์ง ๋ง๊ณ get_data_table_rows๋ก ๋ค์ ์ฝ์ด์ ํ์ธํด์ผ ํ๋ค๋ ๊ฑธ ๋ค์ ๋ฐฐ์ ๋ค.
# ๋ณต๊ตฌ: JSON ๊ฒฝ๋ก๊ฐ ๋ฐฐ์ด ํ๋์์ ๊นจ์ง๋ฏ๋ก CSV ์๋ณต์ผ๋ก ์ฐํ
csv_text = dt.export_to_csv_string()
rows = list(csv.reader(io.StringIO(csv_text)))
# Lines/FollowUpRows ์ปฌ๋ผ๋ง ์ง์ ์ฑ์ ๋ฃ๊ณ
dt.fill_from_csv_string(new_csv_text) # ์ด ๊ฒฝ๋ก๋ ๋ฐฐ์ด์ ์ ๊นจ๋จ๋ฆผ
2) ๋ํ ์ค์๋ ์์ฐฐ ์ด๋์ด ์ ๋ฉ์ถ๋ ๋ฒ๊ทธ. StateTree๋ ๋ํ ์ํ๋ฅผ ์ ํ ๋ชฐ๋ผ์, ๋ํ์ฐฝ์ด ๋ ์์ด๋ Villager๊ฐ ๊ณ์ ๊ฑธ์ด ๋ค๋
๋ค. ANPCPatrolAIController์ Deactivate()/Reactivate()๋ฅผ ์ถ๊ฐํด URetrieveDialogueComponent::OpenConversationFor(๋ํ ์์)์์ ์ ์ง, ReturnToIdle()(๋ํ ์ข
๋ฃ ์ฝ๋ฐฑ)์์ ์ฌ๊ฐํ๋๋ก ์ฐ๊ฒฐํ๋ค. AI ์ปจํธ๋กค๋ฌ๊ฐ ์๋ ์์ NPC๋ Cast<ANPCPatrolAIController>๊ฐ ์คํจํ๋ฏ๋ก ์์ฐํ ์๋ฌด ์ํฅ์ด ์๋ค.
3) ์ํธ์์ฉ ํ๋กฌํํธ ํ
์คํธ("Interact")๋ฅผ ๋ชป ๋ฐ๊พผ ์ฌ๋ก. Manager_InteractionTarget_C์ InteractionText ๋งต(TMap)์ ์ ๊ฐ์ผ๋ก ๋ฎ์ด์จ๋ ์ฌ์ ํ "Interact"๊ฐ ํ์๋๊ณ , ๋น ์ํธ์์ฉ ์ฐฝ์ด ๊ฒน์ณ ๋ณด์ด๋ ์ ์ฆ์๊น์ง ๋ํ๋ฌ๋ค. BP ์ปดํฌ๋ํธ๋ฅผ ์กํฐ๊ฐ ์ด๋ฏธ ๋ ๋ฒจ์ ๋ฐฐ์น๋ ๋ค์ ์ถ๊ฐํ๋ฉด ์ธ์คํด์ค๊ฐ ๊ตฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๊ณ ์์ ์ ์๋ค๋ ๊ฐ์ค๋ก, ์กํฐ๋ฅผ ์ง์ฐ๊ณ ์๋ก ๋ฐฐ์นํด๋ณด๋ ์ ์ฐจ๋ก ๋๊ฒผ๋ค โ ์ด ๋ถ๋ถ์ ์ธ์
์ด ์ข
๋ฃ๋๋ ์์ ๊น์ง ๊ฒฐ๋ก ์ด ๋์ง ์์ ๋ค์ ํ์ธ์ด ํ์ํ ์ฑ๋ก ๋จ์๋ค.
์ฐ๊ธฐ API๊ฐ ์ฑ๊ณต์ ๋ณด๊ณ ํด๋, ๋ฐฐ์ด/๋ณตํฉ ํ์ ํ๋๋ ๋ฐ๋์ ์ฝ์ด์ ์ฌ๊ฒ์ฆํ๋ค. DataTable์
TArray<FText>/TArray<FName>ํ๋์ฒ๋ผ JSON ์ง๋ ฌํ๊ฐ ๊น๋ค๋ก์ด ํ์ ์ "์ฑ๊ณต" ์๋ต๊ณผ ์ค์ ์ ์ฅ ๊ฐ์ด ๋ค๋ฅผ ์ ์๋ค. CSV ์๋ณต์ฒ๋ผ ๋ ์์ ์ ์ธ ์ฐํ ๊ฒฝ๋ก๋ฅผ ์์๋๋ฉด ๋งํ์ ๋ ๋ฐ๋ก ๋์ฒด ์๋จ์ผ๋ก ์ธ ์ ์๋ค.
๊ธฐ์กด ๋ฐฉ์์ ๋์ฒดํ์ง ๋ง๊ณ ์ต์ ์ผ๋ก ์น์ผ๋ฉด ํ๊ท ์ํ์ด ์๋ค.PlayAnimation์์Montageํ๋๋ฅผ ์ต์ ์ผ๋ก ์ถ๊ฐํ ๊ฒ์ฒ๋ผ, ์ ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ๊ฐ์์ ๊ธฐ์กด ๋์์ ๊ทธ๋๋ก ์ฌํํ๊ฒ ๋ง๋ค๋ฉด ์ด๋ฏธ ๋์ํ๋ 7์ข ์ ์์ NPC๋ฅผ ํ๋๋ ๊ฑด๋๋ฆฌ์ง ์๊ณ ์ ๊ท NPC ํ์ ์๋ง ์ ๊ธฐ๋ฅ์ ์ผค ์ ์๋ค.
AI ์ํ(๋ํ ์ค)๋ฅผ ์ด๋ ์์คํ ์ด ๋ชจ๋ฅด๋ฉด ๋ฐ๋์ ์์ ๋์จ๋ค. ์ํธ์์ฉ/๋ํ ๊ฐ์ "์ผ์์ ์ผ๋ก ๋ค๋ฅธ ์์คํ ์ด ์บ๋ฆญํฐ๋ฅผ ์ ์ ํ๋" ์ํฉ์ ์ด๋ ๋ก์ง์ ๋ช ์์ ์ผ๋ก ์๋ ค์ผ ํ๋ค โ ์ด๋ฒ์ Deactivate/Reactivate ํ์ด๋ก ์ฐ๊ฒฐํ์ง๋ง, ์ด๋ฐ ์ข ๋ฅ์ ํ ์ ์ NPC ํ์ ์ ๋ง๋ค ๋๋ง๋ค ๋น ์ง๊ธฐ ์ฌ์ด ์ง์ ์ด๋ผ ์ฒดํฌ๋ฆฌ์คํธํํ ํ์๊ฐ ์๋ค.
์ ํฌ ์ค ESC๋ฅผ ๋๋ฅด๋ฉด ์ผ์์ ์ง ๋ฉ๋ด(๊ณ์ํ๊ธฐ / ์ค์ / ์กฐ์ํค ์๋ด / ๋ฉ์ธ ๋ฉ๋ด๋ก / ๊ฒ์ ์ข
๋ฃ)๊ฐ ๋จ๊ฒ ํ๊ณ , ์ด๋ฏธ ์กด์ฌํ๋ ๋ฐฐํ ํจ๋ ์์คํ
(์ธ๋ฒคํ ๋ฆฌยท์๋๋งตยท์์ ๊ณผ ๊ฐ์ OpenExclusivePanel ํ๋ฆ)์ ์์ฐ์ค๋ฝ๊ฒ ํธ์
์ํจ๋ค.
๊ธฐ์กด ARetrievePlayerController::InputKey๋ ESC๋ฅผ ํจ๋์ด ์ด๋ ค ์์ ๋ ๋ซ๋ ์ฉ๋๋ก๋ง ์ฒ๋ฆฌํ๋ค(if (ActivePanel && Key == Escape) CloseActivePanel()). ํจ๋์ด ํ๋๋ ์๋ ์์ ๊ฒ์ํ๋ ์ด ์ํ์์ ESC๋ ์๋ฌด ๋์๋ ํ์ง ์์๊ณ , ์ผ์์ ์ง ๋ฉ๋ด ์์ ฏ ์์ฒด๋ ์์๋ค.
๊ฒ์ํ๋ ์ด ์ค(ํจ๋ยทํต์ฌ๋กฏ ํ ๋ชจ๋ ์์) ESC๋ฅผ OpenPauseMenu๋ก ์ฐ๊ฒฐํ๋ค.
// ์์ ๊ฒ์ํ๋ ์ด ์ค ESC โ ์ผ์์ ์ง ๋ฉ๋ด
if (!ActivePanel && !bQuickSlotWheelOpen && Params.Key == EKeys::Escape)
{
OpenPauseMenu();
return true;
}
๋ฉ๋ด ์์ ฏ์ ๋ฐฐํ ํจ๋์ ๊ณตํต ๋ฒ ์ด์ค(URetrieveGamePanelWidget)๋ฅผ ์์ํ C++ ๋ฒ ์ด์ค๋ก ๋ง๋ค์ด, ๋ฒํผ OnClicked ๋ฐฐ์ ์ ์ ๋ถ ์ฝ๋์์ ์ฒ๋ฆฌํ๋ค. WBP๋ ์๋ ์ด๋ฆ์ ๋ฒํผ๋ง ๋ฐฐ์นํ๋ฉด ๊ทธ๋ํ ๋ฐฐ์ ์์ด ๋์ํ๋ค.
UCLASS(Abstract, Blueprintable)
class URetrievePauseMenuWidget : public URetrieveGamePanelWidget
{
protected:
virtual void NativeConstruct() override; // ๊ฐ ๋ฒํผ OnClicked.AddDynamic
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> ResumeButton; // RequestClose()
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> SettingsButton; // OpenSettingsPanel()
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> CommandsButton; // OpenControlsGuide()
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> MainMenuButton; // Server_RequestQuitToMenu()
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> QuitButton; // QuitGame()
};
๋ฉ๋ด๋ URetrieveGamePanelWidget ์์์ด๋ผ ESC๋ฅผ ๋ค์ ๋๋ฅด๋ฉด ๋ฒ ์ด์ค์ NativeOnKeyDown์ด ๋ซ์์ค๋ค. ์ฒ์์ UGameplayStatics::SetGamePaused(true)๋ก ์ค์ ์ ์ง๋ฅผ ๋ฃ์๋ค๊ฐ, ๋ฉํฐํ๋ ์ด์์๋ ํ ํด๋ผ์ด์ธํธ๊ฐ ์ ์ฒด๋ฅผ ๋ฉ์ถ ์ ์์ผ๋ฏ๋ก ๊ฑท์ด๋ด๊ณ , ํจ๋์ด ์ด๋ฆฐ ๋์ ์ ํฌ ์
๋ ฅ๋ง ํด๋ผ์ด์ธํธ ๋ก์ปฌ๋ก ์ฐจ๋จํ๋ ๊ธฐ์กด ๋ฐฉ์(PlayerTick์ if (!ActivePanel))์ ๋งก๊ฒผ๋ค.
์์ ฏ ๋ฒํผ ๋ฐฐ์ ์ ๊ทธ๋ํ๊ฐ ์๋๋ผ C++
BindWidgetOptional๋ฒ ์ด์ค์ ๋๋ฉด ๋๊ตฌ ํ๊ณ๋ฅผ ์ฐํํ๋ค. Monolith ๊ฐ์ ์๋ํ๋ก WidgetBlueprint์ OnClicked๋ฅผ ๋ฐฐ์ ํ๋ ๊ฑด ๋ถ์์ ํ์ง๋ง, C++ ๋ฒ ์ด์ค๊ฐ ์ด๋ฆ์ผ๋ก ๋ฒํผ์ ์ก์ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ด๋ฉด WBP๋ ๋ฒํผ ์ด๋ฆ๋ง ๋ง์ถ๋ฉด ๋๋ค.
๋ฉํฐํ๋ ์ด๋ฅผ ์ ์ ํ๋ฉด "์ผ์์ ์ง"๋ ์๊ฐ ์ ์ง๊ฐ ์๋๋ผ ํด๋ผ ๋ก์ปฌ UI๋ค.SetGamePaused๋ ์ฑ๊ธ์์๋ง ์๋ฏธ๊ฐ ์๊ณ , ๋คํธ์ํฌ์์๋ ์ ๋ ฅ ์ฐจ๋จ(ํด๋ผ ๋ก์ปฌ)๋ง์ผ๋ก ๋ฉ๋ด UX๋ฅผ ๋ง๋๋ ํธ์ด ์์ ํ๋ค.
์ผ์์ ์ง ๋ฉ๋ด๊ฐ ์๋ํฐ PIE๋ฟ ์๋๋ผ Standaloneยทํจํค์ง ๋น๋์์๋ ESC๋ก ๋จ๊ฒ ํ๋ค.
PIE์์๋ ๋๋๋ฐ Standalone/ํจํค์ง์์ ESC๋ฅผ ๋๋ฌ๋ ๋ฉ๋ด๊ฐ ์ ๋ด๋ค. ๋ก๊ทธ์ ๊ฒฐ์ ์ ๋จ์๋ Failed to open pause menu: PauseMenuClass is empty โ ์ฆ ์
๋ ฅยท๋ก์ง์ ์ ์์ด๊ณ PauseMenuClass.LoadSynchronous()๊ฐ null์ด์๋ค. ํ์ธํด ๋ณด๋ ์ฟก๋ ํด๋์ WBP_SettingsScreen์ ์๋๋ฐ WBP_PauseMenu๋ ์์ ์์๋ค. PauseMenuClass๊ฐ TSoftClassPtr์ธ๋ฐ, ์ด ํ๋ก์ ํธ์ ์ฟ ์ปค๋ ์ํํธ ์ฐธ์กฐ๋ง ๊ฑธ๋ฆฐ UI ํด๋๊น์ง ๋ฐ๋ผ๊ฐ์ง ์์ ๋์ ์์ ฏ์ ํจํค์ง์ ๋ฃ์ง ์์๋ ๊ฒ์ด๋ค(๊ณผ๊ฑฐ F10 ์ค์ ์ฐฝ๋ ๋๊ฐ์ ์ด์ ๋ก ์ ๋ด๊ณ /Game/Retrieve/UI/Settings๋ฅผ ๊ฐ์ ์ฟก ๋ชฉ๋ก์ ๋ฃ์ด ๊ณ ์น ์ด๋ ฅ์ด ์์๋ค).
; DefaultGame.ini โ TSoftClassPtr๋ก๋ง ์ฐธ์กฐ๋๋ UI ํด๋๋ ์ฟ ์ปค๊ฐ ๋๋ฌ ๋ชป ํจ
+DirectoriesToAlwaysCook=(Path="/Game/Retrieve/UI/Menu")
๊ฐ ์์ฒด๋ ํด๋ก ๋ฃ์ CDO ์ค๋ฒ๋ผ์ด๋๊ฐ ๋์คํฌ์ ์์ ์ ์ผ๋ก ์ง๋ ฌํ๋์ง ์์, ์ด๋ฏธ ์ ๋๋ SettingsPanelClass์ ๋๊ฐ์ด C++ ์์ฑ์์ ๊ณ ์ ํ๋ค.
// ๊ฐ์ ์ฝ๋์ ๋ฐ์ BP ์ง๋ ฌํยทํด์ ์์กดํ์ง ์๋๋ค
PauseMenuClass = TSoftClassPtr<URetrieveGamePanelWidget>(
FSoftObjectPath(TEXT("/Game/Retrieve/UI/Menu/WBP_PauseMenu.WBP_PauseMenu_C")));
// OpenPauseMenu โ ๊ฐ์ด ๋น์ด๋(์: BP์ None ์ค๋ฒ๋ผ์ด๋) ์๋ ค์ง ๊ฒฝ๋ก๋ก ํด๋ฐฑ
TSubclassOf<URetrieveGamePanelWidget> PanelClass = PauseMenuClass.LoadSynchronous();
if (!PanelClass)
{
PanelClass = LoadClass<URetrieveGamePanelWidget>(
nullptr, TEXT("/Game/Retrieve/UI/Menu/WBP_PauseMenu.WBP_PauseMenu_C"));
}
์ง๋จ์ ํต์ฌ์ ๋ก๊ทธ๊ฐ ์๋๋ผ ์ฟก ์ฐ์ถ๋ฌผ ๋น๊ต์๋ค. Saved/Cooked/.../UI/Menu์ ํน์ ์์ ฏ์ด ์๋์ง/์๋์ง๊ฐ "๊ฒ์์ด ์ค์ ๋ก ๋ก๋ํ ์ ์๋๊ฐ"์ ๊ทธ๋ผ์ด๋ ํธ๋ฃจ์ค์๊ณ , ์๋ํฐ ์ธ๋ฉ๋ชจ๋ฆฌ CDO๋ง ํ์ธํ๋ฉด ๊ณ์ ํ๋ค๋ฆฌ๋ฅผ ์ง๊ฒ ๋๋ค.
TSoftClassPtr๋ก๋ง ์ฐธ์กฐ๋๋ UI ํด๋๋DirectoriesToAlwaysCook์ ๋ฑ๋กํด์ผ ํจํค์ง์ ๋ค์ด๊ฐ๋ค. ์ํํธ ์ฐธ์กฐ๋ ํ๋ ์ฐธ์กฐ ์ฒด์ธ์ด ์์ผ๋ฉด ์ฟ ์ปค๊ฐ ๋๋ฝํ ์ ์๊ณ , ๊ทธ๋ฌ๋ฉด ์๋ํฐ์์๋ ๋ฉ์ฉกํ๋ฐ ํจํค์ง์์๋ง ์กฐ์ฉํ ์ฌ๋ผ์ง๋ค.
"์๋ํฐ์์ ๋๋๋ฐ ํจํค์ง์์ ์ ๋๋ฉด" ์ฟก ์ฐ์ถ๋ฌผ์ ์ง์ ๋ณธ๋ค. ์ธ๋ฉ๋ชจ๋ฆฌ ์ํ๋ ๋์คํฌ/์ฟก ์ํ์ ๋ค๋ฅผ ์ ์์ผ๋ฏ๋ก,Saved/Cooked์ ์์ ์ด ์ค์ ๋ก ์กด์ฌํ๋์ง๊ฐ ํ์ ๊ธฐ์ค์ด ๋๋ค.
์์ ฏ ํด๋์ค ๊ธฐ๋ณธ ํ ๋น์ ๋๊ตฌ๊ฐ ์๋๋ผ C++ ์์ฑ์(๋๋ ์ฌ๋์ด Details ํจ๋์์) ๊ณ ์ ํ๋ค. ์ธ๋ฉ๋ชจ๋ฆฌ CDO์๋ ๋ณด์ฌ๋ ๋์คํฌ ์ง๋ ฌํ์ ์คํจํ๋ ๊ฒฝ์ฐ๊ฐ ์์ด, ์ฌ์ปดํ์ผยท์ฌํจํค์ง ํ ์กฐ์ฉํ ๋น ๊ฐ์ด ๋๋ค.
๋ฉ์ธ ๋ฉ๋ด์ "์ค์ " ๋ฒํผ๊ณผ ์ผ์์ ์ง ๋ฉ๋ด์ "์กฐ์ํค ์๋ด" ๋ฒํผ์ ์ด๋ฏธ ์๋ ๊ธฐ๋ฅ์ ์ฐ๊ฒฐํ๋ค. ์ ํ๋ฆ์ ๋ง๋๋ ๊ฒ ์๋๋ผ ๊ธฐ์กด ์ง์ ์ ์ ์ฌ์ฌ์ฉํด ๋ฐฐ์ ๋ง ํ๋ ๊ฒ์ด ๋ชฉํ๋ค.
๋ฉ์ธ ๋ฉ๋ด์ OptionsButton์ NativeConstruct์์ SetIsEnabled(false)๋ก ์์ ์ฃฝ์ด ์์๊ณ ํด๋ฆญ ํธ๋ค๋ฌ๋ ์์๋ค. ์ผ์์ ์ง ๋ฉ๋ด์ CommandsButton(์กฐ์ํค ์๋ด) ์ญ์ ์์ ฏ์๋ ๋ฐฐ์น๋ผ ์์์ง๋ง ๋์ํ๋ C++ ํธ๋ค๋ฌ๊ฐ ์์ด ๋๋ฌ๋ ๋ฌด๋ฐ์์ด์๋ค.
OpenSettingsPanel / OpenControlsGuide์ ์ฐ๊ฒฐ// RetrieveMainMenuWidget.cpp โ ๋นํ์ฑํ๋ฅผ ์ ๊ฑฐํ๊ณ ๊ธฐ์กด ์ง์
์ ์ ์ฐ๊ฒฐ
if (OptionsButton)
{
OptionsButton->OnClicked.AddDynamic(this, &URetrieveMainMenuWidget::HandleOptionsClicked);
}
void URetrieveMainMenuWidget::HandleOptionsClicked()
{
if (ARetrievePlayerController* PC = GetRetrievePlayerController())
{
PC->OpenSettingsPanel(); // F10๊ณผ ๋์ผํ ์ง์
์ ์ฌ์ฌ์ฉ
}
}
์กฐ์ํค ์๋ด๋ ๊ฐ์ ํจํด์ผ๋ก, PC์ OpenControlsGuide๋ฅผ ์ถ๊ฐํ๊ณ ๊ทธ ์์์ ๋ฐฐํ ํจ๋ ์์คํ
(OpenExclusivePanel)์ ์ฌ์ฌ์ฉํ๋ค. ๋ ๊ฒฝ์ฐ ๋ชจ๋ ํจ๋ ๋ซ๊ธฐ ์ ์
๋ ฅ ๋ชจ๋ ๋ณต์ ๋ก์ง์ด ์ด๋ฏธ ์ธ์
์ํ๋ณ๋ก ๋ถ๊ธฐ๋ผ ์์๋ ๋์ ๋ดค๋ค.
// RemoveActivePanelImmediately โ ๋ฉ์ธ๋ฉ๋ด/๊ฒฐ๊ณผ ํ๋ฉด์์ ์ฐ ํจ๋์ UIOnly+์ปค์ ์ ์ง
if (SessionState == ERetrieveSessionState::MainMenu ||
SessionState == ERetrieveSessionState::Result)
{
UpdateInputMode(SessionState);
}
else { /* ์ธ๊ฒ์์์๋ง GameOnly๋ก ๋ณต๊ท */ }
์ฆ ๋ฉ์ธ ๋ฉ๋ด์์ ์ค์ ์ฐฝ์ ์ด์๋ค ๋ซ์๋ ์ปค์ยทํค๋ณด๋ ์กฐ์์ด ์ฃฝ์ง ์๊ณ ์ ์ ๋ณต๊ทํ๋ค โ ์ ๊ท ํ๋ฆ์ ์ํ ๋ณ๋ ์ฒ๋ฆฌ๊ฐ ํ์ ์์๋ค.
๋ฒํผ์ด "๋ฌด๋ฐ์"์ผ ๋ ๋จผ์ ์์ฌํ ๊ฒ์ ๋ฐฐ์ ์ด ์๋๋ผ ํ์ฑ ์ํ๋ค.
SetIsEnabled(false)๋ก ๋งํ ์๊ฑฐ๋ ํธ๋ค๋ฌ๊ฐ ์์ ์๋ ๊ฒฝ์ฐ๊ฐ ํํ๊ณ , ์ด๊ฑด ๋ฐฐ์ ๋ก์ง์ ์๋ฌด๋ฆฌ ๋ด๋ ์ ๋ณด์ธ๋ค.
๊ธฐ์กด ์ง์ ์ (OpenSettingsPanel/OpenExclusivePanel)์ ์ฌ์ฌ์ฉํ๋ฉด ์ ๊ท UI๋ "๋ฐฐ์ "๋ง ๋จ๋๋ค. ์ ๋ ฅ ๋ชจ๋ ๋ณต์์ฒ๋ผ ์ธ์ ์ํ๋ณ ๋ถ๊ธฐ๊ฐ ์ด๋ฏธ ๋ง๋ จ๋ผ ์์ผ๋ฉด, ์ ํ๋ฉด์ ๊ทธ ํ๋ฆ์ ์น๋ ๊ฒ๋ง์ผ๋ก ์ฃ์ง ์ผ์ด์ค๊น์ง ๊ณต์ง๋ก ์ปค๋ฒ๋๋ค.
์กฐ์ํค ์๋ด ํ๋ฉด์ ๋ง๋ค๋, ํ์ ๋ค์ด๊ฐ๋ ํค๋ ๋์์ธ ๋ชฉ์ ์ด๋ ์ถ์ธก์ด ์๋๋ผ ๊ฒ์์ ์ค์ ๋งคํ๋ ํค๋ก ์ฑ์ด๋ค.
์ฒ์์ ์ฐธ๊ณ ์ด๋ฏธ์ง์ ์กฐ์ํ๋ฅผ ๊ทธ๋๋ก ์ฎ๊ฒผ๋๋ฐ, ์ค์ ๊ฒ์๊ณผ ์ด๊ธ๋๋ ๊ณณ์ด ๋ง์๋ค(์ด๋ฏธ์ง์์ E๊ฐ ์ํธ์์ฉ์ด์์ง๋ง ์ค์ ๋ก๋ E=๋ฒ์คํธยทF=์ํธ์์ฉ, R=๋ฐฉ์ด, Tab=ํ๊ฒ ๊ณ ์ ๋ฑ). "์ค์ ํค"์ ๊ทผ๊ฑฐ๋ฅผ ์ฝ๋/๋ฐ์ดํฐ์์ ์ง์ ๋ฝ์์ผ ํ๋ค.
IMC_Default์์ ๋งคํ์ ๋คํํ๊ณ ํ๋์ฝ๋ฉ ํค์ ํฉ์น๊ธฐUE5.7์์๋ UInputMappingContext์ ๊ตฌ mappings ํ๋กํผํฐ๊ฐ deprecated ๋์ด ๋น ๋ฐฐ์ด์ ๋ฐํํ๋ค. ์ ๊ฒฝ๋ก(default_key_mappings โ mappings)๋ก ์ ๊ทผํด ์ค์ IAโํค๋ฅผ ๋คํํ๋ค.
imc = unreal.load_asset('/Game/Retrieve/Input/IMC_Default')
# UE5.7: ๊ตฌ 'mappings'๋ deprecated(๋น ๋ฐฐ์ด) โ default_key_mappings ๊ฒฝ์
ms = imc.get_editor_property('default_key_mappings').get_editor_property('mappings')
for m in ms:
a = m.get_editor_property('action')
k = m.get_editor_property('key')
print(a.get_name(), '=>', k.get_editor_property('key_name'))
# IA_Move=>W/S/A/D, IA_Jump=>SpaceBar, IA_Attack=>LMB, IA_HeavyAttack=>RMB,
# IA_Guard=>R, IA_LockOn=>Tab, IA_Burst=>E, IA_Absorb=>Q,
# IA_ElementMode1/2/3=>1/2/3, IA_Interaction=>F, IA_UseItem1/2=>4/5, IA_Zoom=>ํ ...
IMC์ ์๊ณ PlayerController::InputKey์์ ์ง์ ์ฒ๋ฆฌ๋๋ ํ๋์ฝ๋ฉ ํค(์ค์ =F10, ํต์ฌ๋กฏ ํ =T, ๋ฏธ๋๋งต ํ์ =N, ์ธ๋ฒค=I, ์๋๋งต=M, ์ผ์์ ์ง=ESC)๋ ๋ฐ๋ก ์ทจํฉํด ํฉ์ณค๋ค. ์์ ฏ์ URetrieveGamePanelWidget ์์์ผ๋ก ๋ง๋ค์ด ESC๋ก ๋ซํ๊ฒ ํ๊ณ , ์นดํ
๊ณ ๋ฆฌ(์ด๋/์ ํฌ/์์/์์คํ
)๋ณ ์์ ์ฝ๋ฉ๊ณผ ํค์บก ๋ฐฐ์น๋ก ๊ตฌ์ฑํ๋ค.
"์ค์ ์กฐ์"์ ๊ทผ๊ฑฐ๋ ๋์์ธ ๋ชฉ์ ์ด ์๋๋ผ IMC์ ์ฝ๋์ ํ๋์ฝ๋ฉ ํค๋ค. Enhanced Input์ ์ฐ๋ฉด ๋ฌผ๋ฆฌ ํค๋ IMC์, ๋๋จธ์ง๋ ์ปจํธ๋กค๋ฌ์
InputKey๋ถ๊ธฐ์ ํฉ์ด์ ธ ์์ผ๋ฏ๋ก ๋ ๋ค ํ์ด์ผ ์ง์ง ๋งคํ์ด ๋์จ๋ค.
์์ง ๋ฒ์ ์ด ์ฌ๋ผ๊ฐ๋ฉด ๋ฐ์ดํฐ ์ ๊ทผ ๊ฒฝ๋ก๊ฐ ๋ฐ๋๋ค. UE5.7์ IMC๋mappings(deprecated)๊ฐ ์๋๋ผdefault_key_mappings.mappings๋ก ์ฝ์ด์ผ ํ๊ณ , ์ด๋ฐ ๊ฑด ์คํค๋ง๋ฅผ ์ง์ ํํค์ณ ํ์ธํด์ผ ํ๋ค.
์์ ฏ ๋ฒํผ ๋ฐฐ์ ๊ฐ์ C++ ๋ณ๊ฒฝ์ ์๋ํฐ๋ฅผ ์ผ ์ฑ ๋น ๋ฅด๊ฒ(Live Coding) ๋ฐ์ํ๋, ๋ฐ์์ด ๋๋ ๋ณ๊ฒฝ๊ณผ ์ ๋๋ ๋ณ๊ฒฝ์ ๊ตฌ๋ถํ๋ค.
"์กฐ์ํค ์๋ด" ๋ฒํผ์ฉ CommandsButton(์ UPROPERTY)๊ณผ HandleCommandsClicked/OpenControlsGuide(์ UFUNCTION)๋ฅผ ์ถ๊ฐํ๊ณ Live Coding์ผ๋ก ์ปดํ์ผํ๋๋ฐ, patch_applied๊ฐ ๋ด๋๋ฐ๋ ๊ทธ ๋ฒํผ๋ง ๋๋ฌ๋ ๋ฌด๋ฐ์์ด์๋ค. ๋ฐ๋ฉด ์ด์ ์ ์ ๋น๋์ ์ด๋ฏธ ๋ค์ด ์๋ ResumeButton์ ์ ๋์ํ๋ค.
// ์ด๋ฐ "์ ๋ฆฌํ๋ ์
๋ฉค๋ฒ"๋ Live Coding์ผ๋ก ๋ฑ๋ก๋์ง ์๋๋ค
UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr<UButton> CommandsButton; // ํด๋์ค ๋ ์ด์์ ๋ณ๊ฒฝ
UFUNCTION() void HandleCommandsClicked(); // ์ ๊ท ๋ฆฌํ๋ ์
ํจ์
Live Coding์ ํจ์ ๋ณธ๋ฌธ ๋ณ๊ฒฝ๋ง ํซํจ์นํ๊ณ , ์ UPROPERTY/UFUNCTION์ฒ๋ผ ํด๋์ค ๋ ์ด์์ยท๋ฆฌํ๋ ์
์ด ๋ฐ๋๋ ๋ณ๊ฒฝ์ ๋ฑ๋กํ์ง ๋ชปํ๋ค. ๊ทธ๋์ BindWidgetOptional์ด CommandsButton์ ์ฑ์ฐ์ง ๋ชปํด ํธ๋ค๋ฌ๊ฐ ๋ถ์ง ์์๋ค. ํด๊ฒฐ์ ๋จ์ํ์ง๋ง ํ์คํ๋ค โ ์๋ํฐ๋ฅผ ๋ซ๊ณ ์ ์ ์ฌ๋น๋ ํ ์ฌ์คํ(๋๋ ์ฌํจํค์ง). ์ฆ์์ผ๋ก ์์ธ์ ๊ตฌ๋ถํ ์ ์์๋ค: ๊ธฐ์กด ๋ฐ์ด๋๋ฆฌ์ ์๋ ๋ฒํผ์ ๋๊ณ , ๋ผ์ด๋ธ๋ก ์ถ๊ฐํ ์ ๊ท ๋ฉค๋ฒ๋ง ๋ฌด๋ฐ์.
Live Coding์ "ํจ์ ๋ณธ๋ฌธ ํซํจ์น" ์ ์ฉ์ผ๋ก ์๊ฐํ๋ค. ์
UPROPERTY/UFUNCTION/BindWidget๋ฉค๋ฒ ์ถ๊ฐ๋ ์ ์ ๋น๋๊ฐ ํ์ํ๋ฉฐ, ๋ผ์ด๋ธ ์ปดํ์ผ์ด ์ฑ๊ณต(patch_applied)ํด๋ ๋ฆฌํ๋ ์ ์๋ ๋ฐ์๋์ง ์๋๋ค.
"๋ฐฉ๊ธ ์ถ๊ฐํ ๊ฒ๋ง ์ ๋๊ณ ๊ธฐ์กด ๊ฒ์ ๋๋ค"๋ฉด ๋ฆฌํ๋ ์ ๋ฑ๋ก์ ์์ฌํ๋ค. ๋์ผ ๋ฉ์ปค๋์ฆ์ธ๋ฐ ์ ๊ท ๋ฉค๋ฒ๋ง ์ฃฝ์ด ์์ผ๋ฉด ๋ฐฐ์ ๋ก์ง์ด ์๋๋ผ ๋น๋ ๋ฐฉ์(๋ผ์ด๋ธ vs ์ ์)์ ๋ฌธ์ ๋ค.
| ์์ | ํต์ฌ ๋ฌธ์ | ํด๊ฒฐ ์์น |
|---|---|---|
| ๋ง์ NPC ์์ฐฐ AI | NPC ๋ฒ ์ด์ค์ AI ์์ฒด๊ฐ ์์๊ณ , ์ ํฌ AI(Enemy)์ ๊ฒฐํฉํด ์ฌ์ฌ์ฉ ๋ถ๊ฐ | Enemy์ ์์ ํ ๋ถ๋ฆฌ๋ ์ต์ StateTree(Task 1๊ฐ, Evaluator ์์) ์ ์ค |
| ์ด๋ ์ ํ์ ๋ฒ๊ทธ | bCanStrafe/bOrientRotationToMovement ์ถฉ๋ + ๋ฉ์ฌ ํ์ ๋ณด์ ๋๋ฝ | ํ์ ๊ถํ์ ํ ์ฃผ์ฒด๋ก ํต์ผ + ๊ธฐ์กด ์ ์ ๋์ ์ ์ ๊ณผ ์ปดํฌ๋ํธ ๊ฐ ๋น๊ต๋ก ์์ธ ํน์ |
| NPC ๋ํ ํ์ฅ | ๋ชฝํ์ฃผ ์ฌ์ยท์์ดํ ๋ณด์ ์์, ๋ํ ์ค ์ด๋ ์ ๋ฉ์ถค | AnimBP Slot ๊ธฐ๋ฐ ๋ชฝํ์ฃผ(์ต์ ํด๋ฐฑ), ํ๋ฅ ๊ธฐ๋ฐ ๋ณด์, Deactivate/Reactivate ํ |
| DataTable ๋ฐฐ์ด ํ๋ | JSON ์ฐ๊ธฐ ๊ฒฝ๋ก๊ฐ ๋ฐฐ์ด ํ๋์์ ์กฐ์ฉํ ์คํจ | CSV export/fill ์๋ณต์ผ๋ก ์ฐํ + ์ฐ๊ธฐ ํ ํญ์ ์ฌ๊ฒ์ฆ |
| ์ผ์์ ์ง ๋ฉ๋ด | ๊ฒ์ํ๋ ์ด ์ค ESC ์ง์ ์ ยท๋ฉ๋ด ์์ ฏ ๋ถ์ฌ | ESCโOpenPauseMenu + C++ ๋ฒ ์ด์ค ์์ ฏ์ผ๋ก ๋ฒํผ ๋ฐฐ์ ์ ์ฝ๋์ ๊ณ ์ , MP๋ ์ ์ง ๋์ ํด๋ผ ๋ก์ปฌ ์
๋ ฅ ์ฐจ๋จ |
| ํจํค์ง ๋ฏธํ์ ํจ๋ | ์ํํธ ์ฐธ์กฐ UI ํด๋๋ฅผ ์ฟ ์ปค๊ฐ ๋๋ฝ | DirectoriesToAlwaysCook ๋ฑ๋ก + ๊ฐ์ C++ ์์ฑ์ ๊ณ ์ + ๊ฒฝ๋ก ํด๋ฐฑ(์ผ์ค ์์ ์ฅ์น) |
| ๋ฉ๋ด ๋ฒํผ ๋ฐฐ์ | ๋ฒํผ์ด ๋นํ์ฑ/ํธ๋ค๋ฌ ๋ถ์ฌ | ์ฃฝ์ ๋ฒํผ์ ์ด๋ ค ๊ธฐ์กด ์ง์
์ (OpenSettingsPanel/OpenExclusivePanel) ์ฌ์ฌ์ฉ |
| ์กฐ์ํค ์๋ด | ์ฐธ๊ณ ์ด๋ฏธ์ง๊ฐ ์ค์ ๋ฐ์ธ๋ฉ๊ณผ ๋ถ์ผ์น | IMC_Default + ํ๋์ฝ๋ฉ ํค์์ ์ค์ ๋งคํ์ ์ง์ ๋คํ |
| Live Coding ํ๊ณ | ์ UPROPERTY/UFUNCTION ๋ฏธ๋ฐ์ | ํจ์ ๋ณธ๋ฌธ๋ง ๋ผ์ด๋ธ, ๋ฆฌํ๋ ์ ๋ฉค๋ฒ ์ถ๊ฐ๋ ์ ์ ์ฌ๋น๋ |
๊ณตํต ๊ตํ: ์ค๋ ๋ ๊ฐ๋ ์์ (๋ฐฐ๊ฒฝ NPC, ์ผ์์ ์ง/์กฐ์ํค UI)์ ๊ฒฐ๊ตญ ๊ฐ์ ์์น์ผ๋ก ์๋ ดํ๋ค.
- ์๋ ๊ฒ๊ณผ ์๋ ๊ฒ์ ์ ํํ ๊ฐ๋ฅธ๋ค. ๋ํ ํธ๋ฆฌยท๋คํ์ฐจ ๋ํยท์ค์ ์ฐฝยท๋ฐฐํ ํจ๋ยท์ ๋ ฅ๋ชจ๋ ๋ณต์์ ์ด๋ฏธ ์์๊ณ , AIยท๋ชฝํ์ฃผยท์์ดํ ๋ณด์ยท์ผ์์ ์ง ์ง์ ์ ยท์กฐ์ํค ์์ ฏ์ ์์๋ค. ์๋ ๊ฑธ ๋ค์ ๋ง๋ค์ง ์๊ณ ์๋ ๊ฒ๋ง ์น์ผ๋, ์์ NPC 7์ข ๋ ๊ธฐ์กด ๋ฉ๋ด ํ๋ฆ๋ ํ๋ ์ ๊ฑด๋๋ฆฌ๊ณ ์ ๊ท ๊ธฐ๋ฅ์ ๋ถ์ผ ์ ์์๋ค.
- ์ญํ ์ด ๋ค๋ฅธ ์กํฐยทUI๋ ์์ ์ง์ ๋ถํฐ ๋ถ๋ฆฌํ๊ณ , ์ ๊ธฐ๋ฅ์ ๊ธฐ์กด ๋์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฌํํ๋ ์ต์ ์ผ๋ก ์น๋๋ค. Enemy์์ ๊ฐ๋ผ๋ธ NPC AI,
PlayAnimation์์ ์น์Montage์ต์ ์ด ๊ทธ ์๋ค.- ๋ฌธ์ ๋ ๋๋ถ๋ถ ์ฝ๋๊ฐ ์๋๋ผ ํด์ฒด์ธ์ ๊ฒฝ๊ณ์์ ๋ฌ๋ค. DataTable ๋ฐฐ์ด ์ฐ๊ธฐ, ์ฟ ํน ๋๋ฝ, Live Coding์ ๋ฆฌํ๋ ์ ํ๊ณ, IMC ์ ๊ทผ ๊ฒฝ๋ก ๋ณ๊ฒฝ โ ๋ชจ๋ "์ฐ๊ธฐ ์ฑ๊ณต ์๋ต / ์๋ํฐ์์ ๋จ"์ ๋ฏฟ๋ ์๊ฐ ํ๋ค๋ฆฌ๋ฅผ ์ง๊ฒ ํ๋ค. ๊ทธ๋์ ํ์ ๊ธฐ์ค์ "์ฐ๊ณ ๋์ ์ฝ์ด ์ฌ๊ฒ์ฆํ๋๊ฐ / ์ฟก ์ฐ์ถ๋ฌผ์ ์๋๊ฐ / ์ ์ ๋น๋์ ๋ค์ด๊ฐ๋๊ฐ"๋ก ๋ฐ๊พธ๋ ๊ฒ์ด ์ค๋ ๋ฐ๋ณต๋ ๊ตํ์ด์๋ค.