Unreal5 Timeline

Unreal5 Timeline

Unreal5 Timeline에 대한 글입니다.

타임라인을 사용하여 시간 기반 애니메이션을 만들 수 있습니다.이 글에서는 Timeline을 이용해 카메라 확장과 축소를 구현합니다.
  • 타임라인을 사용하여 구현한 카메라 확장과 축소.
  • 블루프린트로 구현하기.
  • C++을 이용해 구현하기.

타임라인을 사용하여 구현한 카메라 확장과 축소.

이런 식으로 앉았을 때 카메라가 멀어지고 일어서면 가까워지는 카메라를 Timeline을 사용해 만들었습니다.

파일 크기때문에 프레임 좀 줄였더니 뚝뚝 끊기는군요...

https://docs.unrealengine.com/5.0/ko/timelines-in-unreal-engine/

블루프린트.

블루프린트로는 이렇게 구현했습니다. 

특정 키 입력을 받으면 작동합니다.

이후 키를 다시 누르면 원래대로 돌아갑니다.

timeline(CrouchCamDistance)에 들어가는 커브는 간단하게 0.0, 1.0으로 설정했습니다.

 요약하면 특정키를 누르면 카메라가 550까지 부드럽게 멀어졌다가 특정키에서 손을 떼면 다시 400으로 돌아오는 간단한 코드입니다.

C++

C++코드도 짧으니까 그냥 긁어서 올리겠습니다.

이름은 BlackCharacter.h인데 ThirdPersonCharacter 예제에 Timeline만 추가한 것으로 생각하시면 됩니다.

기능도 위에 블루프린트로 만든거랑 거의 같고 주석도 있기 때문에 자세한 설명은 하지 않겠습니다.

BlackCharacter.h 코드.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
/*input*/
#include "InputActionValue.h"
/*Timeline*/
#include "Components/TimelineComponent.h"
#include "BlackCharacter.generated.h"


class UInputMappingContext;
class UInputAction;
class USpringArmComponent;
class UCameraComponent;

UCLASS()
class RPG_BLACK_API ABlackCharacter : public ACharacter
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;
	/*CallBack Input*/
	void Move(const FInputActionValue& value);
	void Look(const FInputActionValue& value);
	void Jump()override;
	void StartCrouch();
	void StopCrouch();
	/*Crouch*/
	UPROPERTY(blueprintReadOnly, Category = Movement)
	bool IsCrouching;
	/*Input Action*/
	UPROPERTY(EditAnywhere, Category = Input)
	UInputMappingContext* BlackContext;
	UPROPERTY(EditAnywhere, Category = Input)
	UInputAction* MovementAction;
	UPROPERTY(EditAnywhere, Category = Input)
	UInputAction* LookAction;
	UPROPERTY(EditAnywhere, Category = Input)
	UInputAction* JumpAction;
	UPROPERTY(EditAnywhere, Category = Input)
	UInputAction* CrouchAction;

	/*Timeline*/
	UPROPERTY(EditAnywhere, Category = TImeline)
	class UCurveFloat* TestCurve;
	FTimeline TestTimeline;
	void BindTestTimeline();
	UFUNCTION()
	void callBackFun(float Value);
	UFUNCTION(Exec)
	void TestPlay();
private:
	/*Character Component*/
	UPROPERTY(VisibleAnywhere)
	USpringArmComponent* CameraBoom;
	UPROPERTY(VisibleAnywhere)
	UCameraComponent* ViewCamera;
public:	
	ABlackCharacter();
	virtual void Tick(float DeltaTime) override;
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	FORCEINLINE bool GetIsCrouching() const { return IsCrouching; }
};

BlackCharacter.cpp 코드입니다.

#include "Character/BlackCharacter.h"
/*Input*/
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
/*Rotate*/
#include "GameFramework/CharacterMovementComponent.h"
/*SpringArm*/
#include "GameFramework/SpringArmComponent.h"
/*Camera*/
#include "Camera/CameraComponent.h"

