[UE5 / C++] ๐Ÿ‘ฟ ์  ๋งŒ๋“ค๊ธฐ (Feat. FSM) (1)

Singery00ยท2024๋…„ 4์›” 28์ผ
0

UE5 C++

๋ชฉ๋ก ๋ณด๊ธฐ
11/20
post-thumbnail

๊ฐœ์š”

๐Ÿ’ก C++๋กœ ์ ์„ ๋งŒ๋“ค์–ด๋ณด์ž. ๋˜ํ•œ ์œ ํ•œ์ƒํƒœ๊ธฐ๊ณ„๋ฅผ ํฌํ•จํ•˜์—ฌ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ๋‹ค.

์ด๋ฒˆ์—๋Š” C++๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ ์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
์ ์ด ํ”Œ๋ ˆ์ด์–ด๋ฅผ ์ซ“์•„ ์˜ค๊ณ ,
ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๊ณต๊ฒฉํ•˜๊ณ ,
์ฒด๋ ฅ์ด 0์ด ๋˜๋ฉด ์ฃฝ๋Š”,
์  AI์—๊ฒŒ ์ง€๋Šฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๋Š” ํ•˜๋‚˜์˜ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ชจ๋ธ์„ State ํŒจํ„ด์œผ๋กœ ์—ฌ๋Ÿฌ ์ œํ•œ๋œ ์ƒํƒœ๊ฐ€ ์กด์žฌํ•˜๊ณ , ํŠน์ • ์กฐ๊ฑด์— ๋ฌผ๋ ค ์„œ๋กœ ์—ฐ๊ฒฐ๋˜์–ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

State Machine Overview


๋ณธ๋ก 


๐Ÿ‘ฟ ์  c++ class์ƒ์„ฑ

์šฐ์„  ์ ์˜ ์‹ค์ฒด๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

C++ํด๋ž˜์Šค ์ƒ์„ฑํ•ด์ฃผ์‹œ๊ณ ,

Character ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”.

์ด๋ฆ„์€ Enemy๋กœ ์ง€์–ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

์™ธํ˜• ํ• ๋‹น

์ž„์‹œ๋กœ SKM_Manny๋ฅผ ์ž…ํ˜€ ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.
๋ ˆํผ๋Ÿฐ์Šค ๋ณต์‚ฌํ•ด์ฃผ์‹œ๊ณ ,

Enemy์˜ ์ƒ์„ฑ์ž์—์„œ ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ ์šฉํ•ด์ฃผ์„ธ์š”.

AEnemy::AEnemy()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	ConstructorHelpers::FObjectFinder<USkeletalMesh>mannyMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Manny.SKM_Manny'"));

	if (mannyMesh.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(mannyMesh.Object);
		GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));
	}
}

โ—์ปดํŒŒ์ผ ์ €์žฅโ—ํ›„ ์–ธ๋ฆฌ์–ผ ์—๋””ํ„ฐ๋กœ ๋Œ์•„์™€ Enemy c++ Class๋ฅผ ์šฐํด๋ฆญํ•ด์„œ BP๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

Blueprint ํด๋”์— BP_Enemy๋ผ๊ณ  ์ง€์–ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.


Actor Component

์ด์ œ ์ ์—๊ฒŒ ๋“ค์–ด๊ฐˆ ์ง€๋Šฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

Blueprint์—์„  Behavior Tree๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜์˜€๋Š”๋ฐ,
์ด๋ฒˆ์—๋Š” FSM์„ ์ ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ ์ „์— Actor Component๋Š” ์›”๋“œ์˜ ์•กํ„ฐ์— ์ฒจ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ์ž…๋‹ˆ๋‹ค. Camera, Spring Arm์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ€์ฐฉํ•ด์„œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๊ฒƒ์ด์ฃ .

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋”ฐ๋กœ ํฌ์ŠคํŒ…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Unreal Actor Component Docs


FSM Component

๊ทธ๋ž˜์„œ FSM์€ Actor Component๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

Class ์ƒ์„ฑ

ํด๋ž˜์Šค๋ฅผ All Classes๋กœ ํ•˜์—ฌ Actor Component๋กœ ๊ฒ€์ƒ‰ํ•ด์ฃผ์„ธ์š”.

EnemyFSM์ด๋ผ๊ณ  ์ด๋ฆ„ ์ง€์–ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ ์ž‘์„ฑ

์ด์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

State Enum

์šฐ์„  ๊ฐ ์ƒํƒœ์— ๋Œ€ํ•œ ์žก์„ Enum Class๋กœ ๋งŒ๋“ค์–ด ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.
IDLE, MOVE, ATTACK, DAMAGE, DIE 5๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "EnemyFSM.generated.h"

