그냥 게임개발자

[UE5] - 5장 MeleeAttack 구현(콤보 공격) (C++, 블루프린트) 본문

UE5_Tutorial1

[UE5] - 5장 MeleeAttack 구현(콤보 공격) (C++, 블루프린트)

sudoju 2023. 9. 26. 18:07

본 강의는 인프런 루키스 강의를 보고 작성한 글인점을 알려드립니다.

1. 애니메이션 몽타주

2. 콤보 공격

 

 

ProjectSettings에 Input값을 설정하자.

 

1. 애니메이션 몽타주

콤보 공격에 사용할 애니메이션을 만들기 위해 애니메이션 몽타주를 만든다.

이런 화면이 만들어질 것인데 이 캐릭터는 공격 애니메이션이 4개나 있어서 4개의 콤보 공격을 만들어보자.

드래그를 해서 애니메이션들을 넣어보자.

 

일단 우리는 몽타주 섹션을 구분별로 나누어 볼 것이다.

이런 식으로 Montage(DefaultGroup) 라인에 오른쪽 버튼을 클릭을 해서 몽타주 섹션을 4개정도 추가해줬다.

이름은 그냥 Attack0 ~ 4까지 해주었다.

이 때 번호의 순서는 맞춰주도록 하자.

우리가 나중에 Index로 이용하기 때문이다.

혹시 이름 수정하는 방법을 모를까봐 스샷 첨부했다.

이런 식으로 몽타주 섹션을 추가해준 뒤 우리는 우측 하단에 Montage Sections라고 하는 곳에서 다 하나씩 끊어줄 것이다.

완성!

 

이제 코드를 써보자.

MyCharacter.h

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

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Attack 함수와 Attack을 Check하는 함수를 만듬.
	void Attack();
	void AttackCheck();

	void MoveForward(float Value);
	void MoveRight(float Value);
	void Turn(float Value);
	void LookUp(float Value);
    
protected:
	UPROPERTY(VisibleAnywhere)
	class USpringArmComponent* SpringArm;

	UPROPERTY(VisibleAnywhere)
	class UCameraComponent* Camera;

	// 어택중인 것을 판별하는 변수
	UPROPERTY(VisibleAnywhere, Category=Pawn)
	bool IsAttacking = false;
    	UPROPERTY()
    	// 몽타주를 사용하기 위한 AnimInstance
	class UMyAnimInstance* AnimInstance;

	// 콤보의 인덱스를 사용하기 위한 변수
	UPROPERTY()
	int32 AttackIndex = 0;
    
public:
	UPROPERTY()
	float MoveForwardValue= 0;
	UPROPERTY()
	float MoveRightValue = 0;

 

MyCharacter.cpp

void AMyCharacter::PostInitializeComponents()
{
	// BeginPlay보다 전에 시작됨
	Super::PostInitializeComponents();

	AnimInstance = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());
	if (AnimInstance)
	{
		// 몽타주가 끝나면 AddDynamic에 되어있는 함수를 실행시켜라(델리게이트)
		AnimInstance->OnMontageEnded.AddDynamic(this, &AMyCharacter::OnAttackMontageEnded);
		AnimInstance->OnAttackHit.AddUObject(this, &AMyCharacter::AttackCheck);
	}
}

// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	// BindAxis = 조이스틱
	// BindAction = 조이스틱 버튼
	PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &AMyCharacter::Jump);
	PlayerInputComponent->BindAction(TEXT("Attack"), EInputEvent::IE_Pressed, this, &AMyCharacter::Attack);

	PlayerInputComponent->BindAxis(TEXT("MoveForward"), this, &AMyCharacter::MoveForward);
	PlayerInputComponent->BindAxis(TEXT("MoveRight"), this, &AMyCharacter::MoveRight);
	PlayerInputComponent->BindAxis(TEXT("TurnCamera"), this, &AMyCharacter::Turn);
	PlayerInputComponent->BindAxis(TEXT("LookUp"), this, &AMyCharacter::LookUp);
}

void AMyCharacter::Attack()
{
	if (IsAttacking) { return; }

	AnimInstance->PlayAttackMontage();

	AnimInstance->JumpToSection(AttackIndex);

	AttackIndex = (AttackIndex + 1) % 3;

	IsAttacking = true;
	UE_LOG(LogTemp, Log, TEXT("AttackCheck %s"), (IsAttacking ? TEXT("true") : TEXT("false")));
}

void AMyCharacter::AttackCheck()
{
	FHitResult HitResult;
	FCollisionQueryParams Params(NAME_None, false, this);

	float AttackRange = 100.f;
	float AttackRadius = 50.f;

	// 채널을 이용해서 Sweep하겠다.
	bool bResult = GetWorld()->SweepSingleByChannel(
		OUT HitResult,
		GetActorLocation(),
		GetActorLocation() + GetActorForwardVector() * AttackRange,
		FQuat::Identity,
		ECollisionChannel::ECC_EngineTraceChannel2,
		FCollisionShape::MakeSphere(AttackRadius),
		Params);

	FVector Vec = GetActorForwardVector() * AttackRange;
	FVector Center = GetActorLocation() + Vec * 0.5f;
	float HalfHeight = AttackRange * 0.5f + AttackRadius;
	FQuat Rotation = FRotationMatrix::MakeFromZ(Vec).ToQuat();
	FColor DrawColor;
	if (bResult)
		DrawColor = FColor::Green;
	else
		DrawColor = FColor::Red;

	DrawDebugCapsule(GetWorld(), Center, HalfHeight, AttackRadius,
		Rotation, DrawColor, false, 2.f);

	if (bResult && HitResult.GetActor())
	{
		UE_LOG(LogTemp, Log, TEXT("Hit Actor : %s"), *HitResult.GetActor()->GetName());

		FDamageEvent DamageEvent;
		// 데미지 주는 곳
	}
}

.
.
.

void AMyCharacter::OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted)
{
	// 몽타주가 끝났을 때 불러와지는 델리게이트
	IsAttacking = false;
	UE_LOG(LogTemp, Log, TEXT("MontageEnd %s"), (IsAttacking ? TEXT("true") : TEXT("false")));
}

이제 캐릭터 하나 아무거나 올려놓고 빌드 해서 실행해보자.

잘 된다.

일단 여기까지 써보겠다.

오늘은 설명이 좀 부족한데 나중에 수정하겠다...(바빠)...