1. 언리얼에서 사용하는 풀더구조

  • .vs 폴더
    • Visual Studio가 자동 생성하는 폴더로, 디버깅·솔루션 설정 관련 파일들이 있습니다.
    • 직접 수정할 일은 거의 없습니다.
  • Binaries 폴더
    • C++ 코드를 빌드하면 생성되는 실행 파일(.exe, .dll 등)이 저장됩니다.
    • 이 폴더를 지우면 다음 빌드시 처음부터 빌드를 다시 수행하므로 시간이 더 걸릴 수 있습니다.
  • Config 폴더
    • 게임플레이, 렌더링, 입력 등의 설정을 담은 .ini 파일이 모여 있습니다.
    • 에디터나 코드로 특정 설정을 바꾸면, 이 폴더 내 .ini 파일들이 종종 수정됩니다.
  • Content 폴더
    • 언리얼 에디터의 Content Browser와 연결되어 있으며, 게임 내 모든 에셋 (모델, 머티리얼, 사운드 등)이 들어 있습니다.
    • 에디터에서 새 에셋을 만들거나 다른 에셋을 불러오면 (import) 이 폴더 안에 파일이 생성됩니다.
  • DerivedDataCache 폴더
    • 에셋, 쉐이더 등을 빠르게 처리하기 위한 캐시 파일이 저장됩니다.
  • Intermediate 폴더
    • C++를 컴파일할 때 생성되는 임시 파일들이 저장됩니다.
  • Saved 폴더
    • 자동 저장 파일, 로그 (.log), 크래시 덤프 등이 모여 있습니다.
    • 게임·에디터가 비정상 종료되었을 때, 이 폴더 안의 로그 파일을 확인해 문제를 진단할 수 있습니다.
  • Source 폴더
    • C++ 소스 코드 (.cpp, .h)가 들어 있는 핵심 폴더로, 실제 게임 로직을 작성하는 곳입니다.

2. Visual Studio상 트리구조

  • Config 폴더
    • .ini 파일을 통해 에디터와 게임의 초기 상태를 지정합니다.
      • DefaultEditor.ini: 에디터 환경 설정 (뷰포트, UI 등)
      • DefaultEngine.ini: 엔진 전반 설정 (렌더링, 네트워킹 등)
      • DefaultGame.ini: 게임플레이 관련 설정 (게임 모드, 플레이어 컨트롤러 클래스 등)
      • DefaultInput.ini: 키보드·마우스·패드 등의 기본 입력 바인딩
  • Source 폴더
    • 실제 C++ 소스 코드(.cpp, .h)가 들어 있습니다.
    • 최초 프로젝트 생성 시에는 프로젝트명.cpp, 프로젝트명.h 등 최소 파일만 있지만, 새 클래스를 만들수록 점차 늘어납니다.
    • 빌드 설정 관련 주요 파일도 포함됩니다.
      • 프로젝트명.Build.cs: 해당 프로젝트에 필요한 모듈, 라이브러리, 종속성 등을 정의
      • 프로젝트명.Target.cs, 프로젝트명Editor.Target.cs: 각각 게임 실행용, 에디터용 빌드 방식을 정의

3. Unreal Build

빌드과정 : C파일 수정 -> Build(컴파일 + 링크) -> DLL File -> Unreal Editor

C++ 코드 빌드의 목적

  • C++ 코드를 수정했다면, 이를 컴파일(Compile) + 링크(Link) 해 “동적 라이브러리 (DLL)”로 만들어야 합니다.
  • 그렇게 생성된 DLL이 언리얼 에디터에서 로드되어, 새로 작성한 로직 (함수, 클래스 등)이 게임이나 에디터 내에서 즉시 반영됩니다.

빌드 구성 및 플랫폼 확인

  • Visual Studio 상단 툴바에는 빌드 구성 (Configuration)과 플랫폼 (Platform)을 선택하는 드롭다운이 있습니다.

    • 왼쪽 - 빌드 구성 (예: DebugGame, Development Editor 등)
    • 오른쪽 - 플랫폼 (Win64)
  • 빌드 구성 (Configuration) 종류

    • DebugGame
      • 게임 로직만 디버그 정보를 포함하고, 엔진은 최적화된 상태로 빌드합니다.
      • 에디터가 아닌 독립 실행 파일 환경에서 디버깅이 가능합니다.
    • DebugGame Editor
      • 에디터 환경에서 게임 로직을 디버그하기 편한 설정입니다.
      • 에디터 플레이 중에 C++ 로직을 추적하거나 브레이크포인트를 걸어볼 수 있습니다.
    • Development
      • 디버그 정보를 최소화해 실행 속도를 높인 개발용 빌드입니다.
      • 독립 실행 파일 환경 테스트·개발 단계에서 주로 쓰입니다.
    • Development Editor
      • 에디터에서도 개발·테스트를 원활히 할 수 있도록 구성된 빌드 모드입니다.
      • Live Coding 사용 시나리오와 궁합이 좋고, 초·중급자들이 자주 사용합니다.
      • 본 강의에서도 기본 모드로 활용합니다.
    • Shipping
      • 최종 사용자에게 배포할 때 사용하는 릴리스 빌드입니다.
      • 디버그 정보를 제거하고, 성능 최적화가 극대화됩니다.
  • 플랫폼 (Platform) 설정

    • 기본적으로 Win64가 선택되어 있으며, 이는 Windows 64비트 환경을 의미합니다.
    • 모바일 (Android, iOS), 콘솔 (PS, Xbox 등)로 빌드하려면 해당 플랫폼용 SDK를 추가로 설치해야 합니다.

