언리얼 엔진 본캠프 6주차-5 언리얼 엔진 C++ : Actor 라이프 사이클과 Transform + 리플렉션 시스템

정재훈·2025년 1월 24일
0

unreal engine

목록 보기
17/45

카테고리 정의하기

.h : DECLARE_LOG_CATEGORY_EXTERN(카테고리 이름, 기본 심각도, All);
.cpp : DEFINE_LOG_CATEGORY(카테고리 이름);

<Actor 라이프 사이클>

  • 생성자 : 오브젝트가 메모리에 할당될 때 딱 한번만 호출
    • world에 액터가 없을 가능성이 높음
    • 컴포넌트 생성해서 액터에 부착
  • PostInitializeComponents() : 모든 컴포넌트들이 초기화되고 난 후 호출되는 함수
    • 생성자에서 컴포넌트들이 다 만들어진 후에 컴포넌트들끼리 상호작용해야 할 게 있으면 이 함수에서 처리하면 됨
    • 에디터에서 시작 버튼을 눌렀을 때 BeginPlay()하기 전에 호출됨
  • BeginPlay() : 액터가 월드에 스폰되는 시점에 바로 호출이 되는 함수
    • 액터가 스폰되고나서 처리해야할 작업을 여기서 처리
  • Tick(float DeltaTime) : 매 프레임마다 호출
    • 이동, 애니메이션, 물리 계산 등 물 흐르듯 자연스럽게 동작해야 하는 것들을 처리하기 좋음
  • Destroy() : 강제로 오브젝트를 삭제할 때 사용하는 함수
  • Destroyed() : Destroy() 내부에서 Destroyed()를 호출
  • EndPlay() : 게임 종료, Destroy(), 레벨 전환 등 액터가 사라질 때 호출되는 함수

생성자 → PostInitializeComponents() → BeginPlay() → Tick() → (Destroy()) → EndPlay()

라이프 사이클 함수 구현할 때 [반드시] 부모의 라이프 사이클 함수(Super::~~~)를 호출해줘야 함


<Transform>

Transform

Actor는 Object와 다르게 Transform 정보가 있어 world에 배치가 가능하다고 했는데 사실은 루트 컴포넌트가 있어야 Transform 개념을 가지게 됨

  • 아무것도 없는 Actor도 world의 원점에 배치는 되지만 모습도 안보이고 기즈모로 움직일 수도 없음.
  • 대신 스태틱 메시든, 스프링 암이든 상관없이 루트 컴포넌트를 지정하면 원하는 위치에 배치를 할 수 있고 기즈모로 움직일 수 있다

Location(FVector), Rotation(FRotator), Scale(FVector)을 Transform(FTransform-구조체)으로 한번에 관리할 수 있음

  • Rotation에서 pitch-yaw-roll 만으로는 Gimbal Lock 문제가 있고 이를 위해 쿼터니언(FQuat)이라는 것을 사용한다
    • Gimbal Lock : 3차원 회전을 오일러 각도로 표현할 때 발생하는 문제

Tick()에서 Transform 계산

Tick()은 매 프레임마다 실행이 되기 때문에 어떤 계산을 할 때 프레임에 의존적이게 되고, 이에 따라 컴퓨터의 성능에 많은 영향을 받기 때문에 게임 플레이의 형평성이 어긋나게 된다

달리기를 한다고 했을 때 (거리 = 속도 x 시간)
A(100FPS)와 B(10FPS)의 속도(10)가 같다고 가정하고 매 프레임마다 해당 속도만큼 움직인다고 하면

  • A는 1초 동안 100프레임을 보여주니까 10 * 100 = 1000 만큼 이동하고
  • B는 1초 동안 10프레임을 보여주니까 10 * 10 = 100 만큼 이동한다
  • 이렇게 되면 컴퓨터가 좋지 않은 사람은 게임을 제대로 즐기기 어려워질 것이다

이를 방지하고 게임의 일관성을 유지하기 위해 DeltaTime을 계산할 때 곱해주는 방식을 사용한다 => 프레임 독립적 계산

DeltaTime : 이전 프레임에서 현 프레임까지 걸린 시간

  • 게임에서 프레임 속도(framerate)가 일정하지 않을 때 게임 플레이가 일관성을 유지하도록 만듦
  • A:100FPS = 1초에 100프레임 => 1프레임 당 deltaTime = 0.01초
  • B:10FPS = 1초에 10프레임 => 1프레임 당 deltaTime = 0.1초

달리기를 한다고 했을 때 (거리 = 속도 x 시간)
A와 B의 속도(10)가 같다고 가정하면 1초동안 움직인 거리는 10으로 같을 수 밖에 없다
다만 움직이는 과정을 몇 번 보여주는 지는 프레임에 달려있는 것

  • A의 deltaTime이 0.01초니까 1초 동안 100번 보여줄 것이고
  • B의 deltaTime은 0.1초니까 1초동안 10번 보여주는 것