// Sets default values
ABlackCharacter::ABlackCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	//bOrientRotationToMovement 는 현재 캐릭터가 가속값을 가지고 있다면 현재 가속되고있는값 방향으로 캐릭터 메쉬를 회전시켜줍니다.
	GetCharacterMovement()->bOrientRotationToMovement = true;
	/*spring arm*/
	//#include "GameFramework/SpringArmComponent.h"필요
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(GetRootComponent());
	CameraBoom->TargetArmLength = 400.f;
	CameraBoom->bUsePawnControlRotation = true;
	/*CharacterRotate*/
	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;
	bUseControllerRotationYaw = false;
	/*Camera*/
	ViewCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ViewCamera"));
	ViewCamera->SetupAttachment(CameraBoom);
	ViewCamera->SetRelativeLocation(FVector(0, 0, 20));

	IsCrouching = false;
}

// Called when the game starts or when spawned
void ABlackCharacter::BeginPlay()
{
	Super::BeginPlay();
	if (APlayerController* PlayerController = Cast<APlayerController>(GetController())) {
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) {
			Subsystem->AddMappingContext(BlackContext, 0);
		}
	}
	BindTestTimeline();
}

void ABlackCharacter::Move(const FInputActionValue& value)
{
	const FVector2D MovementVector = value.Get<FVector2D>();
	const FRotator Rotation = Controller->GetControlRotation();
	const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
	const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	AddMovementInput(ForwardDirection, MovementVector.Y);
	const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	AddMovementInput(RightDirection, MovementVector.X);
}

void ABlackCharacter::Look(const FInputActionValue& value)
{
	const FVector2D LookVector = value.Get<FVector2D>();
	AddControllerYawInput(LookVector.X);//도리도리
	AddControllerPitchInput(LookVector.Y);//끄덕끄덕
}

void ABlackCharacter::Jump()
{
	Super::Jump();
}

void ABlackCharacter::StartCrouch()
{
	IsCrouching = true;
	GetCharacterMovement()->MaxWalkSpeed = 350.f;
	//CameraBoom->TargetArmLength = 500.f;
	TestTimeline.PlayFromStart();
}

void ABlackCharacter::StopCrouch()
{
	IsCrouching = false;
	GetCharacterMovement()->MaxWalkSpeed = 500.f;
	//CameraBoom->TargetArmLength = 400.f;
	TestTimeline.ReverseFromEnd();//손을 떼면 반대로.
}

void ABlackCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	TestTimeline.TickTimeline(DeltaTime);
}

void ABlackCharacter::BindTestTimeline()
{
	if (TestCurve) {
		FOnTimelineFloatStatic TestCallBack;
		FOnTimelineEventStatic TimelineFinishedCallback;
		TestCallBack.BindUFunction(this, "callBackFun");
		TestTimeline.AddInterpFloat(TestCurve, TestCallBack);
		TestTimeline.SetTimelineFinishedFunc(TimelineFinishedCallback);
	}
}
void ABlackCharacter::callBackFun(float Value)
{
	//GEngine->AddOnScreenDebugMessage(-1, 3, FColor::Green, FString::SanitizeFloat(FMath::Lerp(400, 550, Value)));
	CameraBoom->TargetArmLength = FMath::Lerp(400, 550, Value);
}
void ABlackCharacter::TestPlay()
{
	TestTimeline.PlayFromStart();
}

void ABlackCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
		EnhancedInputComponent->BindAction(MovementAction, ETriggerEvent::Triggered, this, &ABlackCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ABlackCharacter::Look);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ABlackCharacter::Jump);
		EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Started, this, &ABlackCharacter::StartCrouch);
		EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &ABlackCharacter::StopCrouch);
	}
}

Curve파일 및 0.0 , 1.0설정.

생성한 Curve파일을 넣고 사용하니 거의 동일하게 작동했습니다.

Designed by JB FACTORY