Notice
Recent Posts
Recent Comments
Link
«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

공부용 이모저모

UE4 - 루트 모션 본문

UE4 - 기타

UE4 - 루트 모션

불타는버스 2021. 10. 12. 15:26

 

루트 모션 | 언리얼 엔진 문서 (unrealengine.com)

 

루트 모션

언리얼 엔진 4 에서 루트 기반 애니메이션을 처리하는 방식을 살펴봅니다.

docs.unrealengine.com

보통 애니메이션은 프로그램 상에서 이동시키지 않고 자체적으로 이동시키는 경우가 많다.

이는 프로그래머와 애니메이터는 보통 따로 분류하기 때문이다.

 

하지만 애니메이션을 그대로 대입하다보면 아래와같은 문제가 발생한다.

애니메이션이 끝나고 부자연스럽게 캐릭터가 복귀한다.
충돌영역을 무시하고 애니메이션이 진행된다.

이러한 상황을 해결하기 위해 필요한것이 루트 모션이다.

애니메이션에서 본 그리기 - 모든 계층 구조
빨간색 선이 캐릭터 위치와 애니메이션 중점의 차이이다.
에셋 디테일에 EnableRootMotion 체크
캐릭터가 고정되어 보인다
정말 격한 움직임을 가진 애니메이션에만 쓰는것이 좋을듯 하다.
세부 옵션이 존재한다,애니메이션이 애초부터 돌아가있는 경우가 있으므로 첫프레임 기준으로 삼는방법도 있다.
애니메이션이 아닌 캐릭터 기준으로 설정하고 싶다면

애니메이션 블루프린트 - 디테일에 Root motion 전용 옵션이 존재한다.

 

No Root Motion Extraction - 루트 모션 추출 x.루트 모션은 그대로 놔둔다.(애니메이션에서 쓰더라도 무시함)

Ignore Root Motion - 루트모션 무시 - 루트모션을 추출하지만 캐릭터에 적용 X(제자리에서 움직이지 않는다)

Root Motion From Everything - 모든 것에서 루트 모션

Root Motion from Montages Only -  몽타주에서만 루트모션

 

루트모션이 다소 부하가 걸리는듯 하다.전체 애니메이션엔 네크워크에 문제가 생긴다고 한다..
주의점.애니메이션 자체가 꼬여있을경우,방향은 잡아주지만 궤도는 잡아주지 않는다.
방향 자체는 맞게 되었지만, 움직이는 방향은 틀어져있다.아쉬운 부분

이 부분은 애니메이션에서 자체적으로 조율해주거나,Anim First Frame을 사용해서 해결한다.

 

언리얼에서 애니메이션 수정하기

 

언리얼 자체적으로도 애니메이션을 간섭 하는것이 가능하다.

루트에 해당하는 스켈레톤을 선택하고 키를 누르면 줄이 생긴다.

이상태로 회전/이동 등의 액션을 진행 시킨 후 키를 한번 더 누르면 애니메이션에 추가적으로 해당 옵션이 적용된다.

 

루트모션을 적용한 애니메이션. 그런데 점프 모션에 점프를 하지않는다.

두가지 문제점이 생겼다..하나는 애니메이션 자체에 중력이 적용되어 버려서 점프 모션이 점프를 하지 않는것.

두번째는, 애니메이션이 적용되는 동안 이동키,점프키 등이 먹지 않는다는 점이다.

 

중력은 제거할수 있다고 쳐도, 내가 원하던건 ADD였지 값의 대입이 아니었기 때문에

코드상에서 처리해보기로 했다.

앞서 체크했던, 실제좌표와 중점간의 거리를 알아내야한다.

//캐릭터가 갖고있는 메쉬의 본의 개수 만큼 순회한다.
for (int i = 0; i < GetMesh()->GetNumBones(); i++)
{
	//각 본의 이름을 얻어 오는것이 가능하다.
	if (GetMesh()->GetBoneName(i) == "Bip001")
	{
    	//본의 트랜스폼 정보등도 얻어 올 수 있다
		//GetMesh()->GetBoneTransform(i)
		break;
	}
}

 

애니메이션 관련이어서 AnimInstance에서 작업하기로했다.

죽었을 경우라던가, 몽타주가 적용 안되는 상황등에서는 피해야한다고 생각했기 때문이다.

//애니메이션 초당 확인
void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	FAnimInstanceProxy& Proxy = GetProxyOnGameThread<FAnimInstanceProxy>();
	FTransform ProxyTransform = Proxy.GetExtractedRootMotion().GetRootMotionTransform();

	//폰의 포인터를 얻어온다
	auto Pawn = TryGetPawnOwner();
	if (!::IsValid(Pawn)) 
		return;

	if (!IsDead)
	{
		//폰의 속도 데이터를 입력한다(GetVelocity가 속도 데이터이고 Size가 그 수치이다)
		CurrentPawnSpeed = Pawn->GetVelocity().Size();
		//폰을 캐릭터로 캐스팅
		auto Character = Cast<ACharacter>(Pawn);
		if (Character)
		{
			//캐릭터의 떨어짐 여부를 저장한다.
			IsInAir = Character->GetMovementComponent()->IsFalling();

			//어차피 내가 원하는건 중점이고 중점은 일반적으로 0번 본(허리)이다.
			Character->SetActorLocation(Character->GetMesh()->GetBoneTransform(0).GetLocation());
		}
	}
}

