Unreal만의 데이터 타입 2 (스마트 포인터 | 수학 | 시간 | 색상 | UObject)

김여울·2025년 7월 14일
0

내일배움캠프

목록 보기
43/111

📍 2025. 07. 08 수업

Unreal만의 데이터 타입

4. 스마트 포인터 타입

  1. UObject 기반 클래스일 경우
  • 언리얼이 GC(가비지 컬렉션)이 자동으로 관리해줌
    → 스마트 포인터 쓸 필요 없음
    가비지 컬렉션 쓰지 않는 메모리를 자동으로 정리해주는 시스템
  • 대신 UPROPERTY() 로 참조를 관리해야 해야 GC가 추적함
  1. 순수 C++ 클래스 (UObject 상속 안 함)
  • GC 대상 아님
  • 메모리 누수 방지를 위해 스마트 포인터 사용 권장
    • TSharedPtr / TSharedRef : 참조 카운팅 기반 공유
    • TUniquePtr : 유일 소유권 (RAII)

4.1 TSharedPtr

a. 특징

  • 여러 개의 포인터가 하나의 객체를 공유할 수 있는 스마트 포인터
  • 내부적으로 참조 카운트(reference count) 를 사용해
    누가 이 객체를 참조 중인지 추적함
  • 참조 중인 포인터가 전부 사라지면 (참조 카운트 = 0)
  • 객체 생성은 MakeShared<T>() 사용
// 구조체 생성
struct FMyData
{
	int32 Value;

	FMyData(int32 InValue) : Value(InValue) {}

	void Print() const
	{
		UE_LOG(LogTemp, Warning, TEXT("Value: %d"), Value);
	}
};

// 객체를 생성하고 TSharedPtr로 관리
TSharedPtr<FMyData> SharedInt = MakeShared<FMyData>(10);

// SharedInt가 가리키는 객체를 AnotherSharedInt도 같이 가리킴 (포인터 공유, 복사 아님!)
TSharedPtr<FMyData> AnotherSharedInt = SharedInt;

// 값 확인 (같은 객체니까 동일한 값 출력)
SharedInt->Print();           // 출력: Value: 10
AnotherSharedInt->Print();    // 출력: Value: 10

// 값 변경해보면?
SharedInt->Value = 42;
AnotherSharedInt->Print();    // 출력: Value: 42 (같은 객체를 참조하니까 같은 값 출력)

/*
  📌 정리
  - SharedInt와 AnotherSharedInt는 같은 FMyData 객체를 가리킴
  - 복사가 아니라 "공유"임 (참조 카운트만 +1 되는 것)
*/

// SharedInt 해제
SharedInt = nullptr;  // 객체는 아직 살아 있음 (AnotherSharedInt가 참조 중)

// 마지막 참조 해제
AnotherSharedInt = nullptr;  // 여기서 객체 메모리 자동 삭제됨!

b. 주의

  • 순환 참조(Circular Reference)
    서로가 서로를 TSharedPtr로 참조하면 참조 카운트가 0이 되지 않음
    → 객체가 삭제되지 않음 (메모리 누수)

  • 해결법
    한 쪽은 TWeakPtr로 만들어서 참조 카운트에 포함되지 않게 함

// 구조체
struct FChild;

struct FParent
{
	TSharedPtr<FChild> Child;
};

struct FChild
{
	TSharedPtr<FParent> Parent;
};

// TSharedPtr
TSharedPtr<FParent> MyParent = MakeShared<FParent>();
TSharedPtr<FChild> MyChild = MakeShared<FChild>();

MyParent->Child = MyChild;
MyChild->Parent = MyParent;  // 순환 참조 발생

// 해결법 : TWeakPtr
// TWeakPtr는 참조 카운트에 영향을 안 줌
// TSharedPtr가 없어지면 자동으로 무효화됨
struct FChild
{
	TWeakPtr<FParent> Parent;  // 약한 참조로 변경
};

4.2 TWeakPtr

  • TSharedPtr의 약한 참조 버전
  • 참조 카운트에 영향을 주지 않음
  • 주로 순환 참조 방지용으로 사용됨
    (예) 서로 참조하는 구조에서 한 쪽만 TWeakPtr
// 순환 참조 방지 예시
// 부모와 자식이 서로 참조하는 구조
struct FChild;

struct FParent
{
	TSharedPtr<FChild> Child;
};

struct FChild
{
	TWeakPtr<FParent> Parent; // <- 여기만 TWeakPtr로!
};


// 부모와 자식이 서로 참조하는 구조
struct FChild;

struct FParent
{
	TSharedPtr<FChild> Child;
};

struct FChild
{
	TWeakPtr<FParent> Parent; // <- 여기만 TWeakPtr로!
};

