● 몽타주 끼워넣기

애니메이션 블루프린트에 공격 모션을 추가하여 공격 모션을 만들 수도 있다. 그리고 그렇게 만든다면 애니메이션 리플리케이션이 더 쉬울 것이다. 하지만 나는 공부하기 위한 목적으로 그러한 방식을 선택하지 않았다.

내가 선택한 방법은 애니메이션 몽타주를 이용하는 방법이다.

사실 싱글 플레이라면 구현 자체는 이 방법이 쉽다. 우선 애니메이션 몽타주를 콘텐츠 브라우저에서 추가한 다음 열어서 공격 모션을 등록한다. 연속 공격은 구현할 생각이 없으니 하나만 골랐다.
UCLASS()
class HIDDENTHIEF_API UMainCharAnimation : public UAnimInstance
{
GENERATED_BODY()
public:
virtual void NativeBeginPlay() override;
//공격 애니메이션 실행
UFUNCTION()
void PlayAttackAnimation();
private:
//공격 애니메이션 몽타주
UPROPERTY(EditInstanceOnly, Category = "Montage", meta = (AllowPrivateAccess = true))
UAnimMontage* AttackMontage;
};
그다음 지금 사용하는 애니메이션의 코드에 애니메이션 몽타주를 할당한 변수와 public함수를 선언한다.
void UMainCharAnimation::PlayAttackAnimation()
{
Montage_Play(AttackMontage);
}
함수에서는 그 몽타주를 실행하는 함수를 사용하면 된다. 애님 인스턴스를 상속받은 클래스에서는 몽타주 관련 함수를 사용할 수가 있으니 구현은 어렵지 않다.

선언한 UPROPERTY 변수는 블루프린트에서 할당하는 것을 잊지 말자
//MainGameCharacter.h
private:
//이 캐릭터의 애니메이션
UPROPERTY()
UMainCharAnimation* MainCharAnimation;
//MainGameCharacter.cpp
void AMainGameCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
MainCharAnimation = Cast<UMainCharAnimation>(GetMesh()->GetAnimInstance());
if (!IsValid(MainCharAnimation))
{
UE_LOG(LogHiddenThief, Error, TEXT("'%s' : 애님 인스턴스를 찾을 수 없습니다."), *GetNameSafe(this));
}
}
void AMainGameCharacter::Attack()
{
//공격
if (IsValid(MainCharAnimation))
{
MainCharAnimation->PlayAttackAnimation();
}
}
참고로 캐릭터가 가진 메시의 애니메이션 인스턴스에 접근하는 방법도 복잡하지 않다. 캐릭터의 GetMesh 함수가 스켈레탈 메시 컴포넌트를 반환하고, 그 컴포넌트에서 GetAnimInstance 함수가 애니메이션 인스턴스를 반환한다. 여기서 직접 만든 클래스로 캐스팅 하면 선언했던 public 함수를 호출할 수 있다.


//MainGameCharacter.h
protected:
//마우스 왼쪽 버튼 클릭
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* MouseClickAction;
//MainGameCharacter.cpp
//마우스 클릭
EnhancedInputComponent->BindAction(MouseClickAction, ETriggerEvent::Started, this, &AMainGameCharacter::Attack);
그런데 생각해보니까 이 공격 함수를 실행하기 위한 입력을 만들지 않았다. 즉시 마우스 클릭에 대한 에셋 생성 및 코드 구현을 진행했다.

● 문제 해결하기
A. 연속 공격 방지
마우스를 광클하면 누른대로 계속 공격 모션이 나간다. 공격을 시작하면 적어도 모션을 마칠 때 까지 공격을 못하게 막아야 한다.
void UMainCharAnimation::PlayAttackAnimation()
{
if (Montage_IsPlaying(AttackMontage))
{
return;
}
Montage_Play(AttackMontage);
}
일단 몽타주 실행 부분은 해당 몽타주가 재생중이면 재생 못하게 막는다.
//MainGameCharacter.h
private:
//공격 최소 간격(초)
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Character State", meta = (AllowPrivateAccess = true))
float AttackDelayTime;
//공격 딜레이 적용
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Character State", meta = (AllowPrivateAccess = true))
float CurrentAttackDelay;
//MainGameCharacter.cpp
void AMainGameCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (CurrentAttackDelay > 0)
{
CurrentAttackDelay -= DeltaTime;
}
}
void AMainGameCharacter::Attack()
{
//중복 공격 불가
if (CurrentAttackDelay > 0)
{
return;
}
//공격
CurrentAttackDelay = AttackDelayTime;
if (IsValid(MainCharAnimation))
{
MainCharAnimation->PlayAttackAnimation();
}
}

그리고 캐릭터에도 딜레이 시간을 구현하여 중복 공격을 막는다.
굳이 이중으로 막을 필요가 있나 싶지만 이후 구현할 내용을 생각하여 결정한 것이다. 애니메이션에서야 몽타주 중복 실행을 막는 것이 당연하겠지만 캐릭터에서는 이 공격 모션을 가지고 추가로 구현할 것이 있기 때문이다.
B. 공격중 이동을 못하게 막기
지금은 이동하면서 공격을 할 수 있는 문제가 있다. 발은 가만히 있는데 이동하는 어색한 연출의 문제도 있지만 공격을 할 때 신중하게 공격할 것을 의도한 내용이다.
void AMainGameCharacter::Move(const FInputActionValue& Value)
{
//공격 상태에서는 못움직인다.
if (CurrentAttackDelay > 0)
{
return;
}
//입력을 Vector2D로 변환
FVector2D MovementVector = Value.Get<FVector2D>();
//이동 적용
DoMove(MovementVector.X, MovementVector.Y);
}
템플릿에서 복사한 코드중 이동을 담당하는 코드에서 연속 공격 방지에 사용한 if문을 동일하게 추가하면 바로 해결된다.
C. 애니메이션에 의한 이동 방지
공격을 할 때마다 캐릭터가 조금씩 앞으로 이동한다. 공격 애니메이션 자체가 앞으로 전진하면서 움직이기 때문이다. 개인적으로 이 상황이 불편해서 애니메이션에 의한 이동을 막으려고 한다.

그리고 그러기 위한 해결 방법은 간단했다. 하지만 여기까지 도달하는 데에 시간이 걸렸다.
해결 방법은 애니메이션 블루프린트에서 디테일 패널에 있는 루트 모션 모드를 변경하는 것이었다. 처음은 Root Motion From Montage Only로 선택된 상태였는데 이를 Ignore Root Motion으로 변경하니 해결됐다.
참고로 No Root Motion Extraction을 선택하면 애니메이션대로 앞으로 이동하다가 도로 제자리로 돌아오는, 더 어색한 연출이 된다. 그리고 Root Motion from Everything은 앞으로 이동하는 문제가 여전한 데다가 네트워크 환경에서는 부적절한 선택이라고 한다.
'언리얼엔진 학습 및 과제' 카테고리의 다른 글
| 액터 리플리케이션을 이용한 게임 제작 (5) (0) | 2025.12.23 |
|---|---|
| 액터 리플리케이션을 이용한 게임 제작 (4) (0) | 2025.12.22 |
| 액터 리플리케이션을 이용한 게임 제작 (2) (0) | 2025.12.17 |
| 액터 리플리케이션을 이용한 게임 제작 (1) (0) | 2025.12.12 |
| 네트워크 채팅을 이용한 숫자야구 게임 (3) (0) | 2025.12.02 |