본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 33 - Action RPG(Player & Animation Setting & Avoiding(State))

 

필요한 개념


<Player Setting>

Player 기본 구조가 밑의 구조다.

 

ChracterBP 기본 컴포넌트



그런데 Character에는 Mesh는 기본적으로 있으니까, 즉, SpringArm과 Camera 컴포넌트를 넣어줘야 함

Mesh의 회전값이 -90이므로 자식인 SpringArm도 -90도 회전되어있어서, SpringArm을 다시 90도 돌려서 0으로 만든다.

 

* Mesh를 회전시킴, 자식 컴포넌트인 SpringArm도 영향을 받아 다시 0에 맞게 회전시켜야 함



// 사방을 뛰어다닐 수 있게(카메라의 회전에 따라 회전하지 않음(Yaw))
bUseControllerRotationYaw = false;

// bDoCollisionTest : SpringArm 사이에 뭔가 충돌하면 카메라 회전 방지
SpringArm->bDoCollisionTest = false;

// RotationRate : OrientRotationToMovement 이용할때 회전 속도 올려줌
// 원래 기본값 360이어서 720으로 2배 빠르게 해줌
// bOrientRotationToMovement : 현재 캐릭터가 가속값을 가지고 있다면 
// 현재 가속되고있는값 방향으로 캐릭터 메쉬를 회전시켜줍니다.
GetCharacterMovement()->RotationRate = FRotator(0, 720, 0);
GetCharacterMovement()->bOrientRotationToMovement = true;




<Animation Setting>

<게임모드에서 플레이어 세팅>

CGameMode.cpp

CHelpers::GetClass(&DefaultPawnClass, "Blueprint'/Game/Player/BP_CPlayer.BP_CPlayer_C'");




* 프로젝트 세팅 -> 맵&모드 -> CGameMode 세팅 




<플레이어 키 입력 이동 처리>
* PlayerController에서 해줘도 되지만, 복잡해서 Player에서 해주는게 좋다.

void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveFoward);
	PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
	PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
	PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
}


void ACPlayer::OnMoveFoward(float InAxis)
{
	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetForwardVector();

	AddMovementInput(direction, InAxis);
}

void ACPlayer::OnMoveRight(float InAxis)
{
	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetRightVector();

	AddMovementInput(direction, InAxis);
}



* HorizontalLook, VerticalLook은 따로 처리
액터 컴포넌트로부터 상속받아 회전 속도를 조정하는 값들을 관리하는 컴포넌트를 하나 제작해서 처리함(옵션해서 관리함)
ActorComponent : 데이터를 관리하기 위해 사용
카메라 속도를 조정하기 위한 컴포넌트 -> COptionComponent class 생성
나중에 Timeline ZoomIn, ZoomOut 속도도 여기다 넣으면 된다.

* COptionComponent를 들여다 보면


// ClassGroup=(GameProject) -> 클래스 그룹을 지정가능
// meta=(BlueprintSpawnableComponent) -> 블루프린트에서 생성 가능하다는 문구
UCLASS(ClassGroup=(GameProject), meta=(BlueprintSpawnableComponent))
class UONLINE_04_ACTION_API UCOptionComponent : public UActorComponent
{
	...
}



* COptionComponent에서 값 가져와서 회전 값 세팅

 

void ACPlayer::OnHorizontalLook(float InAxis)
{
	float rate = Option->GetHorizontalLookRate();
	// deltaTime도 넣어줘야 정확한 값이 됨
	AddControllerYawInput(InAxis * rate * GetWorld()->GetDeltaSeconds());
}

void ACPlayer::OnVerticalLook(float InAxis)
{
	float rate = Option->GetVerticalLookRate();
	AddControllerPitchInput(InAxis * rate* GetWorld()->GetDeltaSeconds());
}





<캐릭터 애니메이션 처리>
AnimInstance를 상속받는 클래스를 생성한다.

* AnimInstance를 상속받는 클래스 생성



여기서 기본적으로 필요한건 캐릭터의 속도, 방향이다.

UPROPERTY(BlueprintReadOnly, EditAnywhere)
	float Speed;


BlueprintReadOnly : 블루프린트에서 읽을 수 있도록

 

* &nbsp; EditAnywhere시&nbsp;Debug Preview에서 나타남



