본문 바로가기

인생언리얼TPS

3.2 애니메이션과 총위치 동기화(Socket), 발사 반동화면 만들기

Snimper Gun은 Mesh의 자식으로 붙어 있지만 총의 애니메이션과 동기화 되지 않습니다.

이유는 Skeletal Mesh는 많은 뼈로 이루어져 있어 Socket을 사용해 지정하지 않으면 안됩니다. 

SK_Mannequin을 열고 Skeletal Tree 중간쯤 hand_r이라는 뼈가 있고 우클릭후 Add Socket을 클릭하면 hand_rSocket이 생성됩니다.

hand_rSocket을 카피해서 gunMeshComp와 sniperGunComp->SetupAttachment에 넣어줍니다. 그래도 위치나 방향에 오차가 있는데 Detaildㅢ Pase Anims를 켜고 맞추서 값을 얻을수 있습니다.

gunMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("GunMeshComp"));
// 4-1. 부모 컴포넌트를 Mesh 컴포넌트로 설정
gunMeshComp->SetupAttachment(GetMesh(), TEXT("hand_rSocket"));
// 4-2. 스켈레탈메시 데이터 로드
ConstructorHelpers::FObjectFinder<USkeletalMesh> TempGunMesh(TEXT("SkeletalMesh'/Game/FPWeapon/Mesh/SK_FPGun.SK_FPGun'"));
// 4-3. 데이터로드가 성공했다면
if (TempGunMesh.Succeeded())
{
	// 4-4. 스켈레탈메시 데이터 할당
	gunMeshComp->SetSkeletalMesh(TempGunMesh.Object);
	// 4-5. 위치 조정하기
	gunMeshComp->SetRelativeLocation(FVector(-17, 10, -3));
	// 회전 조정하기
	gunMeshComp->SetRelativeRotation(FRotator(0, 90, 0));
}

// 5. 스나이퍼건 컴포넌트 등록
sniperGunComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SniperGunComp"));
// 5-1. 부모 컴포넌트를 Mesh 컴포넌트로 설정
sniperGunComp->SetupAttachment(GetMesh(), TEXT("hand_rSocket"));
// 5-2. 스태틱메시 데이터 로드
ConstructorHelpers::FObjectFinder<UStaticMesh> TempSniperMesh(TEXT("StaticMesh'/Game/SniperGun/sniper1.sniper1'"));
// 5-3. 데이터로드가 성공했다면
if (TempSniperMesh.Succeeded())
{
	// 5-4. 스태틱메시 데이터 할당
	sniperGunComp->SetStaticMesh(TempSniperMesh.Object);
	// 5-5. 위치 조정하기
	sniperGunComp->SetRelativeLocation(FVector(-42, 7, 1));
	// 회전 조정하기
	sniperGunComp->SetRelativeRotation(FRotator(0, 90, 0));
	// 5-6. 크기 조정하기
	sniperGunComp->SetRelativeScale3D(FVector(0.15f));
}

GunMeshComponent는 SniperGunComp와의 충돌을 막기위해 Collision Preset을 NoCollision으로 바꿔줍니다.

CameraShakeBase를 부모로 하는 블루프린트를 만들고 이름을 BP_CameraShake라고 합니다/

블루프린트를 열고 디테일의 Camera shake Pattern을 Wave Oscillator Camera로 설정합니다. 총을 발사하면 위로 들렸다 내려오는 동작을 표현하는데 적합합니다.

● 웨이브 오실레이터 카메라 셰이크 패턴과 시퀀스 카메라 셰이크 패턴 2가지를 사용할 것이다.

웨이브 오실레이터 카메라 셰이크 패턴은 Sine 그래프에 따른 움직임을 묘사한다. 값이 위아래로 진동한다는 것이다.

그러므로 총을 쏠 때 총이 위로 들렸다가 다시 아래로 내려오도록 반동 처리하는데 좋다.

