2023.04.03 ~ 2023.04.09 개발일지
그동안은 소캣에 미리보기로만 넣어두었던 무기를 이제 액터로서 구현했다
무기는 등에 매거나 손에 잡을 수 있어야 하기 때문에 둘 다 소켓을 만들어주었다
(각 각 spine_01 과 hand_r에 소켓을 만들었다)
발도 납도를 취할때, 두 소켓의 무기가 겹치는 지점을 노티파이를 통해 체크해주었다
이 부분에서 무기를 등에 부착시키거나, 손에 부착시키도록 하겠다
이때, 애니메이션 상의 어색한 부분들은 키값을 바꿔가며 맞춰주었다
이때 노티파이에는 느슨한 결합을 위해 델리게이트를 사용했다
IFCharacterAnimInstance
// IFCharacterAnimInstance.h
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "IFCharacterAnimInstance.generated.h"
DECLARE_DELEGATE(FOnAxeDrawDelegate);
DECLARE_DELEGATE(FOnAxeSheatheDelegate);
...
UCLASS()
class INFINITEFIGHTER_API UIFCharacterAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
FOnAxeDrawDelegate OnDraw;
FOnAxeSheatheDelegate OnSheathe;
...
}
==============================================================
// IFCharacterAnimInstance.cpp
void UIFCharacterAnimInstance::AnimNotify_SheatheWeapon()
{
OnSheathe.Execute();
}
void UIFCharacterAnimInstance::AnimNotify_DrawWeapon()
{
OnDraw.Execute();
}
노티파이에서는 델리게이트를 호출하는 역할만 수행하고
IFCharacter.cpp
void AIFCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
// cast IFCharacterAnimInstance to Character's AnimInstance
AnimInstance = Cast<UIFCharacterAnimInstance>(GetMesh()->GetAnimInstance());
// Binding the Delegates
AnimInstance->OnDraw. BindUObject(this, &AIFCharacter::Draw);
AnimInstance->OnSheathe.BindUObject(this, &AIFCharacter::Sheathe);
}
void AIFCharacter::Draw()
{
FName WeaponSocket(TEXT("Weapon_R"));
Axe->DetachFromActor(FDetachmentTransformRules::KeepRelativeTransform);
Axe->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, WeaponSocket);
}
void AIFCharacter::Sheathe()
{
FName BackSocket(TEXT("Weapon_Back"));
Axe->DetachFromActor(FDetachmentTransformRules::KeepRelativeTransform);
Axe->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, BackSocket);
}
캐릭터에서 무기가 장착되는 것을 구현 후 델리게이트에 등록시켜주었다
완성된 모습은 다음과 같다
공격은 몽타주를 통해 구현했다
이때 몽타주 그룹을 나누고 다음 액션이 가능한 순간 이후부터 입력이 추가로 들어온다면 다음 공격이 가능하게끔 구현했다
IFCharacterAnimInstance.cpp
void UIFCharacterAnimInstance::PlayWeakAttackMontage()
{
if (bCanDoNextAction)
{
AttackCombo++;
if (GetAxeHolding())
{
if (!bIsAttackPlaying)
{
Montage_Play(WeaponWeakAttackMontage, 1.2f);
bIsAttackPlaying = true;
}
else
{
Montage_JumpToSection(GetAttackMontageSection(AttackCombo));
}
}
else
{
if (!bIsAttackPlaying)
{
Montage_Play(UnArmWeakAttackMontage);
bIsAttackPlaying = true;
}
else
{
Montage_JumpToSection(GetAttackMontageSection(AttackCombo));
}
}
}
}
const FName UIFCharacterAnimInstance::GetAttackMontageSection(const int32& Section)
{
return FName(*FString::Printf(TEXT("Attack%d"), Section));
}
입력이 오면 몽타주의 다음 섹션을 플레이 하는 방식으로 구현했다
이렇게 3가지 타입의 공격을 준비했다
(맨손 약공격 3연타)
(맨손 강공격)
(무기 약공격 3연타)
무기 강공격을 추후 추가해줄 예정이다
이때 공격 모션들은 가만히 서서 하는 것이 어색해 보이기에 루트모션을 걸고, 앞으로 전진하도록 수정했다
레퍼런스로 삼고 있는 갓오브워(2018)의 경우,
캐릭터가 행동 중이라면 언제나 캐릭터가 카메라의 회전을 따라가고, 그렇지 않다면 캐릭터 주위를 카메라가 돌 수 있다
이를 구현해주기 위해서 2가지 기법을 사용해보았다
하나는 GetCharacterMovement()->bUseControllerDesiredRotation = true
를 통해 캐릭터를 부드럽게 카메라 방향으로 바꿔주는 것이었고,
다른 하나는 bUseControllerRotationYaw = true
를 통해 즉각적으로 캐릭터를 회전시키는 방법이었다
실험을 해본 결과, 루트모션이 적용된 몽타주는 실행 시 bUseControllerDesiredRotation
를 true 로 설정한다고 하더라도 그 순간 회전을 하지 않았다
그렇기에 몽타주를 실행할때는 bUseControllerRotationYaw = true
를 사용하고, 그렇지 않을때는 반대의 방법을 사용했다
이는 아래와 같은 로직을 통해 실행되었다
IFCharacter.cpp
void AIFCharacter::RotateToCamera()
{
GetCharacterMovement()->bOrientRotationToMovement = false;
GetCharacterMovement()->bUseControllerDesiredRotation = true;
}
// needs the parameters to bind the delegate OnMontageStarted
void AIFCharacter::RotateToCameraMontage(UAnimMontage* Montage)
{
if (!AnimInstance->IsDrawOrSheatheMontage())
bUseControllerRotationYaw = true;
}
void AIFCharacter::RotateDefault()
{
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->bUseControllerDesiredRotation = false;
}
// needs the parameters to bind the delegate OnMontageEnded
void AIFCharacter::RotateDefaultMontage(UAnimMontage* Montage, bool bInterrupted)
{
bUseControllerRotationYaw = false;
if (GetVelocity().Size() == 0)
{
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->bUseControllerDesiredRotation = false;
}
}
적용된 모습은 다음과 같다
자세를 취할때는 캐릭터가 카메라 방향으로 부드럽게 회전하고,
공격몽타주가 실행 중이더라도 캐릭터는 회전이 가능하다
k-토비폭스의 행보를 응원합니다