NativeBeginPlay : 게임이 시작되고, 애니메이션이 최초의 콜이 될때 호출(BeginPlay)
NativeUpdateAnimation : 게임뿐만이 아니라 에디터 상황에서도 애니메이션이 플레이 될 때 호출(Tick)


Speed = character->GetVelocity().Size2D();
Direction = CalculateDirection(character->GetVelocity(), character->GetControlRotation());



얘를 기반으로 AnimBP를 만든다.


* CPlayer에서 AnimBP를 불러옴

TSubclassOf<UAnimInstance> animInstance;
CHelpers::GetClass<UAnimInstance>(&animInstance, "AnimBlueprint'/Game/Player/ABP_CPlayer.ABP_CPlayer_C'");
GetMesh()->SetAnimInstanceClass(animInstance);




<상태 관리 컴포넌트 생성(StatusComponent)>
상태관리 ex) HP라던가 캐릭터의 상태를 관리할 액터 컴포넌트 클래스(CStatusComponent)를 생성한다.
이동 속도나, HP등을 관리하게 됨

* CStatusComponent를 활용하여 상태 체크

// CPlayer.cpp

void ACPlayer::OnMoveFoward(float InAxis)
{
	// 이동 금지 상황일때 빠져나옴
	CheckFalse(Status->CanMove());

	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetForwardVector();

	AddMovementInput(direction, InAxis);
}

void ACPlayer::OnMoveRight(float InAxis)
{
	CheckFalse(Status->CanMove());

	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetRightVector();

	AddMovementInput(direction, InAxis);
}



*Tip)
대부분 컴포넌트가 있다면 값을 가져다 쓰는 방식을 많이씀

 

 

<Player Avoiding(StateComponent 생성)>

애니메이션 상태 관리
(애니메이션 상태에 따라 몽타주 플레이 가능)
Avoid 몽타주 동작 관리를 위해 사용

*Tip) 상태를 정의할 enum 자료형을 만듬
실무에서는 간단한 상태를 관리하기 위해서는
enum을 사용하기는 하지만 캐릭터의 상태처럼 복잡한 상태를 관리하는 것은
enum을 사용하지 않음(StatePattern을 사용)



// StateComponent.h


UENUM
- BlueprintType : 블루프린트와 통신할 것인지


// 언리얼에서는 기본적으로 ENUM은 uint8(unsigned int8)을 사용
UENUM(BlueprintType)
enum class EStateType : uint8
{
	// 애니메이션 상태 정의
	Idle, Roll, BackStep,
};

// 상태를 바뀌기 위해
// 이벤트로 해줘도 되지만, BP콜이 이게 편함
// 기존타입과 새로 바꾸려는 타입을 넣어줌
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FStateTypeChanged, EStateType, InPrevType, EStateType, InNewType);

public:
	// Get은 괜찮지만, Set은 이 방식은 관리가 짜증남(어디서 set되는지 알수가 없다.)
	//FORCEINLINE void SetType(EStateType InType) { InType = Type; }
	//FORCEINLINE EStateType GetType() {return Type;}
	// 그래서 함수를 하나하나 만들어줌(처음에는 귀찮지만, 관리는 편함)

	// 이렇게 하면 일일히 위처럼 리턴받아 비교하지 않아도
	// IsIdleMode()를 콜하면 받아서 바로 쓸 수 있다.
	// BlueprintPure : (const) getter의 성격에 해당하는 함수를 블루프린트 그래프에 노출시킬 때 사용하면 된다.
	// Get은 BlueprintPure하고 Set은 BlueprintCallable하는 게 좋다.
	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsIdleMode() { return Type == EStateType::Idle; }

	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsRollMode() { return Type == EStateType::Roll; }

	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsBackstepMode() { return Type == EStateType::Backstep; }

public:
	// 얘의 경우에도 따로 Type을 받지 않아도 Idle 상태로 안에서 Type을 바꿔주면 됨
	void SetIdleMode();
	void SetRollMode();
	void SetBackstepMode();

private:
	void ChangeType(EStateType InType);


public:
	// BlueprintAssignable : 블루프린트에서도 할당 가능하도록
	// 즉, 다이나믹 멀티캐스트 델리게이트에 UPROPERTY(BlueprintAssignable)을 지정해 주어야만 블루프린트에서 해당 델리게이트를 검색할 수 있게 된다.
	UPROPERTY(BlueprintAssignable)
		FStateTypeChanged OnStateTypeChanged;