4.3 TUniquePtr

  • 소유권이 오직 하나만 존재함
  • 다른 곳에 넘기려면 move 연산자로 소유권 이전
  • 언리얼에서는 MoveTemp() 사용

* 언리얼 스마트 포인터 & 메모리 관리 방식 비교

타입/기반소유권참조 카운트자동 삭제사용 용도
TSharedPtr공유 소유있음마지막 참조 해제 시 삭제여러 객체가 같은 데이터를 공유할 때 사용
TWeakPtr소유 안 함없음자동 삭제 없음순환 참조 방지용. TSharedPtr를 감시만 할 때 사용
TUniquePtr단독 소유없음소유자 소멸 시 삭제소유권이 한 군데만 필요할 때. MoveTemp()로 소유권 이동해야 함
UObject 기반 클래스사용-GC가 자동 관리가비지 컬렉션이 관리하므로 스마트 포인터를 쓰지 않음

UObject 기반이 아닌 C++ 객체를 사용할 때만 위 스마트 포인터들을 사용하고
게임 오브젝트, 액터 등은 대부분 UObject 기반이기 때문에 스마트 포인터들을 자주 쓰진 않음

4.4 TSharedRef

  • null이 될 수 없는 TSharedPtr
    얘는 null이 안 되는 안전한 포인터 비슷한 애
    사실은 참조(Reference) 타입
  • 생성할 때부터 무조건 유효한 객체를 참조해야 함
// 유효한 객체를 생성해서 TSharedRef에 담기
TSharedRef<FMyData> MyRef = MakeShared<FMyData>(100);

// 바로 사용 가능 (null 체크 필요 없음)
MyRef->Print();  // 출력: Value: 100

↓↓↓ 여기 아래부터는 언리얼에서 쓰이는 포인터 ↓↓↓
💡 전부 UObject 기반이라 GC에 안전함

4.5 TWeakObjectPtr

  • UObject에 대한 약한 참조 포인터
  • GC가 객체를 파괴했는지 확인할 수 있음 (IsValid() 로 체크)
    → UObject가 파괴되면 TWeakObjectPtr 는 자동으로 null로 변함
  • 순환 참조 방지용으로 자주 사용됨

4.6 TSoftObjectPtr

  • 리소스를 지연 로드할 때 사용 (에디터 & 런타임 모두)
  • 실제 객체를 들고 있는 게 아니고 에셋 경로를 기억함
    • 경로만 기억했다가 나중에 쓸 때 로딩 → 메모리 절약
  • 필요할 때 LoadSynchronous() 로 로딩

