๐ก C++๋ก ์ ์ ๋ง๋ค์ด๋ณด์. ๋ํ ์ ํ์ํ๊ธฐ๊ณ๋ฅผ ํฌํจํ์ฌ ๋ง๋ค์ด ๋ณด๊ฒ ๋ค.
์ด๋ฒ์๋ ์ ๋๋ฉ์ด์ ์ ์ฉ๊ณผ ์ถ๊ฐ ์ํ์ ๋ํด์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
์ธ๋ฆฌ์ผ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ABP_Manny์ ์ ๋๋ฉ์ด์ ์ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
๋ ํผ๋ฐ์ค ๋ณต์ฌํ์๊ณ ,
Enemy์ ์์ฑ์ ํจ์์์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์.
TEXT()์๋ ๊ฐ์์ ๋ ํผ๋ฐ์ค๊ฐ ๋ค์ด๊ฐ๊ฒ ์ฃ ?
AEnemy::AEnemy()
{
// ์๋ต
// ์ถ๊ฐ ConstructorHelpers::FObjectFinder<UAnimBlueprint>ABP_Manny(TEXT("/Script/Engine.AnimBlueprint'/Game/Characters/Mannequins/Animations/ABP_Manny.ABP_Manny'"));
if (ABP_Manny.Succeeded())
{
GetMesh()->SetAnimClass(ABP_Manny.Object->GeneratedClass);
}
}
โ์ปดํ์ผ ์ ์ฅโํ ์ธ๋ฆฌ์ผ ์๋ํฐ๋ก ๋์์ ์คํํด์ ํ์ธํด์ฃผ์ธ์.
ํน์ ์ ๋๋ฉ์ด์ ์ด ์ ์ฉ๋์ง ์์๋ค๋ฉด BP_Enemy๋ฅผ ์ญ์ ํ๊ณ ๋ค์ ๋ง๋ค์ด ๋ณด์ธ์.
Enemy.h๋ก ์ด๋ํด์ AM_Slash๋ฅผ ์ถ๊ฐํด์ค์๋ค.
์คํํ ์ ๋๋ฉ์ด์ ๋ชฝํ์ฅฌ๋ฅผ ๋ฃ์ด์ค ๋ณ์์ ๋๋ค.
public:
UPROPERTY(EditAnywhere)
UAnimMontage* AM_Slash;
๋ก๊ทธ ์ถ๋ ฅ ๋์ ์ ๋๋ฉ์ด์
์ด ๋์ค๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
EnemyFSM.cpp๋ก ์ด๋ํ์๊ณ ์ฐ์ ํค๋๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์.
#include "Animation/AnimInstance.h"
๊ทธ๋ฆฌ๊ณ AttackState()์ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ ๋ถ๋ถ์ ์ ๋๋ฉ์ด์ ์ ์ถ๋ ฅํ๋๋ก ์ฝ๋๋ฅผ ์์ ํด์ฃผ๊ฒ ์ต๋๋ค.
me์ AnimInstance๋ฅผ ๋ฐ์ ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ Montage_Play๋ฅผ ํตํด ์ ๋๋ฉ์ด์ ์ ์คํํฉ๋๋ค.
void UEnemyFSM::AttackState()
{
FVector Direction = target->GetActorLocation() - me->GetActorLocation();
if (Direction.Size() <= attackRange)
{
if (currentTime > attackDelayTime)
{
UAnimInstance* AnimInstance = me->GetMesh()->GetAnimInstance();
if (AnimInstance != nullptr)
{
AnimInstance->Montage_Play(me->AM_Slash);
currentTime = 0;
}
}
}
else
{
mState = EEnemyState::MOVE;
currentTime = 0;
}
}
โ์ปดํ์ผ ์ ์ฅโํ ์ธ๋ฆฌ์ผ ์๋ํฐ๋ก ์ด๋ํฉ๋๋ค.
์ ๋ AM_Slash๋ผ๋ ๊ณต๊ฒฉ ๋ชจ์ ์ ๋ฏธ๋ฆฌ ๋ชฝํ์ฅฌ๋ก ๋ง๋ค์ด ๋์์ต๋๋ค.
BP_Enemy๋ฅผ ์ด์ด์ AM_Slash๋ฅผ ํ ๋นํด์ฃผ์ธ์.
โ์ปดํ์ผ ์ ์ฅโํ ์คํํด์ฃผ์๋ฉด ์์ฃผ ์ ๊ณต๊ฒฉํ๋๊ฑธ ํ์ธํ ์ ์์ต๋๋ค.
Damage, Die State์ ๊ตฌํ์ ์์ ์ฐ์ ์ ์๊ฒ ํผํด๋ฅผ ์ค ์ ์๋๋ก ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
MyPlayer.cpp๋ก ์ด๋ํด์ Fire()๋ฅผ ์์ ํด์ฃผ๊ฒ ์ต๋๋ค.
๋ฐ์ฌํ ๊ณต์ BeginOverlap์ด๋ ๋ค๋ฅธ ๊ธฐ๋ฅ์ผ๋ก ๋ฐ๋ฏธ์ง๋ฅผ ์ค ์ ์๊ฒ ์ง๋ง,
LineTrace๋ฅผ ์ถ๊ฐ๋ก ๋ฐ์ฌํ์ฌ ๋ฐ๋ฏธ์ง๋ฅผ ์ฃผ๊ฒ ์ต๋๋ค.
LineTrace๋ฅผ ๋ฐ์ฌํ ์์, ๋์ ์ง์ ํด์ฃผ๊ฒ ์ต๋๋ค.
void AMyPlayer::Fire()
{
FVector startPos = camera->GetComponentLocation();
FVector endPos = camera->GetComponentLocation() + camera->GetForwardVector()*10000;
}
๊ทธ๋ฆฌ๊ณ HitResult๋ฅผ ๋ฐ์ hitInfo์ LineTrace์ ํ๋ ์ด์ด๋ ๋ง์ผ๋ฉด ์๋๋ค params์ ์ฌ์ฉํด์ ์ ์ธํด์ฃผ๊ฒ ์ต๋๋ค.
void AMyPlayer::Fire()
{
// ์๋ต
// ์ถ๊ฐ
FHitResult hitInfo;
FCollisionQueryParams params;
params.AddIgnoredActor(this);
}
๊ทธ๋ฆฌ๊ณ ์์์ ์ค์ ํ ๊ฐ๋ค๋ก LineTrace๋ฅผ ์ด์ฃผ๊ฒ ์ต๋๋ค.
์ฑ๋๊ฐ์ ECC_Visibility๋ฅผ ์์๋ก ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
void AMyPlayer::Fire()
{
// ์๋ต
// ์ถ๊ฐ
bool bHit = GetWorld()->LineTraceSingleByChannel(hitInfo, startPos, endPos, ECC_Visibility, params);
if (bHit)
{
}
}
๋ํ ์ ์คํ ์ง์ ์ ์์น๋ฅผ ํ์ ํ๊ธฐ ์ํด ๊ฐ๋จํ ์ดํํธ๋ฅผ ์ํํ๋๋ก ํด์ฃผ๊ฒ ์ต๋๋ค.
void AMyPlayer::Fire()
{
// ์๋ต
if (bHit)
{
// ์ถ๊ฐ
FTransform bulletTrans;
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), effectFactory, hitInfo.ImpactPoint);
}
}
MyPlayer.h๋ก ๊ฐ์ effectFactory๋ฅผ ์ ์ธํด์ค์๋ค.
public:
UPROPERTY(EditAnywhere,Category=effectFactory)
class UParticleSystem* effectFactory;
โ์ปดํ์ผ ์ ์ฅโํด์ฃผ์๊ณ ์ธ๋ฆฌ์ผ ์๋ํฐ๋ก ์ด๋ํฉ๋๋ค.
BP_Player๋ฅผ ์ด์ด์ Effect Factory์ ์ดํํธ๋ฅผ ๋ฃ์ด ์ค๋๋ค.
๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ P_Explosion์ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
โ์ปดํ์ผ ์ ์ฅโํ ํ ํ๋ ์ดํด๋ณด๋ฉด LineTrace๊ฐ ๋ฐ์ฌ๋๊ณ , ์ถฉ๋์ ์ ์ดํํธ๊ฐ ๋์ต๋๋ค.
์ด์ LineTrace์ ์ ์คํ ๋์์ด ์ ์ด๋ผ๋ฉด ํผํด๋ฅผ ์ค์ผ๊ฒ ์ฃ ?
2๊ฐ์ง๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
GetDefaultSubobjectByName()์ผ๋ก ํน์ ์ด๋ฆ์ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด ์ธ์ํฉ๋๋ค.
์ฐ๋ฆฌ๋ ๋ชจ๋ ์ ์ด FSM์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
"FSM์ ๊ฐ์ง๊ณ ์๋๊ฐ?"๋ก ์ ์ธ์ง ์๋์ง ํ๋จํ ์ ์์ต๋๋ค.
ApplyDamage()๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ๋ฏธ์ง๋ฅผ ์ค ์ ์์ต๋๋ค.
๋์์ TakeDamage()๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ ํ Damage์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ๊ทธ ๊ฐ์ ๋ฐ์ ์ฌ ์ ์์ต๋๋ค.
hitInfo.GetActor()๋ก FSM์ด ์๋ค๋ฉด enemy๋ฅผ ํ ๋นํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ enemy๊ฐ ์ ์์ ์ผ๋ก ๋ฐ์์์ก๋ค๋ฉด, ApplyDamage()๋ฅผ ํตํด ์ ์ค ๋์์๊ฒ 1์ ๋ฐ๋ฏธ์ง๋ฅผ ์ฃผ์์ต๋๋ค.
void AMyPlayer::Fire()
{
//์๋ต
if (bHit)
{
FTransform bulletTrans;
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), effectFactory, hitInfo.ImpactPoint);
// ์ถ๊ฐ
auto enemy = hitInfo.GetActor()->GetDefaultSubobjectByName(TEXT("FSM"));
if (enemy)
{
UGameplayStatics::ApplyDamage(hitInfo.GetActor(), 1, NULL, NULL, NULL);
}
}
}
ECC_Visibility๋ก LineTrace์ ์ฑ๋์ ์ค์ ํ์์ผ๋ BP_Enemy์ Capsule Collision์ Visibility๋ฅผ Block์ผ๋ก ๋ณ๊ฒฝํด์ฃผ๊ฒ ์ต๋๋ค.
์ด์ ApplyDamage()๋ก ์ ์กํ ๋ฐ๋ฏธ์ง๋ฅผ TakeDamage๋ก ๋ฐ์์ผ ํฉ๋๋ค.
Enemy Class๋ก ์ด๋ํฉ๋๋ค.
์ฐ์ ์ฒด๋ ฅ๊ณผ TakeDamage๋ฅผ ์ ์ธํด์ฃผ์ธ์.
์ฒด๋ ฅ์ 3์ผ๋ก ์ฃผ์๊ณ ,
TakeDamage๋ override๋ก ๊ฐ์ ธ์์ผ ํฉ๋๋ค.
public:
int Hp = 3;
virtual float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
Super::TakeDamage()๋ก damage ๊ฐ์ ๋ฐ์ ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ Hp๋ฅผ ๊ฐ์ํ๊ณ , 0์ดํ๋ผ๋ฉด mState๋ฅผ DIE๋ก,
์ด์์ด๋ฉด DAMAGE๋ก ๋ง๋ค์ด ์ฃผ๊ฒ ์ต๋๋ค.
float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float damage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
Hp -= damage;
if (Hp<=0)
{
fsm->SetDieState();
}
else {
fsm->SetDamageState();
}
return damage;
}
SetDieState์ SetDamageState๋ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ์ฃผ์์ต๋๋ค.
void UEnemyFSM::SetDamageState()
{
mState = EEnemyState::DAMAGE;
}
void UEnemyFSM::SetDieState()
{
mState = EEnemyState::DIE;
}
ํผ๊ฒฉ ์ํ์ผ ๋ ๋ง๋ ๋ชจ์ ์ ๋ฃ์ด ์ฃผ๊ฒ ์ต๋๋ค.
AM_Hit๋ฅผ ์ถ๊ฐํด์ฃผ๊ณ Category๋ฅผ Monate๋ก ๋ฌถ์ด ์ฃผ์์ต๋๋ค.
public:
UPROPERTY(EditAnywhere, Category = Montage)
UAnimMontage* AM_Slash;
UPROPERTY(EditAnywhere, Category = Montage)
UAnimMontage* AM_Hit;
DamageState๋ ์๋์ ๊ฐ์ด ์์ฑํด์ฃผ์ธ์.
AttackState์ ๋๊ฐ์ต๋๋ค.
Play ๋์๋ง AM_Hit๋ก ๋ค๋ฅผ ๋ฟ์ด์ฃ .
๊ทธ๋ฆฌ๊ณ ํผ๊ฒฉ์ IDLE์ํ๋ก ๋ณ๊ฒฝํด์ฃผ๊ฒ ์ต๋๋ค.
void UEnemyFSM::DamageState()
{
UAnimInstance* AnimInstance = me->GetMesh()->GetAnimInstance();
if (AnimInstance != nullptr)
{
AnimInstance->Montage_Play(me->AM_Hit);
mState = EEnemyState::IDLE;
currentTime = 0;
}
}
โ์ปดํ์ผ ์ ์ฅโํ๊ณ ์ธ๋ฆฌ์ผ ์๋ํฐ๋ก ๊ฐ์ฃผ์ธ์.
๋ง๋ค์ด๋ AM_Hit๋ฅผ ๋ฃ์ด ์ฃผ๊ฒ ์ต๋๋ค.
โ์ปดํ์ผ ์ ์ฅโํ๊ณ ์คํํด์ฃผ์ธ์.
๊ทธ๋ผ ํผ๊ฒฉ ์ ์ ๋๋ฉ์ด์ ์ด ์ฌ์๋๊ณ ์ฒด๋ ฅ์ด ๊ฐ์๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์ฒด๋ ฅ์ด 0์ด ๋๋ฉด DieState๋ก ์ค๊ฒ ๋ฉ๋๋ค.
DieState๊ฐ ๋๋ฉด ๋ ์ด์ ๋ค๋ฅธ State๋ก ๋ณ๊ฒฝํด์ค ํ์๊ฐ ์์ต๋๋ค.
๋๊ทธ๋ ์ํ๋ก ๋ง๋ค๊ณ , Capsule Collision์ NoCollision์ผ๋ก ์ค์ ํด์ฃผ๊ฒ ์ต๋๋ค.
#include "Components/CapsuleComponent.h"
void UEnemyFSM::DieState()
{
mState = EEnemyState::DIE;
me->GetMesh()->SetSimulatePhysics (true);
me->GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
me->GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
โ์ปดํ์ผ ์ ์ฅโํ๊ณ ์คํํด์ฃผ์๊ณ ์ ์ 3๋ฒ ๊ณต๊ฒฉํ๋ฉด ์ฐ๋ฌ์ง๋ ๋ชจ์ต์ ํ์ธํ์ค ์ ์์ต๋๋ค.
์ฌ๊ธฐ๊น์ง ์ ์ ์ํ๋ณ ๊ธฐ๋ฅ ๊ตฌํ ๋ฐ ๊ณต๊ฒฉ ์ํธ์์ฉ๊น์ง ์๋ฃํ์์ต๋๋ค.
๋ค์์๋ Spawn Manager๋ฅผ ๋ง๋ค์ด ์ ์ด ์๋์ผ๋ก ์์ฑ๋๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
์ ๊ทธ๋ฆฌ๊ณ ์ ์ด ๊ณต๊ฒฉํ ๋ ํ๋ ์ด๊ฐ ํผ๊ฒฉ๋๋ ๊ฒ๋ ์ถํ ์งํํ๊ฒ ์ต๋๋ค.