UENUM(BlueprintType)
enum class EEnemyState : uint8
{
	IDLE,
	MOVE,
	ATTAK,
	DAMAGE,
	DIE,
};

๋ณ€์ˆ˜, ํ•จ์ˆ˜ ์„ ์–ธ

ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” mState๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ  IDLE๋กœ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ์„ธ์š”.

๋˜ํ•œ ๊ฐ ์ƒํƒœ์ผ ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•ด์ฃผ์„ธ์š”.

public:
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category=FSM)
	EEnemyState mState = EEnemyState::IDLE;

	void IdleState();
	void MoveState();
	void AttackState();
	void DamageState();
	void DieState();

์ƒํƒœ๋ณ„ ํ–‰๋™ ์‹คํ–‰

Tick()ํ•จ์ˆ˜์—์„œ Switch๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ mState ๊ฐ’์— ๋”ฐ๋ผ ํŠน์ • ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

// Called every frame
void UEnemyFSM::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...

	switch (mState)
	{
	case EEnemyState::IDLE:
		IdleState();
		break;
	case EEnemyState::MOVE:
		MoveState();
		break;
	case EEnemyState::ATTAK:
		AttackState();
		break;
	case EEnemyState::DAMAGE:
		DamageState();
		break;
	case EEnemyState::DIE:
		DieState();
		break;
	default:
		break;
	}
}

FSM ์ปดํฌ๋„ŒํŠธ ํ• ๋‹น

FSM์€ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.
Enemy Class๋กœ ์ด๋™ํ•ด Camera์ฒ˜๋Ÿผ ๋ถ€์ฐฉํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Enemy.h

์ปดํฌ๋„ŒํŠธ์ธ UEnemyFSM์„ ์„ ์–ธํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

public:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FSMComponent)
	class UEnemyFSM* fsm;

Enemy.cpp

๊ทธ๋ฆฌ๊ณ  CreateDefaultSubobject๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ• ๋‹นํ•ด์ฃผ์„ธ์š”.

#include "EnemyFSM.h"

// Sets default values
AEnemy::AEnemy()
{
	// ์ƒ๋žต

	fsm = CreateDefaultSubobject<UEnemyFSM>(TEXT("FSM"));
}

โ—์ปดํŒŒ์ผ ์ €์žฅโ—ํ•˜๊ณ  ์—๋””ํ„ฐ์—์„œ BP_Enemy๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”.

๊ทธ๋Ÿผ Components์— Fsm์ด ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


IdleState

๋‹ค์‹œ EnemyFSM Class๋กœ ๊ฐ€์„œ ๊ฐ ์ƒํƒœ ๋ณ„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

Idle ์ƒํƒœ๋ถ€ํ„ฐ ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ƒํƒœ ์ „ํ™˜ ์‹œ๊ฐ„๋ณ€์ˆ˜ ์„ ์–ธ

์ผ๋‹จ IdleDelayTime๊ณผ currentTime์„ ๊ฐ๊ฐ ์ดˆ๊ธฐํ™”ํ•ด์„œ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”.

public:
	UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category=FSM)
	EEnemyState mState = EEnemyState::IDLE;

	// ์ถ”๊ฐ€
	UPROPERTY(EditDefaultsOnly,Category=FSM)
	float idleDelayTime = 2;

	float currentTime = 0;

currentTime์œผ๋กœ ์‹œ๊ฐ„๊ฒฝ๊ณผ ์ฒดํฌ

๊ทธ๋ฆฌ๊ณ  Tick์—์„œ currentTime์— DeltaTime์„ ๋ˆ„์ ํ•ฉํ•˜์—ฌ ํ˜„์žฌ ์‹œ๊ฐ„์˜ ๊ฒฝ๊ณผ๋ฅผ ์ฒดํฌํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ์ถ”๊ฐ€
	currentTime += DeltaTime;
    
    // ์ƒ๋žต
}

IdleState() ์ž‘์„ฑ

๋˜ํ•œ currentTime์ด ๊ฐ ์ƒํƒœ ๋ณ„ ์‹œ๊ฐ„๋ณด๋‹ค ์ปค์ง„๋‹ค๋ฉด ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ์ „ํ™˜ํ•˜๊ณ , 0์œผ๋กœ ๋‹ค์‹œ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ๋˜๊ฒ ์ฃ ?