4.7 TSoftClassPtr

  • TSoftObjectPtr` 의 클래스 전용 버전
  • UClass 타입을 경로로 저장하고, 나중에 로드
  • 블루프린트 클래스 참조 등에 자주 사용됨
    (예) SpawnActor용

* UObject 전용 포인터 정리

타입용도특징GC 안전 여부
TWeakObjectPtrUObject에 대한 약한 참조IsValid()로 GC에 의해 파괴됐는지 확인 가능안전함
TSoftObjectPtr리소스를 지연 로드할 때 사용에셋의 경로를 저장, 필요할 때 로드안전함
TSoftClassPtrUClass를 지연 로드할 때 사용블루프린트 클래스 등 클래스 경로 저장안전함

5. 수학 관련 타입

순수 C++이든 USTRUCT()든, 언리얼에서 구조체면 접두사로 F 를 붙여주자

5.1 FVector

⚠️ C++ STL의 vector랑 완전 다름

  • 위치, 방향, 속도 등을 표현하는 3차원(3D) 벡터 타입
  • double 값 3개 (X, Y, Z)로 구성됨

- Vector vs Scalar

  • 벡터 : 방향이 있는 화살표
    → 크기 + 방향을 가진 물리량
    (예) 위치, 속도, 힘
  • 스칼라 : 방향 없는 숫자값
    → 크기만 있는 실수
    (예) 속력, 온도, 질량
// 구조체 정의
struct FVector
{
	double X, Y, Z;

	FVector(double InX, double InY, double InZ)
		: X(InX), Y(InY), Z(InZ)
	{ }
};

// 사용 예
void TestVector()
{
	// 생성자 방식
	FVector Location(100.0, 50.0, 0.0);
    // 아니면 이렇게 명시적으로
	FVector Location = FVector(100.0, 50.0, 0.0);

	// 위치 출력
	printf("X: %.1f, Y: %.1f, Z: %.1f\n", Location.X, Location.Y, Location.Z);
}

5.2 FVector2D

  • double 값 2개 (X, Y)로 구성된 2차원(2D) 벡터 타입
  • UI 위치, 화면 좌표, 평면 계산 등에서 위젯의 위치·오프셋·정렬 같은 2D 위치 조절에 자주 사용됨
// 2D 벡터 생성
FVector2D ScreenPos(100.0, 50.0);

// 값 출력
printf("X: %.1f, Y: %.1f\n", ScreenPos.X, ScreenPos.Y);

5.3 FRotator

  • 회전(각도) 표현용 타입 (Pitch, Yaw, Roll)
  • float 기반 타입
  • Gimbal Lock 현상 발생할 수 있으니 FQuat (쿼터니언) 사용하기
FRotator Rotation(0.0, 90.0, 0.0); // Pitch, Yaw, Roll
Actor->SetActorRotation(Rotation);

5.4 FQuat

  • Quaternion(사원수) 기반 회전 표현 (X, Y, Z, W)
  • Gimbal Lock 없이 회전 계산 가능 (FRotator 보다 안정적)
    Gimbal Lock 회전 축 두 개가 겹쳐서 회전 축 하나가 사라지는 현상
    → 회전 축이 꼬여서 제대로 못 도는 상태
  • 내부 회전 연산이나 슬쩍 회전시킬 때 유용함
  • 언리얼 엔진에서는 외부엔 Rotator로 보여주고, 내부는 Quat으로 처리함
    → 자동으로 변환되니까 짐벌락 현상 신경쓰지 않아도 됨
struct FQuat
{
	float X;
	float Y;
	float Z;
	float W;
};


FQuat Quat = FQuat(FVector(0, 0, 1), FMath::DegreesToRadians(90)); // Z축 90도 회전

5.5 FTransform

위치(FVector), 회전(FQuat), 스케일(FVector) 정보를 모두 가진 타입

FTransform Transform;

// 1. 위치 설정 (X축 100 이동)
Transform.SetLocation(FVector(100.0f, 0.0f, 0.0f));

// 2. 회전 설정 (Yaw 90도)
// → Euler 각을 쿼터니언으로 변환해서 회전 적용
Transform.SetRotation(FQuat::MakeFromEuler(FVector(0.0f, 90.0f, 0.0f)));

// 3. 스케일 설정 (기본 크기)
Transform.SetScale3D(FVector(1.0f, 1.0f, 1.0f));

5.6 FMatrix

  • 4x4 행렬 구조체
  • 내부적으로 FTransform이 FMatrix로 변환돼서 연산됨
  • 직접 다룰 일은 많지 않음

5.7 FBox, FSphere

  • 범위(Bounds)나 충돌 영역(Collision Volume) 표현용 구조체
  • 보통 AABB(축 정렬 박스) 나 구 형태 영역으로 사용
  • 충돌 검사, 포함 여부 판단, 공간 최적화 등에 활용
// IsInside() : 
// FBox나 FSphere 안에 어떤 위치(FVector)가 포함돼 있는지 확인하는 함수

// FBox: 최소/최대 좌표로 영역 정의
FBox Box(FVector(0, 0, 0), FVector(100, 100, 100));
bool bInside = Box.IsInside(FVector(50, 50, 50)); // true

// FSphere: 중심 + 반지름
FSphere Sphere(FVector(0, 0, 0), 100.0f);
bool bInSphere = Sphere.IsInside(FVector(0, 0, 50)); // true

6. 시간 관련 타입

6.1 FDateTime

  • 날짜 + 시간 전체를 표현하는 타입
  • 연, 월, 일, 시, 분, 초 등 포함
  • 현재 시간 구하기: FDateTime::Now()
// 현재 시간 구하기
FDateTime Now = FDateTime::Now(); 
UE_LOG(LogTemp, Warning, TEXT("현재 시각: %s"), *Now.ToString());

// 특정 시간 정하기
FDateTime SpecificDate(2025, 7, 8, 10, 0, 0); // 2025-07-08 10:00:00
UE_LOG(LogTemp, Warning, TEXT("특정 날짜: %s"), *SpecificDate.ToString());

  • 게임에서 이벤트 시간, 기간 체크, 제한 시간 설정 등에 잘 쓰임
// 이벤트 시작/종료 시간 체크
FDateTime Now = FDateTime::Now();
FDateTime EventStart(2025, 7, 14, 12, 0, 0);
FDateTime EventEnd(2025, 7, 15, 12, 0, 0);

if (Now >= EventStart && Now <= EventEnd)
{
    UE_LOG(LogTemp, Warning, TEXT("이벤트 중입니다!"));
}

6.2 FTimespan

  • 두 시각(FDateTime) 간의 간격(시간차)를 나타냄
  • 시간, 분, 초, 밀리초 등으로 나눠서 계산 가능
  • 보상 쿨타임, 경과 시간, 남은 시간 등에 사용됨
// 이벤트 종료까지 남은 시간
FDateTime Now = FDateTime::Now();
FDateTime EndTime(2025, 7, 20, 12, 0, 0);

// 시간 차이 계산
FTimespan Difference = EndTime - Now;

if (Difference.GetTotalSeconds() > 0)
{
	int32 Days    = Difference.GetDays();
	int32 Hours   = Difference.GetHours();
	int32 Minutes = Difference.GetMinutes();

	UE_LOG(LogTemp, Warning, TEXT("이벤트 종료까지 %d일 %d시간 %d분 남음!"), Days, Hours, Minutes);
}
else
{
	UE_LOG(LogTemp, Warning, TEXT("이벤트가 종료되었습니다."));
}

6.3 FTimerHandle

a. 개념 및 역할

  • 핸들(handle) 어떤 객체나 리소스를 간접적으로 관리하기 위한 참조값
  • 타이머 자체를 직접 조작하는 게 아닌 그걸 식별하고 제어할 수 있는 값
  • 일정 시간 후, 일시적 또는 반복적으로 함수를 호출할 때 사용

b. 설정 방법

  • SetTimer() → 타이머 설정 (몇 초 뒤에 어떤 함수 실행할지)
  • ClearTimer() → 타이머 취소 (중간에 멈추고 싶을 때)
  • 마지막 인자 true / false → 반복 실행 여부 설정
// 1초마다 반복 실행
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AMyActor::TickFunction, 1.0f, true);

// 타이머 멈추기
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);

- AActor 안에서 GetWorld() 생략 가능?

  • AActor 안에서는 GetWorld() 없이도 GetWorldTimerManager()를 바로 사용할 수 있음
    → 내부에 편의 함수(wrap) 가 정의돼 있어서 자동으로 GetWorld()를 호출해줌
  • 다른 클래스에선 생략 불가 (직접 GetWorld() 써야 함)
// 둘 다 가능!
GetWorld()->GetTimerManager().SetTimer(...);
GetWorldTimerManager().SetTimer(...); // AActor 안이라 가능

protected:
    virtual void BeginPlay() override
    {
        Super::BeginPlay();

        // 2초 뒤에 PrintHello() 함수를 한 번만 실행하도록 타이머를 설정
        GetWorldTimerManager().SetTimer(TimerHandle, this, &ATimer::PrintHello, 2.0f, false);
    }

c. SetTimer() 인자 해석

부분의미
GetWorldTimerManager()타이머를 관리하는 엔진 객체 가져오기
.SetTimer(...)타이머 설정 함수
TimerHandle이 타이머를 제어할 수 있는 핸들(참조값)
this이 객체(보통 Actor) 기준으로 실행함
&ATimer::PrintHello2초 후 실행할 함수 지정
2.0f2초 후 실행
false반복 ❌ → 한 번만 실행

7. 색상 표현 타입

7.1 FColor

  • 정수 기반(0~255) (RGBA)의 색상 표현 (Red, Green, Blue, Alpha)
  • 주로 디버그 렌더링이나 압축된 색상 데이터(UI, 텍스처, HUD 등) 처리에 사용
  • (예) FColor::Red, FColor(255, 128, 0, 255)

7.2 FLinearColor

  • 실수 float 기반(0.0~1.0) (RGBA)의 색상 표현 (HDR, 감마 보정 가능)
  • 주로 렌더링, 머티리얼, 라이트 계산 등에 사용
  • 근데 보통 블루프린트에서 작업하기 때문에 쓸 일 많이 없음
  • (예) FLinearColor::Blue, FLinearColor(1.0f, 0.5f, 0.0f, 1.0f)

8. UObject 타입 타입

8.1 UObject

  • 모든 언리얼 클래스의 최상위 부모 클래스
  • 기본적인 가비지 컬렉션, 리플렉션, 직렬화 기능 제공
  • 게임 내 동작 ❌, 배치 ❌, 씬에 존재 ❌
    → 위젯, 데이터 자산 등 비시각적 용도

8.2 AActor

  • World에 배치될 수 있는 모든 객체의 부모 클래스
    → World에 배치하고 싶을 땐 Actor를 상속 받기
  • Transform (위치, 회전, 스케일) 을 가짐
  • Tick(), BeginPlay(), EndPlay() 등을 오버라이드 가능
  • 씬에 배치 가능, 충돌, 렌더링 등 가능
  • (예) 캐릭터, 아이템, 오브젝트

8.3 UActorComponent

  • Actor에 붙어서 동작하는 재사용 가능한 컴포넌트
  • 위치는 없고 로직만 있음
  • 특정 기능을 모듈화할 때 사용
    (예) 체력, 총 쏘기, 전투, 인벤토리, 이동 등

언리얼 공식 문서

📎 언리얼 스마트 포인터 라이브러리

0개의 댓글