언리얼 엔진 5/개발 일지

[UE5] 블루아카이브 TPS게임 개발일지 (36) - 엄폐 시스템 구현 6

ciel45 2024. 1. 10. 22:34

서서 엄폐한 상태에서 조준을 할 때, 기존 방식을 그대로 사용할 순 없다.

 

좌우로 막힌 상태에서 조준 버튼을 눌러도 조준을 해선 안되며, 좌 / 우로 트여있다면 그 방향으로 몸을 내밀면서 조준해야한다. 또한 카메라도 해당 방향으로 이동해야한다.

 

우선, CharacterStates.h에 조준 방향을 나타내는 EAimingDirection을 추가하였다.

// CharacterStates.h
#pragma once

UENUM(BlueprintType)
enum class ECharacterState : uint8
{
	ECS_Unarmed UMETA(DisplayName = "Unarmed"),
	ECS_ArmedWithPistol UMETA(DisplayName = "Armed With Pistol"),
	ECS_ArmedWithRifle UMETA(DisplayName = "Armed With Rifle")
};

UENUM(BlueprintType)
enum class EActionState : uint8
{
	EAS_Neutral UMETA(DisplayName = "Neutral"),
	EAS_Attacking UMETA(DisplayName = "Attacking"),
	EAS_Rolling UMETA(DisplayName = "Rolling"),
	EAS_Aiming UMETA(DisplayName = "Aiming"),
};

//조준 방향을 나타내는 열거형
UENUM(BlueprintType)
enum class EAimingDirection : uint8
{
	EAD_Neutral UMETA(DisplayName = "Neutral"),
	EAD_Left UMETA(DisplayName = "Left"),
	EAD_Right UMETA(DisplayName = "Right")
};

 

그리고 KannaCharacter에 EAimingDirection 타입 변수를 만들어주고, Neutral로 초기화했다.

UPROPERTY(BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
EAimingDirection AimingDirection = EAimingDirection::EAD_Neutral;

 

 

 

이제 서서 엄폐 중 마우스 오른쪽 버튼을 눌러서 Aim 함수가 호출되었을 때, 추가할 일은 다음과 같다.

  1. CheckLeftRightHit으로 캐릭터의 양쪽에서 앞으로 라인트레이싱한 결과를 저장한다.
    1. 오른쪽에만 공간이 있다면 AimingDirection을 EAD_Right로 한다.
    2. 왼쪽에만 공간이 있다면 AimingDirection을 EAD_Left로 한다.
    3. 양쪽 모두 공간이 있다면 AimingDirection을 EAD_Right로 한다.
      • 추후 변경될 수 있다.
    4. 양쪽 모두 공간이 없다면 조준을 수행하지 않는다.

 

이를 반영한 Aim 함수는 다음과 같다.

void AKannaCharacter::Aim()
{
	OnAimStart(); // 블루프린트 이벤트 (카메라 확대)

	if (CharacterState == ECharacterState::ECS_Unarmed) return;
	if (ActionState != EActionState::EAS_Neutral) return;

	if (IsInCover && !GetCharacterMovement()->IsCrouching()) //서서 엄폐중 -> 오른쪽, 왼쪽 조준으로 나뉨
	{
		FHitResult HitResult;

		FCollisionQueryParams CollisionParameters;
		CollisionParameters.AddIgnoredActor(this);

		FVector ActorLocation = GetActorLocation();
		UCharacterMovementComponent* Movement = GetCharacterMovement();

		//벽의 방향
		FVector WallDirection = Movement->GetPlaneConstraintNormal() * (-1.f);

		CheckLeftRightHit(WallDirection, ActorLocation, HitResult, CollisionParameters);

		// ********* 조준 방향 결정 **************
		if (LeftHit && !RightHit)
			AimingDirection = EAimingDirection::EAD_Right;
		else if(RightHit && !LeftHit)
			AimingDirection = EAimingDirection::EAD_Left;
		else if (!RightHit && !LeftHit)
			AimingDirection = EAimingDirection::EAD_Right;
		else if (RightHit && LeftHit)// 오른쪽, 왼쪽 모두 빈 공간이 없음 -> 조준 불가
		{
			return;
		}
	}
	else
	{
		AimingDirection = EAimingDirection::EAD_Neutral;
	}

	ActionState = EActionState::EAS_Aiming;

	GetCharacterMovement()->MaxWalkSpeed = 200.f; // 조준 중에는 이동속도 감소
	GetCharacterMovement()->bOrientRotationToMovement = false; // 캐릭터가 방향키에 따라 회전하지 않음 (조준 방향 유지)


}

 

이제 이 AimingDirection을 애니메이션 블루프린트에서 사용할 것이다.

 

애니메이션 블루프린트의 부모클래스인 KannaAnimInstance에 AimingDirection 변수를 추가해주고, Update에서 갱신할 수 있도록 한다.

// KannaAnimInstance.h
UPROPERTY(BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category = "Movement | Aiming Direction")
EAimingDirection AimingDirection;

 

void UKannaAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
	Super::NativeUpdateAnimation(DeltaTime);

	if (KannaCharacterMovement)
	{
		GroundSpeed = UKismetMathLibrary::VSizeXY(KannaCharacterMovement->Velocity);
		IsFalling = KannaCharacterMovement->IsFalling();
		IsCrouching = KannaCharacterMovement->IsCrouching();
		CharacterState = KannaCharacter->GetCharacterState();
		ActionState = KannaCharacter->GetActionState();
		AimingDirection = KannaCharacter->GetAimingDirection(); //이 부분

		CharacterSpeed = KannaCharacter->GetVelocity().Length();
		CharacterDirection = CalculateDirection(KannaCharacter->GetVelocity(), KannaCharacter->GetActorRotation());
		Pitch = KannaCharacter->GetBaseAimRotation().Pitch;
		IsInCover = KannaCharacter->GetIsInCover();
	}
}

 

 

 

이제 애니메이션 블루프린트에서 AimingDirection을 가져다 쓸 수 있다.

먼저 스테이트 머신을 다음과 같이 만들어 주었다.

AimingLeft, AimingRight가 새로 추가된 스테이트이다. 그냥 Aiming은 방향이 상관없는 Neutral에 해당한다.

전환 조건을 각각 (ActionState == Aiming) AND (AimingDirection == Left), (ActionState == Aiming) AND (AimingDirection == Right)로 해주었다.

 

 

 

각 스테이트 내부는 다음 포스팅에서 이어진다.