Public풀더, Private 풀더

모듈들끼리 참조가 가능하냐, 불가능하냐를 정의 할 수 있다.

  • Public : 해당 풀더 안에 있으면 참조가 가능하다.
  • Private : 해당 풀더 안에 있으면 참조가 불가능하다.

Actor와 Object의 차이점

Actor

레벨에 배치가 가능한 오브젝트를 말한다. 트랜스폼을 지원하는 범용 클래스이다.

Object

매크로로 해당 클래스에 대한 인스턴스 정보를 읽을 수 있다. 언리얼에서 제공하는 가장 기본적인 오브젝트는 UObject로 생성시 디폴트 생성자를 호출한다.
절대로 New Keyword를 이용해 생성하면 안된다. 언리얼 엔진으로 관리가 되기 위해서는 누수가 발생할 위험이 크다.

#pragma once

#include 'Object.h'

#include 'MyObject.generated.h' // Unreal Engine 부분
	
UCLASS()
class MYPROJECT_API UMyObject : public UObject
{
	GENERATED_BODY()
};

Object의 소멸

오브젝트가 더 이상 참조되지 않을 때 가비지 컬렉션 시스템이 자동으로 오브젝트를 파괴한다. (Shared_ptr 방식) 강한 참조를 가져서는 안된다. 가비지 컬렉터를 실행하면, 참조되지 않는 오브젝트를 발견하면 메모리에서 제거한다. 또한 MarkPendingKill()함수를 오브젝트에서 바로 호출할 수 있다. 이 함수는 오브젝트에 대한 모든 포인터를 NULL로 설정하고 글로벌 검색에서 오브젝트를 제거한다. 오브젝트는 다음 가비지 컬렉션 패스에서 완전히 삭제 된다.

스마트 포인터는 UObject와 함께 사용하면 안된다.

Object->MarkPendingKill은 Object->MarkAsGarbage()로 대체 되었다.
새로운 함수는 오래된 오브젝트를 추적하는 용도로만 사용한다.
gc.pendingKillEnabled = true인 경우 PendingKill로 표시된 오브젝트는 자동으로 NULL이 되고 가비지 컬렉터에 의해 삭제된다. 강한 참조가 있다면 UObject가 유지된다 그렇기 때문에 이러한 레퍼런스가 UObject를 활성화 한 상태로 유지하는 것을 원하지 않는 경우 WeakPointer를 사용하도록 변환하거나, 수동으로 삭제하는 노멀 포인터여야 한다.
값의 확인은 IsValid를 통해서 해야한다. MarkGarbage는 플래그 표시를 하지만 오브젝트 자체는 해당 오브젝트에 대한 모든 레퍼런스가 해제될 때까지 가비지 컬렉션 되지 않는다.
엑터의 경우, Destroy()가 호출되어서 레벨에서 제거되었어도, 해당 엑터에 대한 모든 레퍼런스가 해제될 때까지 가비지 컬렉션 되지 않는다.(SharedPtr특성)


Reflaction Macro 설명

  • UCLASS()
    • 이 클래스를 언리얼 엔진의 리플렉션 시스템에서 인식하도록 하는 매크로입니다. (이후 강의에서 배우는 내용)
    • 언리얼 에디터에서 이 클래스를 블루프린트로 확장할 수 있게 하고, 에디터의 여러 기능과 연동하도록 합니다.
  • class SPARTAPROJECT_API AItem : public AActor
    • AActor를 상속받아 AItem 클래스를 정의한다는 의미입니다.
    • AItem
      • 언리얼 엔진 C++에서는 클래스 이름에 접두사를 붙이는 컨벤션이 있습니다.
        • A (Actor 계열), U (Object 계열), F (일반 구조체), T (템플릿), E (열거형) 등
        • 우리는 Item 이라는 이름으로 클래스를 만들었지만, 실제 클래스 이름은 Actor 클래스를 상속받았으므로, AItem 으로 생성됩니다.
        • 언리얼 엔진 코드 컨벤션 문서
    • SPARTAPROJECT_API 는 이 클래스를 모듈 (여기서는 SpartaProject) 외부로 Export하기 위한 매크로입니다. DLL 등으로 빌드할 때 필요한 선언입니다.
  • GENERATED_BODY()
    • UCLASS()와 짝을 이루어, 엔진 리플렉션에 필요한 코드를 자동 생성해 주는 매크로입니다.

