언리얼에서 IK기능을 활용하여 애니메이션을 원하는대로 수정, 적용할 수 있다.
IK기능과 FK기능이 무엇인지, 어떤역할로 어떤방식으로 동작하는지 설명할 수 있다.
IK란 무엇인가?
IK가 무엇인가? 이를 이해하려면 FK와 IK의 차이점을 이해해야 한다.
Forward Kinematics(FK)
여기서 H를 움직인다고 가정한다.
FK방식에서는 Root->SP1->SP2->A->K->H 순서대로 움직임을 제어한다.
즉, Root이 움직이면 그 하위에 있는 SP1부터 H까지의 모든 관절이 같이 움직이게 된다.
가장 최우선의 관절부터 객체를 움직이게 되는걸 FK라고 한다.
동작방식
- 순차적 관절 회전
- 캐릭터의 각 관절을 순차적으로 회전시켜 원하는 포즈를 만든다.
- 직접 제어
- 에니메이터가 관절별로 직접적으로 위치와 회전을 제어한다.
- 예측 가능성
- 각 관절의 회전이 직접적으로 영향을 주기 때문에, 결과를 예측하기 쉽다.
장점
- 단순하다.
- 계산이 비교적 단순하기에 리소스가 적게든다. Root부터 나머지 본으로 연산을 더해버리면 되기때문.
- 정밀 제어
- 개별 관절을 직관적으로 정밀하게 제어할 수 있다.
단점
- 복잡하다.
- 단순히 손만 움직인다고 하더라도, Root부터 나머지 본을 전부 탐색연산해야한다.
- 제어 어려움
- Root부터 이동하기 때문에, H를 움직이려고 할때 전에위치한 본 때문에 원하는 동작이 나타나지 않을 수 있다.
Inverse Kinematics(IK)
여기서 H를 움직인다고 가정한다.
IK방식에서는 H를 움직이면, H -> K -> A -> SP2 ->SP1 -> Root 순서대로 움직임을 제어한다.
즉, H가 움직이면 그 상위에 있는 움직임에 한단계 씩 움직임을 제어하게 되는것 이다.
움직이려는 관절부터 움직이면서 상위본 으로 움직임을 전파하는것을 IK라고 한다.
동작방식
- 말단 관절 제어
- 캐릭터의 손이나 발 같은 말단 관절의 위치를 먼저 지정한다
- 자동 계산
- 나머지 관절의 회전값은 지정된 관절 위치에 도달하도록 자동으로 계산된다.
- 편리성
- 단순히 말단 관절을 움직이면 중간 관절들이 자동으로 적절히 배치된다.
장점
- 직관적이다.
- 말단의 관절의 위치만 지정하고 움직이면 됨으로 직관적이고 쉽게 제어할 수 있다.
- 시간 절약
- 많은 관절을 쉽고 빠르게 제어할 수 있어서 시간절약 이 가능하다.
단점
- 복잡한 계산이 필요함
- 복잡한 수식계산이 필요함으로 리소스가 많이 필요하다.
- 불안정성
- 복잡한 수식으로 자동연산이 이루어짐 으로 사용자가 예상하지 못한, 특정 포즈에서 예기치않은 결과가 나타날 수 있다.
언리얼에 IK 적용하기
우선, Base_Charcter에 일부분 데이터를 추가해야한다.
FVector IKTargetLocation;
bool bUseIK;
FVector형 변수와 bool형변수 를 추가한다.
IKTargetLocation은, IK를 동작시킬 때 지정된 본을 이 변수의 위치에 이동시키기 위해 사용된다.
bUseIK는 지정된 상황에서만 IK를 동작시키기 위해 사용된다.
void ABaseCharacter::InteractCanceled()
{
bCanInteract = true;
InteractObject = nullptr;
bUseIK = true;
}
void ABaseCharacter::InteractComplete()
{
bInteractProcessing = false;
bUseIK = false;
}
Interact 함수를 지정한다.
InteractCancled를 실행할 때는 실행한다는 의미이기 때문에, Interact가 실행되면서 bUseIK를 True로 만들고
InteractComplete를 실행했을 때는, 실행이 종료되었다는 의미이기 때문에, Interact가 종료되면서, bUseIK를 false로 만든다.
Base_Weapon
무기에서 Interact될 때, 이 무기의 위치를 Player에 반환해주어야 한다.
void ABaseWeapon::Interact(ABaseCharacter* InteractingActor)
{
// Play Anim Montage
{
enum
{
Low,
Mid
};
const uint8 Index = InteractingActor->GetActorLocation().Z > this->GetActorLocation().Z ?
static_cast<uint8>(Low) : static_cast<uint8>(Mid);
InteractingActor->PlayBehaviorAnimMontage(ECharacterPose::Stand, EBehavior::PickUp, Index);
}
InteractingActor->IKTargetLocation = GetActorLocation();
InteractingActor->bUseIK = true;
InteractingActor->RequestPickUpWeapon(this);
if (InteractingActor->FinishInteract.IsBound())
InteractingActor->FinishInteract.Execute();
}
vector변수를, 무기를 줍는 캐릭터에 전달하게 작업한다.
주의해야 할 점은, 캐릭터에 현재 위치를 전달한 다음에, RequestPickUpWeapon을 실행해야 한다는 점 이다.
순서를 맞추기 어렵다면, 몽타주에 노티파이를 추가해서 노타피아기 RequestPickUPWeapon을 실행하게 해도 좋다.
코드를 작성할 때
위와같이 정의해도 코드가 정상적으로 동작하나
델리게이트 를 통해서 IK 를 동작시키기 위한 Vector값을 지정해도 무방하다. 코드 작성법은 자유
ABP에서 IK 적용하기.
언리얼5에서는 쉽고 간단하게 IK를 적용할 수 있도록 되어있다.
언리얼4 에서는 엄청 고생하면서 넣어야 함으로 참고하자.
ABP가 상속받게되는 부모의 코드를 수정하자.
변수값만 추가하면 된다.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "DefaultAnimInstance.generated.h"
class ABaseCharacter;
/**
*
*/
UCLASS()
class FTPSGAME_API UDefaultAnimInstance : public UAnimInstance
{
GENERATED_BODY()
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = BlendSpace, meta=(AllowPrivateAccess="true"))
bool bFalling;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = BlendSpace, meta=(AllowPrivateAccess="true"))
float Speed;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = BlendSpace, meta=(AllowPrivateAccess="true"))
float Direction;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = IK, meta = (AllowPrivateAccess = "true"))
FVector IKTargetLocation;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = IK, meta = (AllowPrivateAccess = "true"))
bool bUseIK;
UPROPERTY()
TObjectPtr<ABaseCharacter> Owner;
public:
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
private:
void UpdateBlendSpaceValue();
};
bUseIK와 IKTargetLocation을 블루프린트 에서 읽을 수 있도록 생성한다.
#include "Animation/DefaultAnimInstance.h"
#include "Utilities/Helper.h"
#include "Actors/BaseCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "KismetAnimationLibrary.h"
void UDefaultAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
Owner = Cast<ABaseCharacter>(TryGetPawnOwner());
if(Owner == nullptr) return;
UpdateBlendSpaceValue();
IKTargetLocation = Owner->IKTargetLocation;
bUseIK = Owner->bUseIK;
}
void UDefaultAnimInstance::UpdateBlendSpaceValue()
{
bFalling = Owner->GetCharacterMovement()->IsFalling();
Speed = Owner->GetVelocity().Size2D();
FRotator Rotation = FRotator(0, Owner->GetControlRotation().Yaw, 0);
Direction = UKismetAnimationLibrary::CalculateDirection(Owner->GetVelocity(), Rotation);
}
그리고, 이벤트 값을 업데이트하기 위해 에니메이션 틱 마다 IKTargetLocation과 bUseIK를 업데이트 받으면 된다.
ABP 블루프린트 수정
전에 구성하였던 ABP를 위와같이 바꾸면 된다.
에니메이션 레이어로 IK를 구성하고, BlendPoses by bool을 통해 지정된 상황에서만 IK가 실행되도록 구성하자.
IK를 위와같이 정의할 수 있다.
Pose를 받아와서, TwoBoneIK를 통해 몽타주의 지정된 커브의 Alpha값을 이용하여 IK의 Location을 보간하여 수정해준다.
Curve Value의 이름은 몽타주에서 정의한 커브의 이름을 넣어주면 된다.
IK에 사용되는 커브데이터는 몽타주에서 생성할 수 있다.
새 커브 생성을 통해 새 변수 커브를 생성할 수 있다.
이 때 위의 ABP에서 사용되는 Alpha값은 1이 최대, 0이 최소 이기 때문에 무기를 줍는 순간이 1이되도록 한 뒤에 필터값을 선형이 아닌 곡선으로 넣어주면 된다.
테스트
'서울게임아카데미 교육과정 6개월 국비과정' 카테고리의 다른 글
20240521 148일차 언리얼 C++ Aim, Hip조준 전환 (0) | 2024.05.22 |
---|---|
20240521 148일차 언리얼 C++ IK활용 조준보정 & 무기스왑 (0) | 2024.05.21 |
20240513 143일차 언리얼 C++ 애니메이션몽타주시스템 구축 (0) | 2024.05.13 |
20240501 137일차 언리얼 C++ 데이터 레이블 활용 캐릭터 생성6 (0) | 2024.05.01 |
20240430 136일차 언리얼 C++ 데이터 레이블 활용 캐릭터 생성5 (0) | 2024.04.30 |