// UCStateComponent.cpp

void UCStateComponent::SetIdleMode()
{
	// 디버깅 모드로 누구에 의해 콜되었는지 확인가능한 장점이 있음
	ChangeType(EStateType::Idle);
}

void UCStateComponent::SetRollMode()
{
	ChangeType(EStateType::Roll);
}

void UCStateComponent::SetBackstepMode()
{
	ChangeType(EStateType::Backstep);
}

void UCStateComponent::ChangeType(EStateType InType)
{
	// 기존 타입
	EStateType type = Type;
	// 바뀌려는 새타입
	Type = InType;

	if (OnStateTypeChanged.IsBound())
		OnStateTypeChanged.Broadcast(type, InType);
}

 

 

 

 

 

UOnline_04_Action.Build.CS


더보기
using UnrealBuildTool;

public class UOnline_04_Action : ModuleRules
{
	public UOnline_04_Action(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
		PrivateDependencyModuleNames.AddRange(new string[] { });

		// ModuleDirectiory 디렉터리는 Sources 폴더 밑 프로젝트 폴더
		// 이 폴더를 기준으로 헤더를 불러들일 수 있도록 세팅
		// ex) 그래야 폴더 내에 있는 파일들이 밖에 있는 Global.h를 경로를 생략하고 불러들일 수가 있음
		PublicIncludePaths.Add(ModuleDirectory);
	}
}

 

 

 

 

 

 

 

CPlayer.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CPlayer.generated.h"

UCLASS()
class UONLINE_04_ACTION_API ACPlayer : public ACharacter
{
	GENERATED_BODY()

private:
	UPROPERTY(VisibleDefaultsOnly)
		class USpringArmComponent* SpringArm;
	UPROPERTY(VisibleDefaultsOnly)
		class UCameraComponent* Camera;

private:
	UPROPERTY(VisibleDefaultsOnly)
		class UCOptionComponent* Option;

	UPROPERTY(VisibleDefaultsOnly)
		class UCStatusComponent* Status;

public:
	ACPlayer();

protected:
	virtual void BeginPlay() override;

public:	
	virtual void Tick(float DeltaTime) override;

	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
	void OnMoveFoward(float InAxis);
	void OnMoveRight(float InAxis);
	void OnHorizontalLook(float InAxis);
	void OnVerticalLook(float InAxis);
};

 

 

 

 

 

 

 

CPlayer.cpp


더보기
#include "CPlayer.h"
#include "Global.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/InputComponent.h"
#include "Animation/AnimInstance.h"
#include "Components/COptionComponent.h"
#include "Components/CStatusComponent.h"

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


	// 메시에 붙음
	CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetMesh());
	// SprintArm에 붙음
	CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);

	CHelpers::CreateActorComponent<UCOptionComponent>(this, &Option, "Option");
	CHelpers::CreateActorComponent<UCStatusComponent>(this, &Status, "Status");


	// 사방을 뛰어다닐 수 있게(카메라의 회전에 따라 회전하지 않음(Yaw))
	bUseControllerRotationYaw = false;

	GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
	GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));

	USkeletalMesh* mesh;
	CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
	GetMesh()->SetSkeletalMesh(mesh);

	SpringArm->SetRelativeLocation(FVector(9, 0, 140));
	// 원래 회전으로 돌려놈
	SpringArm->SetRelativeRotation(FRotator(0, 90, 0));
	SpringArm->TargetArmLength = 200.0f;
	// bDoCollisionTest : SpringArm 사이에 뭔가 충돌하면 카메라 회전 방지
	SpringArm->bDoCollisionTest = false;
	SpringArm->bUsePawnControlRotation = true;
	SpringArm->bEnableCameraLag = true;

	// GetCharacterMovement()->MaxWalkSpeed = 
	// RotationRate : OrientRotationToMovement 이용할때 회전 속도 올려줌
	// 원래 기본값 360이어서 720으로 2배 빠르게 해줌
	// bOrientRotationToMovement : 현재 캐릭터가 가속값을 가지고 있다면 
	// 현재 가속되고있는값 방향으로 캐릭터 메쉬를 회전시켜줍니다.
	GetCharacterMovement()->RotationRate = FRotator(0, 720, 0);
	GetCharacterMovement()->bOrientRotationToMovement = true;

	TSubclassOf<UAnimInstance> animInstance;
	CHelpers::GetClass<UAnimInstance>(&animInstance, "AnimBlueprint'/Game/Player/ABP_CPlayer.ABP_CPlayer_C'");
	GetMesh()->SetAnimInstanceClass(animInstance);

}