처음에는 그냥 캐릭터의 중점 정보만 받아서 Location하면 된다고 생각했다.

특정한 리듬을 타면서 움직이는데 가만 있어야할 캐릭터가 특정방향으로 계속 움직이는 현상을 보인다.

왜 이런현상이 생기는지 고민하다 애니메이션을 보고 이해했다.

중앙기준이라고는 하나 애니메이션 자체가 전체적으로 앞쪽으로 가는게 더많다.
완전히 중앙에 있는게 아니라 좀 틀어져있다.

예를 들어, 원점 기준으로 2프레임에서 3 픽셀을 전진했고, 3프레임에서 5 픽셀을 전진하면

2프레임에서 3프레임으로 바뀌는 시점에는 2픽셀이 전진되어야한다.

이전 프레임의 좌표와 비교해야 하는 문제였으나, 항상 원점과만 비교했기 때문에

2프레임에서 3픽셀을, 3프레임에서 5픽셀을 더해주면서 굴곡이 강해진 현상이 생긴것이다.


FVector BeforeLocation, NowLocation; 두 변수를 추가해놓고 cpp파일을 바꿔보았다.

//애니메이션 초당 확인
void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	FAnimInstanceProxy& Proxy = GetProxyOnGameThread<FAnimInstanceProxy>();
	FTransform ProxyTransform = Proxy.GetExtractedRootMotion().GetRootMotionTransform();

	//폰의 포인터를 얻어온다
	auto Pawn = TryGetPawnOwner();
	if (!::IsValid(Pawn)) 
		return;

	if (!IsDead)
	{
		//폰의 속도 데이터를 입력한다(GetVelocity가 속도 데이터이고 Size가 그 수치이다)
		CurrentPawnSpeed = Pawn->GetVelocity().Size();
		//폰을 캐릭터로 캐스팅
		auto Character = Cast<ACharacter>(Pawn);
		if (Character)
		{
			//캐릭터의 떨어짐 여부를 저장한다.
			IsInAir = Character->GetMovementComponent()->IsFalling();

			//어차피 내가 원하는건 중점이고 중점은 일반적으로 0번 본(허리)이다.
			if (BeforeLocation == NowLocation)
			{
				//원점과 중앙좌표의 오차를 저장
				NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();
			}
			else
			{
				//이전의 좌표를 기억
				BeforeLocation = NowLocation;
				//원점과 중앙좌표의 오차를 저장
				NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();

				Character->SetActorLocation(Character->GetActorLocation() + BeforeLocation - NowLocation);
			}
		}
	}
}

어느정도 적용이 된것처럼 보인다.

			//몽타주 실행중일때
			if (Montage_IsPlaying(AttackMontage))
			{
				if (BeforeLocation == NowLocation)
				{
					//원점과 중앙좌표의 오차를 저장
					NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();
				}
				else
				{
					//이전의 좌표를 기억
					BeforeLocation = NowLocation;
					//원점과 중앙좌표의 오차를 저장
					NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();

					Character->SetActorLocation(Character->GetActorLocation() + BeforeLocation - NowLocation);
				}
			}

몽타주가 실행중일때만 적용시키도록 바꾸면, 평상시 애니메이션에선 흔들리지 않는다.

 

카메라가 애니메이션을 따라오게는 되었으나,이상하게 돌아가는 건 그대로다.

이 이유는 캐릭터는 애니메이션 만큼 앞으로 갔지만 애니메이션은 루트로부터 앞으로 나가는것이기 때문에 그만큼

앞으로 나가기 때문이다.따라서 애니메이션도 현재 위치에 맞기 움직여줄 필요가있다.

 

//몽타주 실행중일때
			if (Montage_IsPlaying(AttackMontage))
			{
				if (BeforeLocation == NowLocation)
				{
					//원점과 중앙좌표의 오차를 저장
					NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();
				}
				else
				{
					//이전의 좌표를 기억
					BeforeLocation = NowLocation;
					//원점과 중앙좌표의 오차를 저장
					NowLocation = Character->GetActorLocation() - Character->GetMesh()->GetBoneTransform(0).GetLocation();
					FVector v = (BeforeLocation - NowLocation);
					Character->SetActorLocation(Character->GetActorLocation() + v);

					//Z값은 언리얼의 Y좌표.캡슐에 지장이 생기니 X Y값만 조정한다
					Character->GetMesh()->MoveComponent(FVector(NowLocation.X, NowLocation.Y, 0), Character->GetActorRotation(), true);
				}
			}

아직 연구중

 

'UE4 - 기타' 카테고리의 다른 글

UE4 - 카와이 피직스(플러그인)  (0) 2022.03.11
UE4 - 플레이어 빙의하기  (0) 2021.08.20
UE4 - 학습  (0) 2021.07.21