<리플렉션 시스템>

리플렉션 시스템

: 런타임 및 에디터에서 객체와 그 속성, 메서드, 클래스 구조 등을 동적으로 확인하고 다룰 수 있게 해주는 시스템

  • UHT(UnrealHeaderTool) : 리플렉션 시스템을 지원하는 헤더 파일을 찾아서 관련 정보를 수집하고 이걸 토대로 .generated.h파일을 생성하는 툴

  • #include ".generated.h" : 리플렉션 시스템을 지원하는데 필요한 헤더 파일, UHT에 의해 자동으로 생성

    • 리플렉션 등록을 위한 코드
    • 에디터와 블루프린트와 통합하기 위한 코드
    • 메타 데이터를 처리하는 코드
    • 가비지 컬렉션 및 메모리 관리를 위한 코드가 들어있다
    • 가장 마지막에 include 해줘야 한다
  • UCLASS() : 해당 클래스를 리플렉션 시스템에 등록

  • GENERATED_BODY() : UCLASS()와 쌍으로 사용되고 리플렉션 데이터를 자동으로 생성하기 위한 매크로

    • .generated.h 파일의 코드를 클래스에 붙여넣어 해당 클래스에 리플렉션 시스템이 잘 작동하도록 해줌
      • .generated.h 파일 포함: GENERATED_BODY()는 UHT가 생성한 .generated.h 파일의 코드를 컴파일러가 해당 클래스/구조체의 내부에 삽입하도록 합니다.
        • 리플렉션 코드 삽입 위치 지정: 사용자가 작성한 코드에 메타데이터를 삽입할 위치를 명확히 정의합니다.
        • 클래스의 메서드, 속성, 생성자 등이 삽입될 위치를 컴파일러가 인식하게 만듭니다.
        • 코드의 가독성과 유지보수성: 만약 GENERATED_BODY()가 없다면, 개발자가 매번 .generated.h의 내용을 직접 포함하고 관리해야 합니다. 이렇게 되면 코드는 복잡해지고, 유지보수성이 떨어질 수 있습니다.
  • UPROPERTY() : 변수를 리플렉션 시스템에 등록

    • C++ 클래스는 코드에서만 Class Default 값을 설정할 수 있음
    • 이걸 리플렉션 시스템으로 블루프린트로 만들었을 때는 블루프린트를 더블 클릭하면 Class Defaults 창이 뜨고 여기서 설정할 수 있음
    • C++ 클래스이건 블루프린트 클래스이건 뷰포트에 끌어다 놓으면 인스턴스가 되고 각 인스턴스들은 Details창에서 값들을 수정할 수 있음
    • 다만 지정자에 따라 변수들이 에디터에서도 보여지는지, 수정할 수 있는지가 결정됨
    • 리플렉션 시스템에 추가된 UPROPERTY는 가비지 콜렉션(Gargbage Collection)에 의해 생명 주기가 관리되며, 리플렉션을 통해 이 멤버 변수의 이름, 유형 등을 런타임 중에 확인할 수 있음
  • UFUNCTION() : 함수를 리플렉션 시스템에 등록

    • 함수 리플렉션 기능(지정자)은 상당히 많음, 대표적으로 BlueprintCallable, BlueprintPure, BrueprintImplementableEvent가 있다

회전하면서 위아래로 움직이고 크기도 변하는 액터를 만들어보았다

AddActorLocalOffset()AddActorLocalRotation()을 사용하면 액터의 위치와 회전 각각에 변화를 줄 수 있었는데, 크기를 조절하는 함수는 없고 AddActorLocalTransform() 함수가 있어 이 함수를 사용해보았다

조금 특이한 점이 위치와 회전의 변화는 내부적으로 multiply()라는 함수를 통해 내가 NewTransform으로 넘겨준 값만큼 이전 위치와 회전에서 더해서 이동/회전을 하도록하는데, 크기는 NewScale로 정한 값이 그대로 지정되는 것이었다.

(AddActorLocalTransform()함수 말고 SetActorLocation(), SetActorRotation(), SetActorScale3D() 함수들을 사용해서 트랜스폼 값을 변경하는 방법도 있다)






참조 사이트
1. https://forums.unrealengine.com/t/postinitializecomponents-vs-beginplay/62991/4
2. https://velog.io/@lee_raccoon/.generated.hGENERATEDBODY%EA%B0%80-%EB%AD%94%EB%8D%B0
3. https://www.jetbrains.com/help/resharper/Unreal_Engine__Code_Analysis.html#uht_integration
4. https://openmynotepad.tistory.com/112
5. https://rhksgml78.tistory.com/691
6. https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-header-tool-for-unreal-engine
7. https://forums.unrealengine.com/t/what-do-generated-body-and-generated-uclass-body-do/420793

profile
드가자

0개의 댓글