์—ฌ๊ธฐ์„œ๋Š” Move์ƒํƒœ๋กœ ์ „ํ™˜ํ•˜๊ณ  0์œผ๋กœ ์ดˆ๊ธฐํ™” ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::IdleState()
{
	if (currentTime > idleDelayTime)
	{
		mState = EEnemyState::MOVE;
		currentTime = 0;
	}
}

MoveState

Move์ƒํƒœ๋Š” ํ”Œ๋ ˆ์ด์–ด๋ฅผ ํ–ฅํ•ด ๋‹ค๊ฐ€์˜ค๋„๋ก ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

EnemyFSM Class๋กœ ์ด๋™ํ•ด์ฃผ์„ธ์š”.

๋ณ€์ˆ˜ ์„ ์–ธ

๊ทธ๋ฆฌ๊ณ  ์†Œ์œ ๋Œ€์ƒ๊ณผ ํƒ€๊ฒŸ์„ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ ๊ฐ๊ฐ ์„ ์–ธํ•ด์ฃผ์„ธ์š”.

์ฐพ์•„๊ฐˆ AMyPlayerํด๋ž˜์Šค์˜ target๊ณผ,
์†Œ์œ ์ž์ธ AEnemyํด๋ž˜์Šค์˜ me๋ฅผ ์„ ์–ธํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ด๋™ ์ƒํƒœ์— ๋Œ€ํ•œ ์‹œ๊ฐ„๋ณ€์ˆ˜๋„ 1๋กœ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”.

public:
	UPROPERTY(VisibleAnywhere,Category=FSM)
	class AMyPlayer* target;

	UPROPERTY(VisibleAnywhere, Category = FSM)
	class AEnemy* me;
    
    UPROPERTY(EditDefaultsOnly, Category = FSM)
	float moveDelayTime = 1;

target, me ์ •์˜

๊ฐ ํด๋ž˜์Šค์˜ ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์‹œ๊ณ , BeginPlay์—์„œ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

GetActorOfClass๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์›”๋“œ์˜ AMyPlayer๋ฅผ ์ฐพ์•„ target์„ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

me๋Š” GetOwner()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

#include "MyPlayer.h"
#include "Enemy.h"
#include <Kismet/GameplayStatics.h>

// Called when the game starts
void UEnemyFSM::BeginPlay()
{
	Super::BeginPlay();

	// ์ถ”๊ฐ€
	auto actor = UGameplayStatics::GetActorOfClass(GetWorld(), AMyPlayer::StaticClass());
	target = Cast<AMyPlayer>(actor);

	me = Cast<AEnemy>(GetOwner());
}

MoveState ์ •์˜

