레벨디자인
오늘은 월드에 배치된 액터에 움직임을 부여해서 '폴가이즈' 게임처럼 장애물 요소로 만들어볼 것이다.
어떻게 하면 액터가 정해진 길이만큼 정해진 속도로 움직이고, 최대 이동 거리를 설정해 그 거리만큼 움직이면 다시 원래 초기 위치 값으로 되돌아올지 고민해보았다.
MovingPlatform.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MovingPlatfrom.generated.h"
UCLASS()
class UNREALLEARNINGKIT_API AMovingPlatfrom : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMovingPlatfrom();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
UPROPERTY(EditAnywhere, Category = "Moving Platform")
FVector PlatformVelocity = FVector(100, 0, 0);
UPROPERTY(EditAnywhere, Category = "Moving Platform")
float MoveDistance = 100;
UPROPERTY(EditAnywhere, Category = "Rotation")
FRotator RotationVelocity = FRotator(0, 0, 100);
FVector StartLocation;
void MovingPlatform(float DeltaTime); // 실제 플랫폼의 움직임을 구현 함수
void RotatePlatform(float DeltaTime); // 실제 플랫폼의 회전을 구현 함수
bool ShouldPlatformReturn() const; // 플랫폼이 다시 원래의 위치로 되돌아가야하는지 확인하는 함수
float GetDistanceMoved() const; // 움직인 거리를 계산해 가져오는 함수
};
실제 플랫폼의 움직임, 이동과 회전을 구현하는 함수와 플랫폼이 다시 원래 위치로 되돌아가야하는지 판별하는 함수를 구현하였다.
그리고 추가적으로 움직이기 전의 위치값과 이동한 최종 지점의 위치 값의 차이를 구하는 함수를 만들어서, 현재 이동한 거리가 최대이동거리인 MoveDistance를 초과하면 방향을 바꿀 수 있도록 하였다.
PlatformVelocity와 MoveDistance, RotationVelocity는 UPROPERTY 매크로를 사용해 언리얼 엔진의 리플렉션 시스템을 사용하도록 하였다. 이로인해 객체나 변수의 메타데이터를 언리얼에서 읽어들일 수 있게 하였다.
이제 에디터에서 실행해보며 각 플랫폼의 이동과 회전의 방향, 속도를 테스트해보며 월드에 배치된 장애물 요소들의 난이도를 조절할 수 있었다.
MovingPlatform.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MovingPlatfrom.h"
// Sets default values
AMovingPlatfrom::AMovingPlatfrom()
{
// 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;
}
// Called when the game starts or when spawned
void AMovingPlatfrom::BeginPlay()
{
Super::BeginPlay();
StartLocation = GetActorLocation();
}
// Called every frame
void AMovingPlatfrom::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
MovingPlatform(DeltaTime);
RotatePlatform(DeltaTime);
}
void AMovingPlatfrom::MovingPlatform(float DeltaTime)
{
if (ShouldPlatformReturn())
{
// 현재 위치를 시작위치로 변경
FVector MoveDirection = PlatformVelocity.GetSafeNormal();
StartLocation = StartLocation + (MoveDirection * MoveDistance);
SetActorLocation(StartLocation);
// 움직일 방향을 전환
PlatformVelocity = -PlatformVelocity;
}
else
{
FVector CurrentLocation = GetActorLocation();
CurrentLocation = CurrentLocation + (PlatformVelocity * DeltaTime);
SetActorLocation(CurrentLocation);
}
}
void AMovingPlatfrom::RotatePlatform(float DeltaTime)
{
AddActorLocalRotation(RotationVelocity * DeltaTime);
}
bool AMovingPlatfrom::ShouldPlatformReturn() const
{
if (MoveDistance < GetDistanceMoved())
{
return true;
}
return false;
}
float AMovingPlatfrom::GetDistanceMoved() const
{
return FVector::Dist(GetActorLocation(), StartLocation);
}
구현부를 살펴보면 GetDistanceMoved 함수로 현재 액터의 위치에서 시작위치의 차이 값이
ShouldPlatformReturn 함수로 넘어와 if문에서 MoveDistance의 값보다 큰지 여부를 리턴하게 된다.
그 리턴 값에 따라 MovingPlatform 함수에서 움직일 방향을 바꾸어야 할지, 아니면 계속 진행 방향대로 움직이면 될지 결정된다.
false의 값을 받았다면 else문이 실행되는데, 현재의 위치에 PlatformVelocity(속도) * DeltaTime(시간) 을 계산해 거리를 더해주어 현재 위치를 재설정 해주고, 이는 Tick함수에서 계속 호출됨으로 지속적으로 이동하게 한다.
true의 값을 받았다면 위의 if문이 실행되는데 처음에 코드를 작성할 때는
void AMovingPlatfrom::MovingPlatform(float DeltaTime)
{
if (ShouldPlatformReturn())
{
// 현재 위치를 시작위치로 변경
StartLocation = GetActorLocation();
// 움직일 방향을 전환
PlatformVelocity = -PlatformVelocity;
}
}
이렇게 현재의 위치 값을 시작 위치 StartLocation에 바로 넣었는데, 이렇게 해도 정상적으로 작동되지만 Chat gpt에게 더 효율적인 방법이 없을지 물어본 결과,
GetActorLocation()은 플랫폼이 현재 위치에 도달한 시점의 위치를 반환하지만 프레임 스킵이나 DeltaTime 값의 불규칙함으로 인해 부정확한 위치에 있을 수 있다는 의견이 있었다.
if (ShouldPlatformReturn())
{
// 현재 위치를 시작위치로 변경
FVector MoveDirection = PlatformVelocity.GetSafeNormal();
StartLocation = StartLocation + (MoveDirection * MoveDistance);
SetActorLocation(StartLocation);
// 움직일 방향을 전환
PlatformVelocity = -PlatformVelocity;
}
그래서 PlatformVelocity를 단위 백터로 변환해 움직이는 방향 MoveDirection을 얻고 시작위치에 MoveDirection과 MoveDistance를 곱한 값 즉, 이동한 거리를 더해주어 최종적으로 움직인 거리를 계산해 그것을 시작 위치로 재설정 해주었고 액터의 위치도 해당 위치로 설정해주었다.
이렇게 플랫폼의 위치를 정확하게 계산하면 프레임 스킵이나 DeltaTime 오차 등으로 인해 플랫폼이 부정확한 위치에 놓이는 것을 방지할 수 있고,
추후 복잡한 움직임(가속/감속, 곡선 이동 등 비선형이동)을 구현하려고 계획하고 있을 때 유리하다고 한다.
FVector GetSafeNormal(float Tolerance = SMALL_NUMBER) const;
이 과정에서 새로 알게된 것은 GetSafeNormal 함수이다.
이 함수는 FVector 클래스에서 제공하는 함수이고, 백터의 길이를 1로 정규화한 단위 백터(Unit Vector)로 변환하는데 사용된다.
이 함수는 안정성을 보장한다고 하는데, 일반적인 정규화 과정에서는 길이(크기)가 0인 벡터를 나누면 수학적 오류가 발생한다. 그래서 이 함수는 백터의 길이가 Tolerance(허용 오차) 이하일 경우, (0, 0, 0) 벡터를 반환해서 오류를 피한다.
이러한 안정성 때문에 Safe라는 이름이 붙은 것이다.
활용 사례: 방향 계산, 움직임이나 힘의 방향을 계산할 때
마지막으로 위에 내가 만든 MovingPlatform 클래스로 여러 하위 블루프린트 클래스들을 생성해, 내가 커스텀한 기능들로 레벨을 만들어보았다.
언리얼 5.5 빌드 오류
언리얼 5.5 버전 빌드시에 오류가 발생했는데 원인을 몰라 한참 헤매다가 김하연 튜터님께 도움을 요청했다.
현재 5.5 버전에서 특이하게 발생하고 있는 빌드 오류라고... 바로 알아보셨다.
윈도우 기준으로 언리얼 5.5버전이 설치되어있는 경로로 가서 Engin폴더 우클릭을 해보면 '읽기 전용'이 체크되어있는데 그것을 해제하고 '적용'해주면 놀랍게도 바로 빌드가 성공적으로 되었다.
'내배캠 > TIL' 카테고리의 다른 글
24.12.19 (목) (0) | 2024.12.19 |
---|---|
24.12.18 (수) (0) | 2024.12.18 |
24.12.16 (월) (0) | 2024.12.16 |
24.12.10 (화) (1) | 2024.12.10 |
24.12.09 (월) (0) | 2024.12.09 |