void ACPlayer::BeginPlay()
{
	Super::BeginPlay();
	
}

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

}

void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveFoward);
	PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
	PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
	PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
}

void ACPlayer::OnMoveFoward(float InAxis)
{
	// 이동 금지 상황일때 빠져나옴
	CheckFalse(Status->CanMove());

	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetForwardVector();

	AddMovementInput(direction, InAxis);
}

void ACPlayer::OnMoveRight(float InAxis)
{
	CheckFalse(Status->CanMove());

	FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
	FVector direction = FQuat(rotator).GetRightVector();

	AddMovementInput(direction, InAxis);
}

void ACPlayer::OnHorizontalLook(float InAxis)
{
	float rate = Option->GetHorizontalLookRate();
	// deltaTime도 넣어줘야 정확한 값이 됨
	AddControllerYawInput(InAxis * rate * GetWorld()->GetDeltaSeconds());
}

void ACPlayer::OnVerticalLook(float InAxis)
{
	float rate = Option->GetVerticalLookRate();
	AddControllerPitchInput(InAxis * rate* GetWorld()->GetDeltaSeconds());
}

 

 

CAnimInstance.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "CAnimInstance.generated.h"

UCLASS()
class UONLINE_04_ACTION_API UCAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	

protected:
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		float Speed;

	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		float Direction;

public:
	virtual void NativeBeginPlay() override;
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;
};

 

 

 

 

 

 

 

CAnimInstance.cpp


더보기
#include "CAnimInstance.h"
#include "Global.h"
#include "GameFramework/Character.h"

void UCAnimInstance::NativeBeginPlay()
{
	Super::NativeBeginPlay();
}

void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	ACharacter* character = Cast<ACharacter>(TryGetPawnOwner());
	CheckNull(character);

	Speed = character->GetVelocity().Size2D();
	Direction = CalculateDirection(character->GetVelocity(), character->GetControlRotation());
}

 

 

CGameMode.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "CGameMode.generated.h"

UCLASS()
class UONLINE_04_ACTION_API ACGameMode : public AGameModeBase
{
	GENERATED_BODY()

public:
	ACGameMode();
};

 

 

 

 

 

 

 

CGameMode.cpp


더보기
#include "CGameMode.h"
#include "Global.h"

ACGameMode::ACGameMode()
{
	CHelpers::GetClass<APawn>(&DefaultPawnClass, "Blueprint'/Game/Player/BP_CPlayer.BP_CPlayer_C'");
}

 

 

 

COptionComponent.h


더보기
#pragma once

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


UCLASS( ClassGroup=(GameProject), meta=(BlueprintSpawnableComponent) )
class UONLINE_04_ACTION_API UCOptionComponent : public UActorComponent
{
	GENERATED_BODY()

private:
	UPROPERTY(EditDefaultsOnly)
		float HorizontalLookRate = 45;

	UPROPERTY(EditDefaultsOnly)
		float VerticalLookRate = 45;

public:
	FORCEINLINE float GetHorizontalLookRate() { return HorizontalLookRate; }
	FORCEINLINE float GetVerticalLookRate() { return VerticalLookRate; }

public:	
	UCOptionComponent();

protected:
	virtual void BeginPlay() override;
};

 

 

 

 

 

 

 

COptionComponent.cpp


더보기
#include "COptionComponent.h"
#include "Global.h"

UCOptionComponent::UCOptionComponent()
{
}


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

}

 

 

 

CStatusComponent.h


