본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 46 - Action RPG (Melee Attack Combo)

 

필요한 개념


<콤보>
콤보는 BP때와 알고리즘이 거의 동일하나, 몇가지 타격을 위해서만 좀 추가됨


ACDoAction_Melee 클래스의 DoAction(), Begin_DoAction(), End_DoAction()이 핵심

 

* 콤보를 위한 변수 세팅

// ACDoAction_Melee.h

FORCEINLINE void EnableCombo() { bEnable = true; }
FORCEINLINE void DisableCombo() { bEnable = false; }

bool bExist; // 다음 콤보가 존재하는지
bool bEnable; // 다음 콤보가 허용되었는지
bool bLast; // 콤보의 마지막인지

int32 Index; // 현재 몇번째 콤보중인지



// ACDoAction_Melee.cpp
void ACDoAction_Melee::DoAction()
{
	Super::DoAction();
	// 데이터 개수가 0보다 커야함
	CheckFalse(Datas.Num() > 0);

	// 다음콤보가 허용되었다면
	if (bEnable == true)
	{
		// 다음콤보가 있다고 알려줌
		bExist = true;
		// 콤보 입력 더이상 하지 말라(이미 들어왔으니까)
		bEnable = false;

		return;
	}

	// 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();
}

 

void ACDoAction_Melee::Begin_DoAction()
{
	Super::Begin_DoAction();

	CheckFalse(bExist);
	bExist = false;

	// 현재 플레이 중인 모든 몽타주를 중지시킴
	// 이후에 Fist의 마지막 동작을 끊어서 사용하려고 넣은 코드
	// 이후에 다시 설명
	OwnerCharacter->StopAnimMontage();


	Index++;

	const FDoActionData& data = Datas[Index];
	OwnerCharacter->PlayAnimMontage(data.AnimMontage, data.PlayRatio, data.StartSection);

	data.bCanMove ? Status->SetMove() : Status->SetStop();
}

 

void ACDoAction_Melee::End_DoAction()
{
	Super::End_DoAction();

	const FDoActionData& data = Datas[Index];
	// 현재 몽타주 중단 시킴
	OwnerCharacter->StopAnimMontage(data.AnimMontage);

	State->SetIdleMode();
	Status->SetMove();

	// 다시 처음부터 공격할 수 있게 처리
	Index = 0;
}



* BeginAction Notify 만듬(AnimNotify)

 

다음 동작에 대한 구간을 설정할 AnimNotifyState를 만듬 

-> CAnimNotifyState_EnableCombo(시작에는 EnableCombo(), 끝에는 DisableCombo())

EnableCombo -> 콤보 가능
BeginAction -> 다음동작으로 넘김

* Montage FullBody 세팅
이동금지면 FullBody가 정확히 맞다.

* 3번 동작은 다음 넘어가는 동작이 없어서 EndAction만 주면 된다.

 

 

 

 

 

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:
	FORCEINLINE void EnableCombo() { bEnable = true; }
	FORCEINLINE void DisableCombo() { bEnable = false; }

private:
	bool bExist; // 다음 콤보가 존재하는지
	bool bEnable; // 다음 콤보가 허용되었는지
	bool bLast; // 콤보의 마지막인지

	int32 Index; // 현재 몇번째 콤보중인지
};

 

 

 

 

 

 

 

CDoAction_Melee.cpp 수정된 내용


더보기
...

void ACDoAction_Melee::DoAction()
{
	Super::DoAction();
	// 데이터 개수가 0보다 커야함
	CheckFalse(Datas.Num() > 0);

	// 다음콤보가 허용되었다면
	if (bEnable == true)
	{
		// 다음콤보가 있다고 알려줌
		bExist = true;
		// 콤보 입력 더이상 하지 말라(이미 들어왔으니까)
		bEnable = false;

		return;
	}


	// 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();
}

void ACDoAction_Melee::Begin_DoAction()
{
	Super::Begin_DoAction();

	CheckFalse(bExist);
	bExist = false;

	// 현재 플레이 중인 모든 몽타주를 중지시킴
	// 이후에 Fist의 마지막 동작을 끊어서 사용하려고 넣은 코드
	// 이후에 다시 설명
	OwnerCharacter->StopAnimMontage();


	Index++;

	const FDoActionData& data = Datas[Index];
	OwnerCharacter->PlayAnimMontage(data.AnimMontage, data.PlayRatio, data.StartSection);

	data.bCanMove ? Status->SetMove() : Status->SetStop();
}

void ACDoAction_Melee::End_DoAction()
{
	Super::End_DoAction();

	const FDoActionData& data = Datas[Index];
	// 현재 몽타주 중단 시킴
	OwnerCharacter->StopAnimMontage(data.AnimMontage);

	State->SetIdleMode();
	Status->SetMove();

	// 다시 처음부터 공격할 수 있게 처리
	Index = 0;
}

 

 

CAnimNotify_BeginAction.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotify.h"
#include "CAnimNotify_BeginAction.generated.h"

UCLASS()
class UONLINE_04_ACTION_API UCAnimNotify_BeginAction : public UAnimNotify
{
	GENERATED_BODY()

public:
	FString GetNotifyName_Implementation() const override;

	virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};

 

 

 

 

 

 

 

CAnimNotify_BeginAction.cpp


더보기
#include "CAnimNotify_BeginAction.h"
#include "Global.h"
#include "Characters/CPlayer.h"
#include "Actions/CActionData.h"
#include "Actions/CDoAction.h"
#include "Components/CActionComponent.h"

FString UCAnimNotify_BeginAction::GetNotifyName_Implementation() const
{
	return "BeginAction";
}

void UCAnimNotify_BeginAction::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
	Super::Notify(MeshComp, Animation);
	CheckNull(MeshComp);
	CheckNull(MeshComp->GetOwner());

	UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
	CheckNull(action);

	action->GetCurrent()->GetDoAction()->Begin_DoAction();
}

 

 

CAnimNotifyState_EnableCombo.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotifyState.h"
#include "CAnimNotifyState_EnableCombo.generated.h"

UCLASS()
class UONLINE_04_ACTION_API UCAnimNotifyState_EnableCombo : public UAnimNotifyState
{
	GENERATED_BODY()

public:
	FString GetNotifyName_Implementation() const override;

	virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
	virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};

 

 

 

 

 

 

 

CAnimNotifyState_EnableCombo.cpp


더보기
#include "CAnimNotifyState_EnableCombo.h"
#include "Global.h"
#include "Actions/CEquipment.h"
#include "Actions/CDoAction_Melee.h"
#include "Components/CActionComponent.h"

FString UCAnimNotifyState_EnableCombo::GetNotifyName_Implementation() const
{
	return "EnableCombo";
}

void UCAnimNotifyState_EnableCombo::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{
	Super::NotifyBegin(MeshComp, Animation, TotalDuration);
	CheckNull(MeshComp);
	CheckNull(MeshComp->GetOwner());

	UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
	CheckNull(action);

	ACDoAction_Melee* melee = Cast<ACDoAction_Melee>(action->GetCurrent()->GetDoAction());
	CheckNull(melee);

	melee->EnableCombo();
}

void UCAnimNotifyState_EnableCombo::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
	Super::NotifyEnd(MeshComp, Animation);
	CheckNull(MeshComp);
	CheckNull(MeshComp->GetOwner());

	UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
	CheckNull(action);

	ACDoAction_Melee* melee = Cast<ACDoAction_Melee>(action->GetCurrent()->GetDoAction());
	CheckNull(melee);

	melee->DisableCombo();
}

 

 

 

 

 

결과