언리얼5

[UE5 Multiplayer Shooting-9] FABRIK IK를 활용한 무기 장착, AimOffset, FootStepSound 구현

TIN9 2023. 8. 6.
반응형

FABRIK IK

FABRIK (Forward and Backward Reaching Inverse Kinematics)는 효율적인 Inverse Kinematics (IK) 해결 방법 중 하나로, 복잡한 연산 없이도 연결된 관절 구조를 효율적으로 조작할 수 있게 해 줍니다.

 

구현 과정

무기별 스켈레탈 메쉬 - 스켈레톤 트리의 Root_Bone에 소켓 추가 LeftHandSocket로 이름 수정

플레이어 AnimInstance의 업데이트에서 FABRIK IK코드 구현

void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
	Super::NativeUpdateAnimation(DeltaTime);
	
	// 처음 널이라면 얻어옴
	if (BlasterCharacter == nullptr)
	{
		BlasterCharacter = Cast<ABlasterCharacter>(TryGetPawnOwner());
	}
	// 위에서 얻어왔는데도 널이라면 리턴
	if (BlasterCharacter == nullptr)
		return;
        
     .....

	bWeaponEquipped = BlasterCharacter->IsWeaponEquipped();
	EquippedWeapon = BlasterCharacter->GetEquippedWeapon();

	......

	// FABRIK IK
	// 무기를 장착했고 장착한 무기가 있고 장착한 무기의 스켈레탈메쉬가 있고 플레이어의 스켈레탈 메쉬도 있을경우에만 허용
	if (bWeaponEquipped && EquippedWeapon && EquippedWeapon->GetWeaponMesh() && BlasterCharacter->GetMesh())
	{
		// 소켓의 정보를 월드위치로 얻어옴
		LeftHandTransform = EquippedWeapon->GetWeaponMesh()->GetSocketTransform(FName("LeftHandSocket"), ERelativeTransformSpace::RTS_World);
		// 값을 추출해 저장하기 위한 변수
		FVector OutPosition;
		FRotator OutRotation;
		//TransformToBoneSpace 함수를 사용하여 왼손의 위치와 회전을 무기의 소켓 위치로 변환
		// 해당 함수는 지정한 본 ("hand_r")의 공간에 대한 상대적인 변환을 계산합니다.
		BlasterCharacter->GetMesh()->TransformToBoneSpace(FName("hand_r"), LeftHandTransform.GetLocation(),
			FRotator::ZeroRotator, OutPosition, OutRotation);
		// 계산된 위치 OutPosition와 회전 OutRotation으로 LeftHandTransform의 위치와 회전을 업데이트
		LeftHandTransform.SetLocation(OutPosition);
		LeftHandTransform.SetRotation(FQuat(OutRotation));
	}
}

위 코드와 AnimBP 구현한 상태에서 아래와 같이 게임 시작한 뒤 F8 눌러 자유시점 변경 후 hand_l 위치를 무기에 딱 맞게끔 LeftHandSocket의 위치 조절

AimOffset

아래와 같이 9방향에 대한 0프레임짜리 Aim생성

AnimOffset블루프린트 생성

AnimBP - AnimOffset 캐시포즈 생성 및 구현

AnimOffset Cached Pos
AnimBP Equipped - Standing 구조

FootStepSound

FootStepSound는 플레이어 바닥의 Physics Mat을 알아내 Material에 따른 각기 다른 발소리를 출력하도록 구현하였고

플레이어의 이동속도에 따라 발소리의 크기를 달리하여 좀 더 TPS적이고 생동감 있도록 구현하였습니다.

지난 UE5 Action RPG 구현했던 부분 사용했는데 일부 변경해 사용했습니다.

void UFootStepAnimNotify::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, 
	const FAnimNotifyEventReference& EventReference)
{
	ABlasterCharacter* Character = MeshComp->GetOwner<ABlasterCharacter>();

	if (!Character || !Character->IsValidLowLevel())
	{
		return;
	}

	FCollisionQueryParams CollisionParams;
	CollisionParams.AddIgnoredActor(Character);
	CollisionParams.AddIgnoredComponent(Character->GetCapsuleComponent());
	CollisionParams.bReturnPhysicalMaterial = true;

	FVector StartPos = MeshComp->GetOwner()->GetActorLocation();
	FVector EndPos = StartPos;
	EndPos.Z -= 300.f;

	double Length = Character->GetVelocity().Length();

	// 달리기 or 걷기 속도에 따른 발소리 볼륨값 조절
	double OutValue = UKismetMathLibrary::MapRangeClamped(Length, 200.0, 600.0, 0.0, 1.0);

	// 마스터볼륨 조절
	double StepSoundM = 0.5;

	OutValue *= StepSoundM;

	FHitResult HitResult;

	// 바닥으로 Lay를 쏴 피직스 메테리얼을 판단후 해당 Step Sound를 출력하도록 구현
	if (Character->GetWorld()->LineTraceSingleByChannel(HitResult, StartPos, EndPos,
		ECollisionChannel::ECC_Visibility, CollisionParams))
	{
		m_StepSurface = UGameplayStatics::GetSurfaceType(HitResult);

		if (!m_StepSurface)
		{
			UE_LOG(LogTemp, Error, TEXT("FootStep Surface Null!"));
		}

		Sound = SelectSoundBase();
		if (Sound == nullptr)
		{
			UE_LOG(LogTemp, Error, TEXT("FootStep Sound Null!"));
			return;
		}
		VolumeMultiplier = OutValue;
		Super::Notify(MeshComp, Animation, EventReference);
	}
}

cs에 AI모듈 추가해야 한다.

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class Blaster : ModuleRules
{
	public Blaster(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AIModule" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
		
		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
	}
}

 

영상

https://youtu.be/F3p1mF8w6Hc

 

반응형

댓글