프로그래밍 공부
작성일
2024. 5. 21. 15:24
작성자
WDmil
728x90

무기스왑 애니메이션에 IK를 삽입하여 좀더 능동적으로 애님인스턴스를 다룰 수 있다.

 

IK를 위해 객체의 LocalPosition을 참조할 수 있다.

 

에임 오프셋을 생성, 관리 할 수 있다.


Aim Offset 생성

 

 

캐릭터의 조준각도를 조정하기 위해 에임 오프셋을 생성한다.

 

에임 오프셋은 애니메이션의 지정각도 범위에 따라 총기의 위치를 조정해준다.


Base Animation Set

 

에임 오프셋을 사용하기 위해 객체의 기본모션을 정해주고, 설정해야 한다.

지정된 모든 Aim애니메이션을 프로퍼티 매트릭스 에서 한번에 정리한다.

설정은 위 고정된 열 에서의 객체를 참조하여 정의한다.

 

베이스 포즈 에니메이션은 총을 들고 서있는 모션을 기준으로 전환해야 하기 떄문에, 총을 들고 서있는 모션을 기준으로 정해준다.

베이스 포즈


에임 오프셋 설정

 

가로축 과 세로축은 위와같이 정의한다. 0. 0을 정면을 기준으로 서서 바라보는 것으로 하고, -90을 L +90을 R로 한다.

 

그리드 분할양은, 애니메이션의 개수로 정의한다.

배치는 위와같이 한다.

 

정면기준 CC, Up Down, Left, Right로 정의하고 배치한다.

 


ABP

 

기존 애니메이션의 ABP에 AO를 정의하고 전달할 수 있다.

 

Yaw와 Pitch를 C++에서 생성해야 함으로, ABP는 위와같이 배치함 이라고만 이해하자. C++에서 Yaw와 Pitch를 정의한다.


Yaw, Pitch

// 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 UCharacterMovementComponent;
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;

	// false濡?諛붽씀??遺€遺꾩? 釉붾(?꾨┛?몃줈 泥섎━?⑸땲??
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = BlendSpace, meta=(AllowPrivateAccess="true"))
	bool bTurnLeft;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, Category = BlendSpace, meta=(AllowPrivateAccess="true"))
	bool bTurnRight;

	
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = AimOffset, meta=(AllowPrivateAccess="true"))
	float Yaw;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = AimOffset, meta=(AllowPrivateAccess="true"))
	float Pitch;
	


	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = IK, meta=(AllowPrivateAccess="true"))
	FVector IKTargetLocation;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = IK, meta=(AllowPrivateAccess="true"))
	bool bUseIK;


	
	UPROPERTY()
	TObjectPtr<ABaseCharacter> Owner;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, meta=(AllowPrivateAccess="true"))
	TObjectPtr<UCharacterMovementComponent> Movement;

public:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

private:
	void UpdateBlendSpaceValue();
};

 

UPROPERTY로 생성한 Yaw와 Pitch는 블루프린트 에서 참조만 할 수 있게 정의한다.

 

Yaw와 Pitch는 매우 간단하게 구할 수 있는데, 컨트롤 로테이션을 참조하면 된다.

void UDefaultAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	if (Owner == nullptr) return;

	if (Owner)
		Movement = Owner->GetCharacterMovement();

	if (!Movement) return;

	UpdateBlendSpaceValue();
	
	IKTargetLocation = Owner->IKTargetLocation;
	bUseIK = Owner->bUseIK;

	// Aim Offset
	{
		const FRotator CameraRotation = Owner->GetControlRotation();
		const FRotator ActorRotation = Owner->GetActorRotation();
		const FRotator DeltaRotation = UKismetMathLibrary::NormalizedDeltaRotator(CameraRotation, ActorRotation);

		Yaw = DeltaRotation.Yaw;
		Pitch = DeltaRotation.Pitch;
	}

	// Rotate Character
	{
		if (Yaw <= -85.0f && Speed <= 0.1f)
			bTurnLeft = true;

		if (Yaw >= +85.0f && Speed <= 0.1f)
			bTurnRight = true;

		if (bTurnLeft || bTurnRight)
		{
			Movement->bUseControllerDesiredRotation = true;
			Movement->bOrientRotationToMovement = false;
		}

		if (UKismetMathLibrary::NearlyEqual_FloatFloat(Yaw, 0.0f))
		{
			bTurnLeft = bTurnRight = false;
			Movement->bUseControllerDesiredRotation = false;
			Movement->bOrientRotationToMovement = true;
		}
	}
}

 

 