더보기
#pragma once

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


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UONLINE_04_ACTION_API UCStatusComponent : public UActorComponent
{
	GENERATED_BODY()

private:
	// 캐릭터의 이동 속도 관리
	UPROPERTY(EditDefaultsOnly, Category = "Speed")
		float WalkSpeed = 200.0f;

	UPROPERTY(EditDefaultsOnly, Category = "Speed")
		float RunSpeed = 400.0f;

	UPROPERTY(EditDefaultsOnly, Category = "Speed")
		float SprintSpeed = 400.0f;

public:
	FORCEINLINE float GetWalkSpeed() { return WalkSpeed; }
	FORCEINLINE float GetRunSpeed() { return RunSpeed; }
	FORCEINLINE float GetSprintSpeed() { return SprintSpeed; }
	
	FORCEINLINE bool CanMove() { return bCanMove; }

public:	
	UCStatusComponent();

	// 이동 가능하도록, 하지 못하도록 외부에서 세팅가능
	void SetMove();
	void SetStop();

protected:
	virtual void BeginPlay() override;

private:
	// 이동 가능한지
	bool bCanMove = true;
	
};

 

 

 

 

 

 

 

CStatusComponent.cpp


더보기
#include "CStatusComponent.h"
#include "Global.h"

UCStatusComponent::UCStatusComponent()
{
}

void UCStatusComponent::SetMove()
{
	bCanMove = true;
}

void UCStatusComponent::SetStop()
{
	bCanMove = false;
}


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

}

 

 

CStateComponent.h


더보기
#pragma once

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

// 상태를 정의할 enum 자료형을 만듬
// 실무에서는 간단한 상태를 관리하기 위해서는
// enum을 사용하기는 하지만 캐릭터의 상태처럼 복잡한 상태를 관리하는 것은
// enum을 사용하지 않음(StatePattern을 사용)

// 언리얼에서는 기본적으로 ENUM은 uint8(unsigned int8)을 사용
UENUM(BlueprintType)
enum class EStateType : uint8
{
	Idle, Roll, Backstep,
};

// 상태를 바뀌기 위해
// 이벤트로 해줘도 되지만, BP콜이 이게 편함
// 기존타입과 새로 바꾸려는 타입을 넣어줌
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FStateTypeChanged, EStateType, InPrevType, EStateType, InNewType);

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UONLINE_04_ACTION_API UCStateComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	// Get은 괜찮지만, Set은 이 방식은 관리가 짜증남(어디서 set되는지 알수가 없다.)
	//FORCEINLINE void SetType(EStateType InType) { InType = Type; }
	//FORCEINLINE EStateType GetType() {return Type;}
	// 그래서 함수를 하나하나 만들어줌(처음에는 귀찮지만, 관리는 편함)

	// 이렇게 하면 일일히 위처럼 리턴받아 비교하지 않아도
	// IsIdleMode()를 콜하면 받아서 바로 쓸 수 있다.
	// BlueprintPure : (const) getter의 성격에 해당하는 함수를 블루프린트 그래프에 노출시킬 때 사용하면 된다.
	// Get은 BlueprintPure하고 Set은 BlueprintCallable하는 게 좋다.
	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsIdleMode() { return Type == EStateType::Idle; }

	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsRollMode() { return Type == EStateType::Roll; }

	UFUNCTION(BlueprintPure)
		FORCEINLINE bool IsBackstepMode() { return Type == EStateType::Backstep; }

public:
	// 얘의 경우에도 따로 Type을 받지 않아도 Idle 상태로 안에서 Type을 바꿔주면 됨
	void SetIdleMode();
	void SetRollMode();
	void SetBackstepMode();

private:
	void ChangeType(EStateType InType);

public:	
	UCStateComponent();

protected:
	virtual void BeginPlay() override;

public:
	// BlueprintAssignable : 블루프린트에서도 할당 가능하도록
	UPROPERTY(BlueprintAssignable)
		FStateTypeChanged OnStateTypeChanged;

private:
	EStateType Type;

};

 

 

 

 

 

 

 

CStateComponent.cpp


더보기
#include "CStateComponent.h"
#include "Global.h"

UCStateComponent::UCStateComponent()
{
}


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

}

void UCStateComponent::SetIdleMode()
{
	// 디버깅 모드로 누구에 의해 콜되었는지 확인가능한 장점이 있음
	ChangeType(EStateType::Idle);
}

void UCStateComponent::SetRollMode()
{
	ChangeType(EStateType::Roll);
}

void UCStateComponent::SetBackstepMode()
{
	ChangeType(EStateType::Backstep);
}

void UCStateComponent::ChangeType(EStateType InType)
{
	// 기존 타입
	EStateType type = Type;
	// 바뀌려는 새타입
	Type = InType;

	if (OnStateTypeChanged.IsBound())
		OnStateTypeChanged.Broadcast(type, InType);
}

 

 

 

 

결과