target๊ณผ me์— ๋Œ€ํ•ด์„œ ์ดˆ๊ธฐํ™”๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  target์—์„œ me์˜ ์œ„์น˜๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ ๊ฑฐ๋ฆฌ์™€, ๋ฐฉํ–ฅ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
GetActorLocation()์„ ์‚ฌ์šฉํ•ด์„œ Vector๊ฐ’์„ ์–ป๊ฒ ์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::MoveState()
{
	if (target != nullptr && me != nullptr)
	{
		FVector Direction = target->GetActorLocation() - me->GetActorLocation();
}

FVector Direction์— ๊ฑฐ๋ฆฌ, ๋ฐฉํ–ฅ์„ ์•Œ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

Direction.GetSafeNormal()์„ ํ†ตํ•ด ์ •๊ทœํ™”๋ฅผ ํ•˜์—ฌ ๊ฐ’์„ 1๋กœ ๋งŒ๋“ค์–ด ์ฃผ์–ด ๋ฐฉํ–ฅ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฐ’์„ AddMovement Input์— ๋„ฃ์–ด ํ•ด๋‹น ๋ฐฉํ–ฅ์œผ๋กœ ์ด๋™ํ•˜๋„๋ก ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ƒฅ Normalize()๋ฅผ ์‚ฌ์šฉํ•˜์…”๋„ ๋ฌด๋ฐฉํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::MoveState()
{
	if (target != nullptr && me != nullptr)
	{
		FVector Direction = target->GetActorLocation() - me->GetActorLocation();

		// ์ถ”๊ฐ€
		me->AddMovementInput(Direction.GetSafeNormal());

	}


}

๊ทธ๋ฆฌ๊ณ  moveDelayTime๋ณด๋‹ค CurrentTime์ด ์ปค์ง„๋‹ค๋ฉด ๋‹ค์‹œ IDLE์ƒํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::MoveState()
{
	// ์ƒ๋žต
	
    // ์ถ”๊ฐ€
	if (currentTime > moveDelayTime)
	{
		mState = EEnemyState::IDLE;
		currentTime = 0;
	}
}

โ—์ปดํŒŒ์ผ ์ €์žฅโ—ํ›„ ์‹คํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด์ œ 2์ดˆ ๋Œ€๊ธฐ ํ›„, 1์ดˆ ์›€์ง์ด๋Š” ์ ์ด ์™„์„ฑ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


์ž„์‹œ AttackState

๋ณ€์ˆ˜ ์ƒ์„ฑ

์ด๋™ ์ƒํƒœ์— ๋Œ€ํ•œ ์‹œ๊ฐ„๋ณ€์ˆ˜์™€, ๊ณต๊ฒฉ ์‚ฌ๊ฑฐ๋ฆฌ์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”.

	UPROPERTY(EditAnywhere,BlueprintReadOnly,Category=FSM)
	float attackRange = 150.0f;

	UPROPERTY(EditDefaultsOnly, Category = FSM)
	float attackDelayTime = 2;

MoveState ์ˆ˜์ •

attackRange๋ณด๋‹ค ๊ฑฐ๋ฆฌ๊ฐ€ ๊ฐ€๊นŒ์šฐ๋ฉด mState๋ฅผ ATTACK์œผ๋กœ,
์•„๋‹ˆ๋ผ๋ฉด IDLE์ƒํƒœ ๊ทธ๋Œ€๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ณต๊ฒฉ ์‚ฌ๊ฑฐ๋ฆฌ ๋‚ด๋ผ๋ฉด ATTACK ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ currentTime์„ 0์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

void UEnemyFSM::MoveState()
{
	FVector Direction = target->GetActorLocation() - me->GetActorLocation();

	if (Direction.Size() <= attackRange) 
	{
		mState = EEnemyState::ATTACK;
	}
	else 
	{
		if (target != nullptr && me != nullptr)
		{
			me->AddMovementInput(Direction.GetSafeNormal());
		}

		if (currentTime > moveDelayTime)
		{
			mState = EEnemyState::IDLE;
			currentTime = 0;
		}
	}
}

์ž„์‹œ AttackState()

AttackState()๋„ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.
์‚ฌ๊ฑฐ๋ฆฌ ์ด๋‚ด์ธ์ง€ ๋จผ์ € ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
์‚ฌ๊ฑฐ๋ฆฌ๋ณด๋‹ค ๊ธธ๋‹ค๋ฉด ๋‹ค์‹œ MOVE ์ƒํƒœ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ณต๊ฒฉ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ํŒ๋‹จ ํ›„ ๊ณต๊ฒฉํ•ฉ๋‹ˆ๋‹ค.

void UEnemyFSM::AttackState()
{
	FVector Direction = target->GetActorLocation() - me->GetActorLocation();

	if (Direction.Size() <= attackRange)
	{
		if (currentTime > attackDelayTime)
		{
			UE_LOG(LogTemp, Warning, TEXT("Attack State!!"));
			currentTime = 0;
		}
	}
	else
	{
		mState = EEnemyState::MOVE;
		currentTime = 0;
	}

}

โ—์ปดํŒŒ์ผ ์ €์žฅโ— ํ›„ ์‹คํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
์ €๋Š” ํ™•์‹คํ•˜๊ฒŒ ๋ณด์—ฌ ๋“œ๋ฆฌ๊ธฐ ์œ„ํ•ด attackRange์„ 350๊นŒ์ง€ ๋Š˜๋ ธ์Šต๋‹ˆ๋‹ค.

moveDelayTime๊ณผ attackDelayTime์˜ 1์ดˆ, 2์ดˆ๋กœ ๊ฐญ์ด ์žˆ์–ด์„œ ์‚ฌ๊ฑฐ๋ฆฌ ๋‚ด๋กœ ๋“ค์–ด์™€๋„ ๋ฐ”๋กœ ๊ณต๊ฒฉ์€ ํ•˜์ง€ ์•Š๋Š” ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.

์ด๋Š” ์ ๋‹นํžˆ ์ˆ˜์ •ํ•ด์ฃผ์„ธ์š”.


๋งˆ๋ฌด๋ฆฌ

์ €๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•ด์„œ ์ ์ด ์›€์ง์ด์ง€๋งŒ ์—ฌ๋Ÿฌ๋ถ„์€ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋‚˜์˜ค์ง€ ์•Š๊ณ  T์ž๋กœ ๋‹ค๊ฐ€์˜ฌ ๊ฒ๋‹ˆ๋‹ค.

์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ๊ณผ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์— ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

profile
๊ฒŒ์ž„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด๋ณด์ž

0๊ฐœ์˜ ๋Œ“๊ธ€