📍 2025. 07. 08 수업
가비지 컬렉션 쓰지 않는 메모리를 자동으로 정리해주는 시스템UPROPERTY() 로 참조를 관리해야 해야 GC가 추적함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; // 여기서 객체 메모리 자동 삭제됨!
순환 참조(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; // 약한 참조로 변경
};
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로!
};
MoveTemp() 사용| 타입/기반 | 소유권 | 참조 카운트 | 자동 삭제 | 사용 용도 |
|---|---|---|---|---|
TSharedPtr | 공유 소유 | 있음 | 마지막 참조 해제 시 삭제 | 여러 객체가 같은 데이터를 공유할 때 사용 |
TWeakPtr | 소유 안 함 | 없음 | 자동 삭제 없음 | 순환 참조 방지용. TSharedPtr를 감시만 할 때 사용 |
TUniquePtr | 단독 소유 | 없음 | 소유자 소멸 시 삭제 | 소유권이 한 군데만 필요할 때. MoveTemp()로 소유권 이동해야 함 |
UObject 기반 클래스 | 사용 | - | GC가 자동 관리 | 가비지 컬렉션이 관리하므로 스마트 포인터를 쓰지 않음 |
UObject 기반이 아닌 C++ 객체를 사용할 때만 위 스마트 포인터들을 사용하고
게임 오브젝트, 액터 등은 대부분 UObject 기반이기 때문에 스마트 포인터들을 자주 쓰진 않음
// 유효한 객체를 생성해서 TSharedRef에 담기
TSharedRef<FMyData> MyRef = MakeShared<FMyData>(100);
// 바로 사용 가능 (null 체크 필요 없음)
MyRef->Print(); // 출력: Value: 100
↓↓↓ 여기 아래부터는 언리얼에서 쓰이는 포인터 ↓↓↓
💡 전부 UObject 기반이라 GC에 안전함
UObject에 대한 약한 참조 포인터IsValid() 로 체크)TWeakObjectPtr 는 자동으로 null로 변함LoadSynchronous() 로 로딩| 타입 | 용도 | 특징 | GC 안전 여부 |
|---|---|---|---|
TWeakObjectPtr | UObject에 대한 약한 참조 | IsValid()로 GC에 의해 파괴됐는지 확인 가능 | 안전함 |
TSoftObjectPtr | 리소스를 지연 로드할 때 사용 | 에셋의 경로를 저장, 필요할 때 로드 | 안전함 |
TSoftClassPtr | UClass를 지연 로드할 때 사용 | 블루프린트 클래스 등 클래스 경로 저장 | 안전함 |
순수 C++이든 USTRUCT()든, 언리얼에서 구조체면 접두사로 F 를 붙여주자
⚠️ C++ STL의 vector랑 완전 다름
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);
}
double 값 2개 (X, Y)로 구성된 2차원(2D) 벡터 타입// 2D 벡터 생성
FVector2D ScreenPos(100.0, 50.0);
// 값 출력
printf("X: %.1f, Y: %.1f\n", ScreenPos.X, ScreenPos.Y);
float 기반 타입FQuat (쿼터니언) 사용하기FRotator Rotation(0.0, 90.0, 0.0); // Pitch, Yaw, Roll
Actor->SetActorRotation(Rotation);
FRotator 보다 안정적)Gimbal Lock 회전 축 두 개가 겹쳐서 회전 축 하나가 사라지는 현상struct FQuat
{
float X;
float Y;
float Z;
float W;
};
FQuat Quat = FQuat(FVector(0, 0, 1), FMath::DegreesToRadians(90)); // Z축 90도 회전
위치(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));
// 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
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("이벤트 중입니다!"));
}
// 이벤트 종료까지 남은 시간
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("이벤트가 종료되었습니다."));
}
핸들(handle) 어떤 객체나 리소스를 간접적으로 관리하기 위한 참조값SetTimer() → 타이머 설정 (몇 초 뒤에 어떤 함수 실행할지)ClearTimer() → 타이머 취소 (중간에 멈추고 싶을 때)true / false → 반복 실행 여부 설정// 1초마다 반복 실행
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &AMyActor::TickFunction, 1.0f, true);
// 타이머 멈추기
GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
- AActor 안에서 GetWorld() 생략 가능?
GetWorld() 없이도 GetWorldTimerManager()를 바로 사용할 수 있음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);
}
SetTimer() 인자 해석| 부분 | 의미 |
|---|---|
GetWorldTimerManager() | 타이머를 관리하는 엔진 객체 가져오기 |
.SetTimer(...) | 타이머 설정 함수 |
TimerHandle | 이 타이머를 제어할 수 있는 핸들(참조값) |
this | 이 객체(보통 Actor) 기준으로 실행함 |
&ATimer::PrintHello | 2초 후 실행할 함수 지정 |
2.0f | 2초 후 실행 |
false | 반복 ❌ → 한 번만 실행 |
FColor::Red, FColor(255, 128, 0, 255)FLinearColor::Blue, FLinearColor(1.0f, 0.5f, 0.0f, 1.0f)Tick(), BeginPlay(), EndPlay() 등을 오버라이드 가능