Component

  • 3D 메시를 할당해주기 위해서는 액터에 추가적인 컴포넌트 (Component)를 붙여줘야 합니다.
  • 컴포넌트는 언리얼 엔진에서 Actor어떤 역할을 하거나 특정 속성을 갖도록 만들어주는 부품 (파츠) 개념입니다.
  • 하나의 Actor가 여러 종류의 컴포넌트를 조합하여 다양한 기능을 구현할 수 있습니다.
    • 예를 들어, Static Mesh Component + Audio Component + Collision Component를 모두 사용해, 충돌 시 소리가 나는 아이템을 만들 수도 있습니다.

Component 생성

#include "Item.h"

AItem::AItem()
{
		// Scene Component를 생성하고 루트로 설정
		SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
		SetRootComponent(SceneRoot);

		// Static Mesh Component를 생성하고 Scene Component에 Attach
		StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
		StaticMeshComp->SetupAttachment(SceneRoot);
		
		// Static Mesh를 코드에서 설정
		static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
		if (MeshAsset.Succeeded())
		{
			StaticMeshComp->SetStaticMesh(MeshAsset.Object);
		}

		// Material을 코드에서 설정
		static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Gold.M_Metal_Gold"));
		if (MaterialAsset.Succeeded())
		{
			StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
		}
}

라이프사이클

  • 초기화 시점 결정
    • 생성자 (Constructor), PostInitializeComponents, BeginPlay 등이 각각 언제 호출되는지 알아야 적절한 곳에 코드를 배치할 수 있습니다.
    • 예) 컴포넌트 생성(CreateDefaultSubobject)은 생성자에서, 다른 액터 참조나 월드 접근은 BeginPlay에서 처리.
  • 성능 관리
    • 매 프레임마다 호출되는 Tick 함수는 비용이 클 수 있습니다.
    • 따라서 필요한 액터Tick을 활성화하거나 이벤트 기반으로 전환해 최적화해야 합니다.
  • 리소스 정리
    • 액터가 사라질 때 (EndPlay, Destroyed 등) 메모리를 해제하거나 특정 상태를 저장해야 할 수 있습니다.
    • 적절한 시점에 필요한 정리 작업을 하지 않으면 메모리 누수예외 상황이 발생할 수 있습니다.

  • 언리얼 엔진의 Actor는 생성 → 초기화 → 월드 배치 → Tick(실행) → 제거 순으로 동작하며, 이를 지원하기 위해 여러 함수가 자동 호출됩니다.
    1. 생성자 (Constructor)
      • C++ 클래스 객체가 메모리에 생성될 때 단 한 번 호출됩니다.
      • 아직 월드에 완전히 등록된 상태가 아니므로, 다른 액터나 월드 관련 기능은 안전하게 호출하기 어렵습니다.
      • 보통 컴포넌트 생성(CreateDefaultSubobject) 및 기본 변수 초기화에 사용합니다.
    2. PostInitializeComponents()
      • 액터의 모든 컴포넌트가 생성·초기화를 마친 뒤 자동으로 호출됩니다.
      • 컴포넌트들이 이미 준비된 상태이므로, 컴포넌트 간 상호작용 초기화 코드를 넣기 좋습니다.
    3. BeginPlay()
      • 게임이 시작 (Play 모드)되거나, 런타임 중 액터가 새로 생성 (Spawn)되는 순간에 한 번 호출됩니다.
      • 이 시점에서는 월드와 다른 액터들이 준비된 상태이므로, 자유롭게 상호작용 코드를 작성할 수 있습니다.
      • AI, 게임 모드, 플레이어 컨트롤러 등 다른 시스템과의 연동도 보통 이 시점에 처리합니다.
    4. Tick(float DeltaTime)
      • 매 프레임마다 반복 호출되며, 실시간 업데이트가 필요한 로직 (캐릭터 이동, 물리 연산 등)을 넣습니다.
      • 불필요한 액터에는 Tick을 끄고, 이벤트 기반으로 전환하면 성능을 절약할 수 있습니다.
    5. Destroyed()
      • Destroy() 함수를 직접 호출해 액터를 제거할 때 직전에 호출됩니다. (단, 게임 종료나 레벨 전환 시에는 호출되지 않을 수 있음)
      • 보통 EndPlay에서 주요 정리를 마치고, Destroyed()에서 메모리 해제사운드/파티클 정리 등 최종 작업을 수행합니다.
      • Destroyed가 불리면 마지막에 EndPlay도 같이 호출됩니다.
    6. EndPlay(const EEndPlayReason::Type EndPlayReason)
      • 액터가 더 이상 월드에서 활동하지 않을 때 (파괴, 게임 종료, 레벨 전환 등) 호출됩니다.
      • EEndPlayReason::Type은 언리얼 엔진에서 EndPlay 함수가 호출되는 이유를 나타내는 열거형(enum) 타입입니다.
      • 이 함수에서 자원 해제상태 저장을 처리합니다.

사용자 정의 로그

DECLARE_LOG_CATEGORY_EXTERN(LogSparta, Warning, All);
DEFINE_LOG_CATEGORY(LogSparta);
profile
취미로 게임하는사람

0개의 댓글