[UE4] 선형보간법(Linear Interpolation)을 이용하여 움직이는 객체를 만들자!
안녕하세요! 이번에 노트북 새로 장만한 기념으로 언리얼엔진을 공부하고있습니다.
앞으로는 자체엔진공부 & 언리얼엔진공부를 집중적으로 해보려구요!!
[UE4] 선형보간법(Linear Interpolation)을 이용하여 움직이는 객체를 만들자!
선형 보간법(線型補間法, linear interpolation)은 끝점의 값이 주어졌을 때 그 사이에 위치한 값을 추정하기 위하여 직선 거리에 따라 선형적으로 계산하는 방법입니다.
만약에 이러한 상황이 있다고 가정해봅시다.
어떠한 정점A를 (10, 5)이라고 하고 어떠한 정점 B를 (-1, 0)라고 했을때,
일정한 속도로 A에서 B로가는데 5초가 걸렸다고 해봅시다.
그러면 1.7초가 걸렸을때는 어디에 위치할까요?
흠...
아무래도 선형보간법을 사용하지않고 암산으로 풀기에는 한계가 있죠.
선형보간법은 이러한때에 사용할 수 있습니다.
우선 선형보간법을 실제 게임에 적용하기 위해서는 선형보간법이 어떻게 구현되는지 알아야합니다.
이 부분은 저가 설명하는것보다 더 정확한 자료가 나와있으니 참고해주시기 바랍니다.
C++
#include <iostream>
struct Matrix {
float x, y, z;
};
float lerp(float p1, float p2, float d1) {
return (((1-d1) * p1) + (d1 * p2));
}
int main() {
Matrix v0, v1;
float t;
v0 = { 10, 5, 0 };
v1 = { -1, 0, 0 };
t = 1.7f / 5.0f;
float result_x = lerp(v0.x, v1.x, t);
float result_y = lerp(v0.y, v1.y, t);
float result_z = lerp(v0.z, v1.z, t);
std::cout << "x: " << result_x << std::endl;
std::cout << "y: " << result_y << std::endl;
std::cout << "z: " << result_z << std::endl;
}
그렇습니다..
1.7초가 지났을때는 (6.26, 3.3)의 위치에 가게 된다고합니다.
실제 게임에 적용하기
실제 게임에 적용하기 위해서 저는 언리얼엔진을 사용하겠습니다.
(빈 프로젝트) + (C++) + (추가 콘텐츠 포함) 을 선택합니다.
우선 저는 UStaticMeshComponent를 이용해서 액터를 하나 만들고 해당 액터에 선형보간법을 적용하여, 초단위의 특정주기로 오브젝트를 움직이는 예제를 만들어보았습니다.
AMovingBox.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include <Engine/Classes/Components/StaticMeshComponent.h>
#include "MovingBox.generated.h"
UCLASS()
class MYPROJECT003_API AMovingBox : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMovingBox();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
bool isOpen;
UPROPERTY(EditAnywhere)
float waitTime;
float timeDeltaTime;
float fixedZ;
FTimerHandle fTimeHandler;
FVector fActorLocation;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* uStaticMeshComponent;
void BeginMove();
void EndMove();
};
AMovingBox.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MovingBox.h"
// Sets default values
AMovingBox::AMovingBox()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
uStaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Box"));
RootComponent = uStaticMeshComponent;
waitTime = 0.0f;
isOpen = false;
// Z가 0이면은 오브젝트가 땅에 박히니까 그냥 잘보이도록 기본 z값은 50으로 설정
fixedZ = 50.0f;
}
// Called when the game starts or when spawned
void AMovingBox::BeginPlay()
{
Super::BeginPlay();
// 10을 곱해준 이유는 그냥 (10, 5, fixedZ)로 하면 움직이는 거리가 너무 적어서 잘 안보임
SetActorLocation(FVector(10 * 10, 5 * 10, fixedZ));
fActorLocation = GetActorLocation();
GetWorldTimerManager().SetTimer(fTimeHandler, this, &AMovingBox::BeginMove, 0.03f, true, waitTime);
}
// Called every frame
void AMovingBox::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
timeDeltaTime += DeltaTime;
}
void AMovingBox::BeginMove() {
if (!isOpen) {
isOpen = true;
timeDeltaTime = 0.0f;
}
FVector bActorLocation;
// 선형보간법(Linear Interpolation)
// 10을 곱해준 이유는 그냥 (10, 5, fixedZ)로 하면 움직이는 거리가 너무 적어서 잘 안보임
bActorLocation = fActorLocation +
FVector(FMath::Lerp(10 * 10, -1 * 10, timeDeltaTime),
FMath::Lerp(5 * 10, 0 * 10, timeDeltaTime),
FMath::Lerp(fixedZ, fixedZ, timeDeltaTime));
SetActorLocation(bActorLocation);
// 타이머가 끝나면
if (timeDeltaTime > 1.0f) {
GetWorldTimerManager().ClearTimer(fTimeHandler);
GetWorldTimerManager().SetTimer(fTimeHandler, this, &AMovingBox::EndMove, 0.03f, true, waitTime);
}
}
void AMovingBox::EndMove() {
if (isOpen) {
isOpen = false;
timeDeltaTime = 0.0f;
}
FVector bActorLocation;
// 선형보간법(Linear Interpolation)
// 10을 곱해준 이유는 그냥 (10, 5, fixedZ)로 하면 움직이는 거리가 너무 적어서 잘 안보임
bActorLocation = fActorLocation +
FVector(FMath::Lerp(-1 * 10, 10 * 10, timeDeltaTime),
FMath::Lerp(0 * 10, 5 * 10, timeDeltaTime),
FMath::Lerp(fixedZ, fixedZ, timeDeltaTime));
SetActorLocation(bActorLocation);
// 타이머가 끝나면
if (timeDeltaTime > 1.0f) {
GetWorldTimerManager().ClearTimer(fTimeHandler);
GetWorldTimerManager().SetTimer(fTimeHandler, this, &AMovingBox::BeginMove, 0.03f, true, waitTime);
}
}
우선 저는 위의 예제와 같이 두 A(10, 5), B(-1, 0)의 정점을 사용하였고, 다른점이라고 한다면 Z축을 50으로 설정하였고, 매번 10을 곱한다는점입니다.
우선 언리얼엔진4의 좌표평면은 상향벡터가 z인 xy평면좌표계입니다.
저는 공중에 오브젝트를 띄우는것이 더 잘보일것같아서 50으로 했구요, 10을 곱한것은 곱하지않을때보다 변화가 더 눈에 뜁니다.
실행
와우!! 굿!! 아주 잘됩니다.
그럼 안녕!
댓글