๐ TIL - ์ผ๋ฐ ๋ชฌ์คํฐ StateTree ์ ํฌ ํ๋ฆ ๊ฐ์ ๋ฐ ์ถฉ๋ ๊ตฌ์กฐ ์ ๋ฆฌ
์ค๋์ ์ผ๋ฐ ๋ชฌ์คํฐ AI ์ ํฌ ํ๋ฆ์ ์ค์ฌ์ผ๋ก ๊ณต๊ฒฉ ์ง์ ์กฐ๊ฑด, ๊ณต๊ฒฉ๊ถ ๊ด๋ฆฌ ๊ตฌ์กฐ, ์ถฉ๋ ์ฑ๋ ๊ตฌ์ฑ, ๋ฝ์จ ๋์ ํ์ ๋ฑ์ ์ ๊ฒํ๋ค.
๊ธฐ์กด์๋ ์์์ ๋ชฌ์คํฐ๋ง ์์ ๋๋ ํฐ ๋ฌธ์ ๊ฐ ๋ณด์ด์ง ์์์ง๋ง, ์ฌ๋ฌ ๋ง๋ฆฌ๊ฐ ๋์์ ํ๋ ์ด์ด๋ฅผ ์ถ์ ํ๋ ์ํฉ์์๋ ๊ณต๊ฒฉ ์ํ ์ง์ ์ด ๋ฐ๋ณต์ ์ผ๋ก ์คํจํ๊ฑฐ๋ ๋ชฌ์คํฐ๋ผ๋ฆฌ ์๋ก ๊ฐ์ญํ๋ ํ์์ด ๋ฐ์ํ๋ค. ๋ํ ๋ฃจ๋ฉ NPC๊ฐ ๋ชฌ์คํฐ ๊ณต๊ฒฉ ๋์์ด๋ ๋ฝ์จ ๋์์ ํฌํจ๋๋ ๋ฌธ์ ๋ ํ์ธ๋์๋ค.
์ฒ์์๋ ๊ฐ๋ณ StateTree Task๋ ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์ ๋ฌธ์ ๋ฅผ ์์ฌํ์ง๋ง, ์ค์ ์์ธ์ ๊ณต๊ฒฉ๊ถ ๊ด๋ฆฌ ๋ฐฉ์๊ณผ ์ถฉ๋ ์ฑ๋ ๊ตฌ์ฑ, ํ๊ฒ ํ์ ๋ก์ง์ด ์๋ก ๋ง๋ฌผ๋ ค ๋ฐ์ํ๋ ๊ตฌ์กฐ์ ์ธ ๋ฌธ์ ์ ๊ฐ๊น์ ๋ค. ์ด๋ฒ ์์ ์์๋ ์ ํฌ ํ๋ฆ ์์ฒด๋ฅผ ๋ค์ ์ ๊ฒํ๋ฉด์ ๊ณต๊ฒฉ ์ง์ ์กฐ๊ฑด๊ณผ ์ถฉ๋ ๊ตฌ์กฐ๋ฅผ ์ ๋ฆฌํ๋ ๋ฐฉํฅ์ผ๋ก ์ ๊ทผํ๋ค.
๊ธฐ์กด EncirclementSubsystem์ ๊ณต๊ฒฉ ๊ฐ๋ฅํ ๋ชฌ์คํฐ ์๋ฅผ ๋จ์ ์นด์ดํธ ๋ฐฉ์์ผ๋ก ๊ด๋ฆฌํ๊ณ ์์๋ค.
MaxAttackTokens = 3
์ด ๋ฐฉ์์ ๋ชฌ์คํฐ ์ข ๋ฅ์ ๊ด๊ณ์์ด ๋ชจ๋ ๋์ผํ ๋น์ฉ์ผ๋ก ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ฒด๊ธ์ด ๋ค๋ฅธ ๋ชฌ์คํฐ๋ฅผ ํจ๊ป ์ด์ฉํ ๊ฒฝ์ฐ ์ ํฌ ๋ฐ๋๋ฅผ ์ธ๋ฐํ๊ฒ ์กฐ์ ํ๊ธฐ ์ด๋ ค์ ๋ค.
๋ํ ํ๋ ์ด์ด ์ฃผ๋ณ ๊ณต๊ฐ์ด ์ข์ ์ํฉ์์๋ ์ฌ๋ฌ ๋ชฌ์คํฐ๊ฐ ๋์์ ๊ณต๊ฒฉ ์ํ๋ก ์ง์ ํ๋ฉด์ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๊ณต๊ฒฉ ์ํ ์ง์
โ ์ด๋ ๊ฒฝ๋ก ๊ฐ์ญ
โ ๋ฐฉํฅ ์ ํ ๋ฐ๋ณต
โ ๊ณต๊ฒฉ ์์ ์คํจ
โ ๊ณต๊ฒฉ ์ํ ์ฌ์ง์
๋ฐ๋ณต
๊ฒฐ๊ณผ์ ์ผ๋ก ์ ํฌ ํ๋ฆ์ด ์์ฐ์ค๋ฝ๊ฒ ์ด์ด์ง์ง ๋ชปํ๊ณ ๊ณต๊ฒฉ ์ํ์ ์ด๋ ์ํ๋ฅผ ๋ฐ๋ณตํ๋ ํ์์ด ๋ฐ์ํ๋ค.
๊ณต๊ฒฉ๊ถ ๊ตฌ์กฐ๋ฅผ ๋จ์ ์นด์ดํธ ๋ฐฉ์์์ ์์ฐ(Budget) ๊ธฐ๋ฐ ๊ตฌ์กฐ๋ก ๋ณ๊ฒฝํ๋ค.
AttackTokenCost
AttackTokenBudget
๊ฐ ๋ชฌ์คํฐ๋ DataTable์ ์ ์๋ ๋น์ฉ์ ์ฌ์ฉํ๊ณ , ๊ณต๊ฒฉ ๊ฐ๋ฅํ ์ด ์์ฐ ๋ฒ์ ์์์๋ง ๊ณต๊ฒฉ ์ํ์ ์ง์ ํ ์ ์๋๋ก ๊ตฌ์ฑํ๋ค.
const int32 CurrentCost = GetCurrentAttackTokenCost(Ring);
const int32 RequestCost = GetAttackTokenCost(Requester);
const int32 Budget = GetAttackTokenBudget(Ring, Requester);
if (CurrentCost + RequestCost > Budget)
{
return false;
}
์ด๋ฅผ ํตํด ๋ชฌ์คํฐ ์ข ๋ฅ์ ๋ฐ๋ผ ๊ณต๊ฒฉ ๋ฐ๋๋ฅผ ๋ฐ์ดํฐ๋ง์ผ๋ก ์กฐ์ ํ ์ ์๋ ๊ธฐ๋ฐ์ ๋ง๋ จํ๋ค.
์ ํฌ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ฆฌ๋๋ค.
๋ชฌ์คํฐ ์ถ์
โ
ํ๋ ์ด์ด ์ฃผ๋ณ ๋ฐฐํ
โ
๊ณต๊ฒฉ๊ถ ํ๋
โ
๊ณต๊ฒฉ ์ํ ์ง์
โ
๊ณต๊ฒฉ ์ข
๋ฃ
โ
๋ฐฐํ ๋ณต๊ท
๊ธฐ์กด ๊ณต๊ฒฉ ๋ก์ง์ ๊ณต๊ฒฉ ๋ฒ์์ ์ง์ ํ๋ ์๊ฐ ์ฆ์ ๊ณต๊ฒฉ์ ์๋ํ๋ ๊ตฌ์กฐ์๋ค.
DistanceToTarget <= AttackRange
ํ์ง๋ง ์ค์ ํ๋ ์ด ์ํฉ์์๋ ํ๋ ์ด์ด์ ๋ชฌ์คํฐ ์ฌ์ด ๊ฑฐ๋ฆฌ๊ฐ ๊ณต๊ฒฉ ๋ฒ์ ๊ฒฝ๊ณ์์ ๊ณ์ ๋ณ๋ํ๊ธฐ ๋๋ฌธ์ ๊ณต๊ฒฉ ์์ ์ง์ ์ ์กฐ๊ฑด์ด ํด์ ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ฃผ ๋ฐ์ํ๋ค.
๊ณต๊ฒฉ ๋ฒ์ ์ง์
โ ๊ณต๊ฒฉ ์์ ์๋
โ ๊ฑฐ๋ฆฌ ์กฐ๊ฑด ํด์
โ ๊ณต๊ฒฉ ์คํจ
โ ์ฌ์ง์
๋ฐ๋ณต
ํนํ ์ฌ๋ฌ ๋ชฌ์คํฐ๊ฐ ๋์์ ์ด๋ํ๋ ์ํฉ์์๋ ์ด๋ฌํ ํ์์ด ๋์ฑ ์์ฃผ ๋ฐ์ํ๋ค.
StateTreeTask_EnemyAttack์ ์ํํธ ์ง์ ๊ตฌ๊ฐ์ ์ถ๊ฐํ๋ค.
์ ๊ท ๋ณ์:
AttackStartRangeTolerance
AttackStartDelay
๋์ ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ๋ค.
Distance > AttackRange + Tolerance
โ ๊ณต๊ฒฉ ๋ถ๊ฐ
AttackRange < Distance โค AttackRange + Tolerance
โ ์ํํธ ์ง์
๊ตฌ๊ฐ
AttackRange ์ดํ
โ ์ฆ์ ๊ณต๊ฒฉ ๊ฐ๋ฅ
์ํํธ ์ง์ ๊ตฌ๊ฐ์์๋ ์ผ์ ์๊ฐ ๋์ ๋ฒ์๋ฅผ ์ ์งํด์ผ๋ง ๊ณต๊ฒฉ์ด ์์๋๋ค.
๊ณต๊ฒฉ ๊ฐ๋ฅ ๊ฑฐ๋ฆฌ ์ง์
โ AttackStartDelay ์ฒด๋ฅ
โ ๊ณต๊ฒฉ ์์
๋ํ EnterState์ ExitState์์ ์ฒด๋ฅ ์๊ฐ์ ์ด๊ธฐํํ๋๋ก ์์ ํด ์ํ ์ ํ ์ ์ด์ ๊ฐ์ด ๋จ์ง ์๋๋ก ์ ๋ฆฌํ๋ค.
์ด๋ฅผ ํตํด ๊ณต๊ฒฉ ๋ฒ์ ๊ฒฝ๊ณ์์ ๋ฐ์ํ๋ ์คํจ-์ฌ์๋ ๋ฃจํ๋ฅผ ์ค์ด๊ณ ๋ณด๋ค ์์ ์ ์ผ๋ก ๊ณต๊ฒฉ์ด ์์๋ ์ ์๋๋ก ๊ฐ์ ํ๋ค.
์ผ๋ฐ ๋ชฌ์คํฐ์ ๋ณด์ค๊ฐ ๋ชจ๋ ๊ธฐ๋ณธ Pawn ์ฑ๋์ ์ฌ์ฉํ๊ณ ์์๋ค.
ECC_Pawn
์ด ๊ตฌ์กฐ์์๋ ๋ชฌ์คํฐ๋ผ๋ฆฌ ์๋ก Block ์ถฉ๋์ด ๋ฐ์ํ๊ณ , ๋ฝ์จ ํ์ง๋ ์ ํฌ ํ์ ์์๋ ํ๋ ์ด์ด์ ๋ชฌ์คํฐ๋ฅผ ๋ช ํํ๊ฒ ๊ตฌ๋ถํ๊ธฐ ์ด๋ ค์ ๋ค.
์ผ๋ฐ ๋ชฌ์คํฐ ์ ์ฉ ์ฑ๋์ธ EnemyPawn์ ์ ์ฉํ๋ค.
ECC_GameTraceChannel1
์ผ๋ฐ ๋ชฌ์คํฐ๋ ์์ฑ ์ EnemyPawn ์ฑ๋์ ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝํ๋ค.
GetCapsuleComponent()->SetCollisionObjectType(ECC_GameTraceChannel1);
GetCapsuleComponent()->SetCollisionResponseToChannel(
ECC_GameTraceChannel1,
ECR_Overlap);
๋ํ LockOnComponent์ SphereTrace์์๋ ํด๋น ์ฑ๋์ ํ์งํ๋๋ก ์์ ํ๋ค.
SearchObjectTypes.AddUnique(
UEngineTypes::ConvertToObjectType(
ECC_GameTraceChannel1));
์ด๋ฅผ ํตํด ์ผ๋ฐ ๋ชฌ์คํฐ๋ผ๋ฆฌ๋ Overlap์ผ๋ก ์ฒ๋ฆฌํ๋ฉด์ ํ๋ ์ด์ด ๋์ ์ ํฌ ํ์ ์ ์ ์งํ ์ ์๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋ จํ๋ค.
๋ฃจ๋ฉ NPC๊ฐ ๋ชฌ์คํฐ ๊ณต๊ฒฉ ๋์์ด๋ ๋ฝ์จ ๋์์ผ๋ก ์๋ชป ์ธ์๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์์ธ์ ํฌ๊ฒ ์ธ ๊ฐ์ง์๋ค.
1. Pawn ์ฑ๋ ์ถฉ๋ ์ ์ง
2. LockOn ASC ํํฐ ๋ถ์ฌ
3. NoTeam ํ์ ์ฒ๋ฆฌ ๋ฏธํก
๊ทธ ๊ฒฐ๊ณผ ๋ฃจ๋ฉ์ด ์ค์ ์ ํฌ ๋์์ด ์๋์๋ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์ด ๋ฐ์ํ๋ค.
๋ชฌ์คํฐ ํํธ๋ฐ์ค ์ค๋ฒ๋ฉ
โ ๋ฃจ๋ฉ ๊ฐ์ง
๋ฝ์จ SphereTrace
โ ๋ฃจ๋ฉ ํฌํจ
AI ํ ํ์
โ Hostile ์ฒ๋ฆฌ
๋จผ์ ๋ฃจ๋ฉ์ Pawn ์ฑ๋ ์ถฉ๋์ ๋ฌด์ํ๋๋ก ์์ ํ๋ค.
Capsule->SetCollisionResponseToChannel(
ECC_Pawn,
ECR_Ignore);
๋ํ LockOnComponent์์๋ ASC๊ฐ ์๋ ์กํฐ๋ฅผ ์ ์ธํ๋๋ก ํํฐ๋ฅผ ์ถ๊ฐํ๋ค.
if (!ASCIface ||
!ASCIface->GetAbilitySystemComponent())
{
continue;
}
๋ง์ง๋ง์ผ๋ก ํ ํ์ ๋ก์ง์์ NoTeam์ Neutral๋ก ์ฒ๋ฆฌํ๋๋ก ์์ ํ๋ค.
if (OtherTeamId == FGenericTeamId::NoTeam)
{
return ETeamAttitude::Neutral;
}
์ด๋ฅผ ํตํด ๋น์ ํฌ NPC๊ฐ ์ ํฌ ์์คํ ์ ํฌํจ๋์ง ์๋๋ก ์ ๋ฆฌํ๋ค.
์ผ๋ฐ ๋ชฌ์คํฐ ๋จธ๋ฆฌ ์์ ํ์๋๋ ์ฒด๋ ฅ๋ฐ UI๋ฅผ ์๋ก ๊ตฌํํ๋ค.
์ ๊ท ์์ฑ ํ์ผ์ ๋ค์๊ณผ ๊ฐ๋ค.
NormalMonsterHealthBarComponent
RetrieveNormalMonsterHealthBarWidget
RetrieveElementUILibrary
RetrieveEnemyCharacter ์์ฑ์์์ WidgetComponent๋ฅผ ์์ฑํ๊ณ ์บ๋ฆญํฐ์ ๋ถ์ฐฉํ๋ค.
NormalHealthBarComponent =
CreateDefaultSubobject<
UNormalMonsterHealthBarComponent>(...);
NormalHealthBarComponent
->SetupAttachment(GetRootComponent());
NormalHealthBarComponent
->SetRelativeLocation(
FVector(0.f, 0.f, 120.f));
์๋ ๊ณต๊ฐ UI๋ WidgetComponent๋ฅผ ์ง์ ๋ถ์ฐฉํ๋ ๋ฐฉ์์ด ๊ฐ์ฅ ๋จ์ํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฝ๋ค.
๋ํ Component๋ ๋ฐ์ดํฐ ๊ฐฑ์ ๊ณผ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ด๋นํ๊ณ , Widget์ ํํ๋ง ๋ด๋นํ๋๋ก ์ญํ ์ ๋ถ๋ฆฌํด๋๋ฉด ์ดํ ์์ ์ํ, ๋ฒํ ํ์, ๋๋ฒํ ์์ด์ฝ ๋ฑ์ ๊ธฐ๋ฅ์ ํ์ฅํ๊ธฐ๋ ์์ํ๋ค.