• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

ProjectDark - 11 - EnemyCharacterBase

06 Sep 2021

Reading time ~2 minutes

만들 적의 기본적인 베이스다

AI는 유한 상태 모델로 만들것이다.

상태는 대기, 타겟이 있을때, 타겟에게 움직일때, 공격
크게 4개로 나눌 것이고 만약, 적 마다 다른 상태가 있을때 그 상태를 그 캐릭터 클래스에 구현해 줄 것이다.

그리고 FSM은 Enter, Tick, Exit로 구현해 각 상태에서 빠저나가는 것까지 구현할 것이다.

EnemyCharacterBase.h(EnumClass, Strcut)
USTRUCT(Atomic, BlueprintType)
struct FEnemyInfo
{
	GENERATED_USTRUCT_BODY()

public:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	float MaxHp;
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	float CurrentHp;
};

UENUM(BlueprintType)
enum class EEnemyState : uint8
{
	ES_Idle UMETA(DisplayName = "Idle"),
	ES_Targeting UMETA(DisplayName = "Targeting"),
	ES_MoveToTarget UMETA(DisplayName = "MoveToTarget"),
	ES_Attack UMETA(DisplayName = "Attack")
};

UENUM(BlueprintType)
enum class EFSMState : uint8 
{
	FSM_Enter UMETA(DisplayName = "Enter"),
	FSM_Tick UMETA(DisplayName = "Tick"),
	FSM_Exit UMETA(DisplayName = "Exit")
};
EnemyCharacterBase.h
UCLASS(Abstract)
class ENEMYCODES_API AEnemyCharacterBase : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AEnemyCharacterBase();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;


private:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "FSM", meta = (AllowPrivateAccess = "True"))
	EFSMState FSMState;
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "FSM", meta = (AllowPrivateAccess = "True"))
	EEnemyState CurrentState;

private:
	void FSM();
	void FSMEnter();
	void FSMTick();
	void FSMExit();
	void SetCurrentState(const EEnemyState& State);

#pragma region Idle
public:
	virtual void IdleEnter();
	virtual void Idle();
	virtual void IdleExit();
#pragma endregion

#pragma region Targeting
public:
	virtual void TargetingEnter();
	virtual void Targeting();
	virtual void TargetingExit();
#pragma endregion

#pragma region MoveToTarget
public:
	virtual void MoveToTargetEnter();
	virtual void MoveToTarget();
	virtual void MoveToTargetExit();
#pragma endregion

#pragma region Attack
public:
	virtual void AttackEnter();
	virtual void Attack();
	virtual void AttackExit();
#pragma endregion
};

원래 추상 클래스로 만들려고 했는데 안됀다 ㅠㅠ 아무리 찾아봐도 = 0 을 붙이면 안되는지 안나왔지만 암튼 안돼서 그냥 가상함수로 만들어 준다.

EnemyCharacterBase.cpp

#include "EnemyCharacterBase.h"

AEnemyCharacterBase::AEnemyCharacterBase()
{
	PrimaryActorTick.bCanEverTick = true;

	CurrentState = EEnemyState::ES_Idle;
	FSMState = EFSMState::FSM_Enter;
}

void AEnemyCharacterBase::BeginPlay()
{
	Super::BeginPlay();

}

void AEnemyCharacterBase::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	FSM();
}

void AEnemyCharacterBase::FSM()
{
	switch (FSMState)
	{
	case EFSMState::FSM_Enter:
		FSMEnter();
		break;

	case EFSMState::FSM_Tick:
		FSMTick();
		break;

	case EFSMState::FSM_Exit:
		FSMExit();
		break;
	}
}

void AEnemyCharacterBase::FSMEnter()
{
	switch (CurrentState)
	{
	case EEnemyState::ES_Idle:
		IdleEnter();
		break;
	case EEnemyState::ES_Targeting:
		TargetingEnter();
		break;
	case EEnemyState::ES_MoveToTarget:
		MoveToTargetEnter();
		break;
	case EEnemyState::ES_Attack:
		AttackEnter();
		break;
	}

	FSMState = EFSMState::FSM_Tick;
}

void AEnemyCharacterBase::FSMTick()
{
	switch (CurrentState)
	{
	case EEnemyState::ES_Idle:
		Idle();
		break;
	case EEnemyState::ES_Targeting:
		Targeting();
		break;
	case EEnemyState::ES_MoveToTarget:
		MoveToTarget();
		break;
	case EEnemyState::ES_Attack:
		Attack();
		break;
	}
}

void AEnemyCharacterBase::FSMExit()
{
	switch (CurrentState)
	{
	case EEnemyState::ES_Idle:
		IdleExit();
		break;
	case EEnemyState::ES_Targeting:
		TargetingExit();
		break;
	case EEnemyState::ES_MoveToTarget:
		MoveToTargetExit();
		break;
	case EEnemyState::ES_Attack:
		AttackExit();
		break;
	}
}

void AEnemyCharacterBase::SetCurrentState(const EEnemyState& State)
{
	if (CurrentState == State) { return; }

	FSMExit();
	CurrentState = State;
	FSMState = EFSMState::FSM_Enter;
}

FSM에서 처음 들어갈 때, FSM_Enter로 만들어 주고
반복문에서 Enter, Tick순으로 반복한다. 그리고 현재 State를 변경할 때,
현재 State의 Exit를 호출, 현재의 State를 변경해주고 FSM을 Enter로 만들어준다.



Unreal Share Tweet +1