필요한 개념
<CDoAction & CDoAction_Melee 생성>
Melee : 근거리 공격 처리를 위한
CAction : 액션을 담당한 클래스(액션을 수행한다는 의미에서 CDoAction으로 이름 명명)
CAttachment에서 충돌이 정의될 것이어서 CAction은 CAttachment에 연결될 것이다.
// CDoAction.h
public:
// 3가지의 가상함수를 가짐
// 액션을 수행(액션에 해당하는 몽타주 플레이)
virtual void DoAction(){};
// 몽타주에 의해 시작
virtual void Begin_DoAction(){};
// 몽타주에 의해 종료
virtual void End_DoAction(){};
그리고 이 클래스를 상속받아 액션을 수행하는 클래스를 정의할 것이다.(CDoAction_Melee class)
// CActionData.h
// 액션 같은 경우 몽타주 데이터가 더 필요함
USTRUCT(BlueprintType)
struct FDoActionData : public FEquipmentData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
float Power = 5.0f;
// HitStop : 타격되었을 때 일시정지(경직)을 구현할 변수(타격감)
UPROPERTY(EditAnywhere)
float HitStop;
// 타격시 플레이 시킬 효과
UPROPERTY(EditAnywhere)
class UParticleSystem* Effect;
// 효과 위치, 크기를 추가적으로 보정하기 위한 값
UPROPERTY(EditAnywhere)
FTransform EffectTransform;
// 타격시 카메라의 흔들림을 구현할 클래스
UPROPERTY(EditAnywhere)
TSubclassOf<class UCameraShake> ShakeClass;
};
<CAction_Melee Attack 구현>
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TSubclassOf<class ACDoAction> DoActionClass;
를 줘서 세팅가능하도록 한다.
// 몽타주 동작이 여러개니까 여러개 세팅하도록 한다. 이것도 데이터에셋에서 수동으로 세팅함
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<FDoActionData> DoActionDatas;
// CActionData.cpp
// DoAction
{
DoAction = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACDoAction>(DoActionClass, transform, InOwnerCharacter);
DoAction->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
DoAction->SetActorLabel(InOwnerCharacter->GetActorLabel() + "_DoAction");
UGameplayStatics::FinishSpawningActor(Equipment, transform);
}
* 데이터 에셋 BP인 DA_OneHand에서 변수 세팅
전체 데이터를 다 입력하는 것이 아닌 해당 액션에 필요한 정보만 설정함(ex) 효과 이펙트나 이런거 안넣어도 됨)
* Sword_OneHand_Attack_1_Montage 만들어서 세팅
공격시 이동 불가처리 위해서 DefaultSlot을 FullBody로 세팅한다.
* CStateComponent.h에 Action(공격) 상태 추가
UENUM(BlueprintType)
enum class EStateType : uint8
{
// Action : 공격 상태
Idle, Roll, Backstep, Equip, Action, Max
};
// CDoAction.cpp
// 데이터에 맞게 실행시켜줌
void ACDoAction_Melee::DoAction()
{
Super::DoAction();
// 데이터 개수가 0보다 커야함
CheckFalse(Datas.Num() > 0);
// IdleMode여야 함
CheckFalse(State->IsIdleMode());
State->SetActionMode();
const FDoActionData& data = Datas[0];
OwnerCharacter->PlayAnimMontage(data.AnimMontage, data.PlayRatio, data.StartSection);
data.bCanMove ? Status->SetMove() : Status->SetStop();
}
* ActionMapping 세팅된 걸로 Character에서 플레이 시켜주면 됨
CAnimInstance에서 ActionMode를 세팅하고 가져옴
ActionType을 가져오는데 ActionComonent에 있는 Delegate에 함수 연결해서 값을 변경하는게 효율적(계속 Update()하는 것 보단)
action->OnActionTypeChanged.AddDynamic(this, &UCAnimInstance::OnActionTypeChanged);
* AnimBP에서 ActionType에 따라 애니메이션 블랜드 포즈를 지정해 줌(Base포즈에)
블랜드 포즈에 오른쪽 마우스 클릭 시 Type추가가 가능하다.
* 원래는 BeginAction EndAction 할때 Begin, End가 있으면 NotifyState 기반으로 생성하는데, 콤보 때문에 별도로 CNotify를 기반으로 생성함
<CAnimNotify_EndAction 클래스 생성>
// CAnimNotify_EndAction.cpp
void UCAnimNotify_EndAction::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
Super::Notify(MeshComp, Animation);
CheckNull(MeshComp);
CheckNull(MeshComp->GetOwner());
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
CheckNull(action);
// 애초에 없으면 이 애니메이션에 들어올 수 없어서 따로 NULL체크 안함
action->GetCurrent()->GetDoAction()->End_DoAction();
}
* 몽타주에 노티파이 추가함
CDoAction.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Actions/CActionData.h"
#include "CDoAction.generated.h"
UCLASS()
class UONLINE_04_ACTION_API ACDoAction : public AActor
{
GENERATED_BODY()
public:
FORCEINLINE void SetDatas(TArray<FDoActionData> InDatas) { Datas = InDatas; }
public:
ACDoAction();
public:
// 3가지의 가상함수를 가짐
// 액션을 수행(액션에 해당하는 몽타주 플레이)
virtual void DoAction(){};
// 몽타주에 의해 시작
virtual void Begin_DoAction(){};
// 몽타주에 의해 종료
virtual void End_DoAction(){};
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
protected:
UPROPERTY(BlueprintReadOnly)
class ACharacter* OwnerCharacter;
UPROPERTY(BlueprintReadOnly)
class UCStateComponent* State;
UPROPERTY(BlueprintReadOnly)
class UCStatusComponent* Status;
protected:
TArray<FDoActionData> Datas;
};
CDoAction.cpp
#include "CDoAction.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Components/CStatusComponent.h"
ACDoAction::ACDoAction()
{
PrimaryActorTick.bCanEverTick = true;
}
void ACDoAction::BeginPlay()
{
OwnerCharacter = Cast<ACharacter>(GetOwner());
State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
Status = CHelpers::GetComponent<UCStatusComponent>(OwnerCharacter);
Super::BeginPlay();
}
void ACDoAction::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
CDoAction_Melee.h
#pragma once
#include "CoreMinimal.h"
#include "Actions/CDoAction.h"
#include "CDoAction_Melee.generated.h"
UCLASS()
class UONLINE_04_ACTION_API ACDoAction_Melee : public ACDoAction
{
GENERATED_BODY()
public:
virtual void DoAction() override;
virtual void Begin_DoAction() override;
virtual void End_DoAction() override;
};
CDoAction_Melee.cpp
#include "CDoAction_Melee.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/CStateComponent.h"
#include "Components/CStatusComponent.h"
void ACDoAction_Melee::DoAction()
{
Super::DoAction();
// 데이터 개수가 0보다 커야함
CheckFalse(Datas.Num() > 0);
// IdleMode여야 함
CheckFalse(State->IsIdleMode());
State->SetActionMode();
const FDoActionData& data = Datas[0];
OwnerCharacter->PlayAnimMontage(data.AnimMontage, data.PlayRatio, data.StartSection);
}
void ACDoAction_Melee::Begin_DoAction()
{
Super::Begin_DoAction();
}
void ACDoAction_Melee::End_DoAction()
{
Super::End_DoAction();
State->SetIdleMode();
Status->SetMove();
}
CActionData.h 추가된 내용
...
// 액션 같은 경우 몽타주 데이터가 더 필요함
USTRUCT(BlueprintType)
struct FDoActionData : public FEquipmentData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
float Power = 5.0f;
// HitStop : 타격되었을 때 일시정지(경직)을 구현할 변수(타격감)
UPROPERTY(EditAnywhere)
float HitStop;
// 타격시 플레이 시킬 효과
UPROPERTY(EditAnywhere)
class UParticleSystem* Effect;
// 효과 위치, 크기를 추가적으로 보정하기 위한 값
UPROPERTY(EditAnywhere)
FTransform EffectTransform;
// 타격시 카메라의 흔들림을 구현할 클래스
UPROPERTY(EditAnywhere)
TSubclassOf<class UCameraShake> ShakeClass;
};
UCLASS()
class UONLINE_04_ACTION_API UCActionData : public UDataAsset
{
GENERATED_BODY()
public:
...
FORCEINLINE class ACDoAction* GetDoAction() { return DoAction; }
public:
...
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TSubclassOf<class ACDoAction> DoActionClass;
UPROPERTY(BlueprintReadOnly, EditAnywhere)
TArray<FDoActionData> DoActionDatas;
private:
...
class ACDoAction* DoAction;
};
CActionData.cpp 추가된 내용
...
#include "CDoAction.h"
void UCActionData::BeginPlay(class ACharacter* InOwnerCharacter)
{
...
//DoAction
{
DoAction = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACDoAction>(DoActionClass, transform, InOwnerCharacter);
DoAction->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
DoAction->SetActorLabel(InOwnerCharacter->GetActorLabel() + "_DoAction");
DoAction->SetDatas(DoActionDatas);
UGameplayStatics::FinishSpawningActor(Equipment, transform);
}
}
CStateComponent.h 추가된 내용
...
UENUM(BlueprintType)
enum class EStateType : uint8
{
// Action : 공격 상태
Idle, Roll, Backstep, Equip, Action, Max
};
...
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UONLINE_04_ACTION_API UCStateComponent : public UActorComponent
{
GENERATED_BODY()
public:
...
UFUNCTION(BlueprintPure)
FORCEINLINE bool IsActionMode() { return Type == EStateType::Action; }
public:
...
void SetActionMode();
...
};
CStateComponent.cpp 추가된 내용
...
void UCStateComponent::SetActionMode()
{
ChangeType(EStateType::Action);
}
CPlayer.h 추가된 내용
...
UCLASS()
class UONLINE_04_ACTION_API ACPlayer : public ACharacter
{
GENERATED_BODY()
...
private:
void OnOneHand();
void OnDoAction();
};
CPlayer.cpp 추가된 내용
...
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
...
PlayerInputComponent->BindAction("Action", EInputEvent::IE_Pressed, this, &ACPlayer::OnDoAction);
}
void ACPlayer::OnDoAction()
{
// 얘는 따로 State를 체크하지 않음(공격상태인데 콤보일수도 있어서)
//CheckFalse(State->IsIdleMode());
Action->DoAction();
}
CAnimInstance.h 추가된 내용
...
UCLASS()
class UONLINE_04_ACTION_API UCAnimInstance : public UAnimInstance
{
GENERATED_BODY()
protected:
...
UPROPERTY(BlueprintReadOnly, EditAnywhere)
EActionType ActionType;
private:
// 무기를 선택했을 때 (ActionType이 변경될때)
// 델리게이션에 의해 호출될 함수를 생성
// ActionType 바꿔 줄려고, 그냥 Update계속 하는것 보다 효율적
UFUNCTION()
void OnActionTypeChanged(EActionType InPrevType, EActionType InNewType);
};
CAnimInstance.cpp 추가된 내용
...
void UCAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
ACharacter* character = Cast<ACharacter>(TryGetPawnOwner());
CheckNull(character);
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(character);
CheckNull(action);
// 연결 시켜줌
action->OnActionTypeChanged.AddDynamic(this, &UCAnimInstance::OnActionTypeChanged);
}
void UCAnimInstance::OnActionTypeChanged(EActionType InPrevType, EActionType InNewType)
{
ActionType = InNewType;
}
CAnimNotify_EndAction.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "CAnimNotify_EndAction.generated.h"
UCLASS()
class UONLINE_04_ACTION_API UCAnimNotify_EndAction : public UAnimNotify
{
GENERATED_BODY()
public:
FString GetNotifyName_Implementation() const override;
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};
CAnimNotify_EndAction.cpp
#include "CAnimNotify_EndAction.h"
#include "Global.h"
#include "Characters/CPlayer.h"
#include "Actions/CActionData.h"
#include "Actions/CDoAction.h"
#include "Components/CActionComponent.h"
FString UCAnimNotify_EndAction::GetNotifyName_Implementation() const
{
return "EndAction";
}
void UCAnimNotify_EndAction::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
Super::Notify(MeshComp, Animation);
CheckNull(MeshComp);
CheckNull(MeshComp->GetOwner());
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
CheckNull(action);
// 애초에 없으면 이 애니메이션에 들어올 수 없어서 따로 NULL체크 안함
action->GetCurrent()->GetDoAction()->End_DoAction();
}
결과
'Unreal Engine 4 > C++' 카테고리의 다른 글
<Unreal C++> 47 - Action RPG (Character Interface) (0) | 2022.05.16 |
---|---|
<Unreal C++> 46 - Action RPG (Melee Attack Combo) (0) | 2022.05.16 |
<Unreal C++> 43 - Action RPG(Weapon Attachment) (0) | 2022.05.09 |
<Unreal C++> 40 - Action RPG (Action Component & Equip) (0) | 2022.05.09 |
<Unreal C++> 37 - Action RPG (Avoiding(Montages)) (0) | 2022.05.03 |