여기에서, CameraRotation과 ActorRoation의 차이값을 -180 ~ 180사이로 정규화하여 카메라의 회전값과 캐릭터의 회전값의 차이를 구해서, 해당 값을 AimOffset에 반영하게 된다.

 

그렇게 하여, 현재 캐릭터의 회전값에 상체의 무기 Roation을 반영하여 마우스의 바라보는 방향과 무기의 직선방향을 일치시킨다.

 

C++에서 구현한 Yaw와 Pitch를 ABP에서 가져와 사용해주면 된다.


테스트


무기스왑 을 구현하자.

 

전에 작성하였던 코드 중, ChangeWeapon코드를 활용하여 작성한다.

void ABaseCharacter::ChangeWeapon(EAttachType InHolderType)
{
	if (InHolderType == EAttachType::Handle) return;
	
	Swap<FWeaponPair>
		(
			EquippedWeapon,
			AttachmentComponent->GetBackpack()->GetWeaponHolderRef(InHolderType)
		);

	EquippedWeapon.Weapon->ConversionItem(EItemPositionType::Equipped, EAttachType::Handle, GetMesh());
	auto* Backpack = AttachmentComponent->GetBackpack();
	Backpack->GetWeaponHolderRef(InHolderType).Weapon->ConversionItem(EItemPositionType::Equipped, InHolderType, Backpack->GetMesh());
}

 

입력된 EAttachType을 받아온 뒤, 입력값과 Hand값을 교환하는 코드이다.

 

만약, 입력된 EAttachType이 Nullptr이라면, 해당 ChangeWeapon을 사용하는 코드에서 종료되기 때문에, ChangeWeapon까지 도착하지 않는다.

void AHero::SelectMainHolder()
{
	if (AttachmentComponent->IsBackpackEquipped() == false ||
		AttachmentComponent->GetBackpack()->IsValidMainHolderWeapon() == false) return;
	PlayBehaviorAnimMontage(CharacterPose, EBehavior::Changing, 0);
	ChangeWeapon(EAttachType::MainHolder);
}

void AHero::SelectSubHolder()
{
	if (AttachmentComponent->IsBackpackEquipped() == false ||
		AttachmentComponent->GetBackpack()->IsValidSubHolderWeapon() == false) return;
	PlayBehaviorAnimMontage(CharacterPose, EBehavior::Changing, 0);
	ChangeWeapon(EAttachType::SubHolder);

}

무기를 선택 교환하는 함수이다.

 

Nullptr인지 확인하고, 유효하다면 몽타주를 실행하고, 무기를 스왑한다.

void AHero::BindingCombatActions(UEnhancedInputComponent* EnhancedInputComponent)
{
	// Interact
	EnhancedInputComponent->BindAction(*CombatInput.InputActionMap.Find("Interact"), ETriggerEvent::Ongoing, this, &AHero::Interact);
	EnhancedInputComponent->BindAction(*CombatInput.InputActionMap.Find("Interact"), ETriggerEvent::Canceled, this, &AHero::InteractCanceled);

	// Select Holder
	EnhancedInputComponent->BindAction(*CombatInput.InputActionMap.Find("MainHolder"), ETriggerEvent::Triggered, this, &AHero::SelectMainHolder);
	EnhancedInputComponent->BindAction(*CombatInput.InputActionMap.Find("SubHolder"),  ETriggerEvent::Triggered, this, &AHero::SelectSubHolder);
}

이러한 무기 스왑 함수를 실행하는 키 인풋값은 BindACtion을 통해, Mapping하여, 실행할 수 있도록 한다.


몽타주 기입

 

애니메이션 몽타주를 집어넣어야 몽타주가 실행될 것 이다. 무기스왑을 진행하는 몽타주를 기입하자.

전에 만들어넣어 놓았던, Map배열에 몽타주를 집어넣어준다.

몽타주 또한 IK를 활용해야 함으로, IK의 커브값을 집어넣어주자.

위와같이 IK_PickUp으로 정의하고, 이동하면서 스왑해야 함으로, UpperBody를 넣어서 상체만 움직일 수 있도록 해준다.


테스트

 

 

728x90