● 웨이브 오실레이터를 선택하고 옵션을 보면 Location, Rotation 등이 있다. 카메라가 위아래로 회전할 수 있도록 Rotation의 Pitch(Y축 회전)의 값을 바꿔줘야 한다.

● Rotation의 하위 옵션에 회전 면적 배수는 각 회전 속성 Pitch, Yaw, Roll에 있는 진폭(진폭의 크기)에 곱해지는 값이다. 두 값이 곱해져서 최종 진폭 값이 전달된다. 이에 따라 위아래 진동 크기가 정해진다.

● 또 Rotation의 하위 옵션인 회전 빈도 배수도 하위 회전축의 주파수(초당 진동수) 값과 곱해져서 최종 결과로 전달된다.

● Timing 카테고리는 경과시간(= 지속시간)과 전환되는데 걸리는 시간(블랜드 인/ 블랜드 아웃)을 갖고 있다.

다음과 같이 설정합니다. Pitch만 사용하기때문에 Yaw와 Roll은 0으로 처리합니다.

컴파일 저장합니다. 

이렇게 생성한 BP_CameraShake를 C++에서 사용하기 위해서 TPSPlayer.h에 변수를 생성해 준다. 변수로 받을 수 있도록 TSubclassOf<T>를 사용한다.

	UPROPERTY(EditAnywhere, Category = CameraMotion)
	TSubclassOf<class UCameraShakeBase> cameraShake;

BP_TPSPlayer.cpp InpuFire()부분에 Controller를 통해 CameraShake를 컨트롤해줍니다.

우선, TPSPlayer를 제어하고 있는 APlayerController 인스턴스를 GetFirstPlayerController() 함수로 가져온다.

그리고 APlayerController에서 PlayerCameraManager 멤버 변수를 가져온다. 가져온 APlayerCameraManager 클래스는 플레이어가 갖고 있는 카메라를 관리하는 역할을 한다. 이 클래스의 StartCameraShake() 함수에 위에서 만든 cameraShake를 매개변수로 넘기면 된다.

그러면 총을 쏠 때마다 화면이 흔들리는 것을 확인할 수 있다.

● 다음은 시퀀스 카메라 셰이크 패턴을 사용해서 화면을 흔드는 것도 확인해 보겠다. 이는 시퀀스를 이용하는 방법으로 애니메이터가 직접 자신이 원하는 효과를 포현하는 데 좋다. 방금 한 웨이브 오실레이터 패턴의 경우는 세부적인 표현을 애니메이터가 수정하기 어려우므로 시퀀스 카메라 셰이크 패턴이 대안이 될 수 있다.

void ATPSPlayer::InputFire(const FInputActionValue& Value)
{
	//카메라셰이크 재생
	auto controller = GetWorld()->GetFirstPlayerController();
	controller->PlayerCameraManager->StartCameraShake(cameraShake);
	//공격 애니메이션 재생
	auto anim = Cast<UPlayerAnim>(GetMesh()->GetAnimInstance());
	anim->PlayAttackAnim();

BP_TPSPlayer 블루프린트 디테일에서 shake를 검색해 연결해 줍니다.

플레이해보면 발사시 땅이 흔들립니다.

 

이제 Camera Shake방법을 Sequence Camera Shake Pattern으로 바꾸어 보겠습니다.

Camera animation sequence를 파일 형태로 존재합니다. Sequence드롭다운메뉴를 눌러 선택하면

Save as창이 나타나는데 CS_FireShake로 저장합니다. Sequence 드롭다운메뉴에 자동으로 설정되어 있습니다.

에디터가 열리면 +를 눌러 Transform을 추가하고

Transform을 아래로 열어Ctrl+wheel로 timeline을 확대해서

Rotation Pitch에 +를 4번 눌러 키프레임 4개를 넣어주고 끌어다 0,1,2,3 프레임에 키프레임을에 배치합니다.. Pitch값이 0, 2, -1, 0으로 변화하게 해줍니다.

저장후 잘 지정되어 있는지 확인해주세요

플레이하면 화면이 위아래로 흔들립니다.