TPSPlayer는 WASD키 입력시 애니메이션이 제어된건 ThirdPersonTemplete안에 FSM(유한상태머신)이 구현되어 있기 때문입니다. 이걸 구현해보겠습니다.
ActorComponent를 상속받아 EnemyFSM 이라는 C++클래스를 제작합니다. 컴포넌트로 제작하면 다른 컴포넌트에 부착하여 사용이 가능합니다.
UEnemyFSM 클래스가 만들어 집니다.
EnemyFSM.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "EnemyFSM.generated.h"
// 사용할 상태 정의
UENUM(BlueprintType)
enum class EEnemyState : uint8
{
Idle, Move, Attack, Damage, Die,
};
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class TPSPROJECT_API UEnemyFSM : public UActorComponent
{
GENERATED_BODY()
public:
UEnemyFSM();
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public:
// 상태변수
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FSM)
EEnemyState mState = EEnemyState::Idle;
// 대기상태
void IdleState();
// 이동상태
void MoveState(float DeltaTime);
// 공격상태
void AttackState();
// 피격상태
void DamageState();
// 죽음상태
void DieState();
// 대기시간
UPROPERTY(EditDefaultsOnly, Category = FSM)
float idleDelayTime = 2;
// 경과시간
float currentTime = 0;
// 타겟
UPROPERTY(VisibleAnywhere, Category = FSM)
class ATPSPlayer* target;
// 소유액터
UPROPERTY()
class AEnemy* me;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spawn)
FString id;
//이동속도
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spawn)
float moveSpeed = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spawn)
float minSpeed = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Spawn)
float maxSpeed = 10.f;
UFUNCTION()
void SetMoveSpeed(float value);
// 공격범위
UPROPERTY(EditAnywhere, Category = FSM)
float attackRange = 100.0f;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FSM)
FVector destination;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = FSM)
FVector dir;
// 공격대기시간
UPROPERTY(EditAnywhere, Category = FSM)
float attackDelayTime = 2.0f;
// 피격 알림 이벤트 함수
void OnDamageProcess();
// 체력
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = FSM)
int32 maxHp = 10;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = FSM)
int32 hp = maxHp;
// 피격대기시간
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = FSM)
int32 maxMp = 100;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = FSM)
int32 mp = maxHp;
// 피격대기시간
UPROPERTY(EditAnywhere, Category = FSM)
float damageDelayTime = 2.0f;
UPROPERTY(EditAnywhere, Category = FSM)
float mpDelayTime = 2.0f;
// 아래로 사라지는 속도
UPROPERTY(EditAnywhere, Category = FSM)
float dieSpeed = 50.0f;
// 사용중인 애니메이션 블루프린트
UPROPERTY()
class UEnemyAnim* anim;
UPROPERTY(EditAnywhere, Category = Animation)
class UAnimMontage* damageAnim;
// Enemy 를 소유하고 있는 AIController
//UPROPERTY()
//class AAIController* ai;
// 길찾기 수행시 랜덤위치
FVector randomPos;
// 랜덤위치 가져오기
//bool GetRandomPositionInNavMesh(FVector centerLocation, float radius, FVector& dest);
};
EnemyFSM.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Enemy.h"
#include "EnemyFSM.h"
#include "TPSPlayer.h"
#include <Components/CapsuleComponent.h>
#include <Components/SphereComponent.h>
#include <Components/WidgetComponent.h>
#include "Components/widget.h"
#include <Kismet/GameplayStatics.h>
#include <Kismet/KismetMathLibrary.h>
#include "HPBar.h"
// Sets default values
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;
// 1. 스켈레탈메쉬 데이터 로드
//
ConstructorHelpers::FObjectFinder<USkeletalMesh> tempMesh(TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn.SKM_Quinn"));
// 1-1. 데이터 로드 성공하면
if (tempMesh.Succeeded())
{
//UE_LOG(LogTemp, Warning, TEXT("Enemy Skeletal Mesh Loading"));
// 1-2. 데이터 할당
GetMesh()->SetSkeletalMesh(tempMesh.Object);
// 1-3. 메쉬 위치 및 회전 설정
GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -88), FRotator(0, -90, 0));
// 1-4. 메쉬 크기 수정
GetMesh()->SetRelativeScale3D(FVector(0.84f));
GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
sphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Collosion"));
sphereComp->SetRelativeScale3D(FVector(3, 3, 3));
sphereComp->SetCollisionProfileName(TEXT("EnemyHand"));
GetCapsuleComponent()->SetCollisionProfileName(TEXT("Enemy"));
GetMesh()->SetCollisionProfileName(TEXT("NoCollision"));
// EnemyFSM 컴포넌트 추가
fsm = CreateDefaultSubobject<UEnemyFSM>(TEXT("FSM"));
if (fsm) {
UE_LOG(LogTemp, Warning, TEXT("fsm"));
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf( TEXT("FSM: ") );// , fsm->GetFName()));
//GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("FSM:") );
// 애니메이션 블루프린트 할당하기
}
ConstructorHelpers::FClassFinder<UAnimInstance> tempClass(TEXT("/Game/Anim/ABP_Enemy.ABP_Enemy_C"));
if (tempClass.Succeeded())
{
//UE_LOG(LogTemp, Warning, TEXT("ABP_Enemy Loading"));
//GetMesh()->SetAnim
GetMesh()->SetAnimInstanceClass(tempClass.Class);
}
widgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPWidget"));
widgetComp->SetupAttachment(GetMesh());
widgetComp->SetRelativeLocation(FVector(0, 0, 300.0f));
UClass* WidgetCompClass = LoadClass<UUserWidget>(NULL, TEXT("/Game/UI/WBP_HPEnemy.WBP_HPEnemy_C"));
if (WidgetCompClass) {
widgetComp->SetWidgetClass(WidgetCompClass);
widgetComp->SetWidgetSpace(EWidgetSpace::Screen);
}
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
}
// Called when the game starts or when spawned
void AEnemy::BeginPlay()
{
Super::BeginPlay();
auto actor = UGameplayStatics::GetActorOfClass(GetWorld(), ATPSPlayer::StaticClass());
// ATPSPlayer 타입으로 캐스팅
target = Cast<ATPSPlayer>(actor);
// 소유객체 가져오기
//me = Cast<ANPC>(GetOwner());
me = this;
widgetComp->InitWidget();
hpBar = Cast<UHPBar>(widgetComp->GetUserWidgetObject());
if (IsValid(hpBar)) {
//FText idText = FText::FromString(FString::FromInt(12));
FVector _pos = target->GetActorLocation();
hpBar->ctextId = GetClass()->GetDisplayNameText();
hpBar->cInfo = FText::FromString(FString::Printf(TEXT("IDLE")));
//hpBar->ctextPos = FText::FromString(FString::Printf(TEXT("%f %f %f"), _pos.X, _pos.Y, _pos.Z));
hpBar->cHP = 10;
hpBar->cMaxHP = 10;
hpBar->cMP = 100;
hpBar->cMaxMP = 100;
/*widget->SetTextID(GetClass()->GetDisplayNameText());
FVector _pos = target->GetActorLocation();
widget->SetTextPosition(FText::FromString(FString::Printf(TEXT("%f %f %f"), _pos.X, _pos.Y, _pos.Z)));
*/
}
}
// Called every frame
void AEnemy::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
'인생언리얼TPS' 카테고리의 다른 글
3.2 블렌드스페이스를 이용한 애니메이션 합성 (1) | 2023.12.23 |
---|---|
3.2 알파타입버전 제작하기 - 애니메이션 FSM (1) | 2023.12.23 |
3.1-8 적Enemy생성하기 (0) | 2023.12.15 |
3.1-7 스나이퍼모드 C++ (0) | 2023.12.15 |
스나이퍼 모드 구현하기 (0) | 2023.12.13 |