tools -> open visual studio 2022 클릭

 

<Item.h>

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

 

  • #include "CoreMinimal.h"
    • 언리얼에서 가장 기본이 되는 헤더로, 엔진 전역 타입, 매크로, 함수 등을 가져옴
    • 필수 헤더이므로 거의 모든 .h 파일의 맨 위에 포함됨
  • #include "GameFramework/Actor.h"
    • AActor 클래스 선언을 사용 가능하게 함
  • #include "Item.generated.h"
    • 코드들을 블루프린트에서 볼 수 있게 해주는 일종의 기능을 사용하기 위해서 포함하는 헤더파일
    • 언리얼 헤더툴 (UHT)이 자동 생성하는 코드를 포함하는 것으로, 항상 헤더의 마지막 줄에 위치해야 함
    • 다른 곳으로 이동하면 컴파일 오류!
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
		GENERATED_BODY()
	
public:
		// 생성자
		AItem();

protected:
	  // 액터가 월드에 스폰 (배치된) 직후 한 번만 호출
		virtual void BeginPlay() override;
		// 매 프레임마다 호출
		virtual void Tick(float DeltaTime) override;
};

 

AItem, AActor 같이 A가 붙은 이유 : 언리얼 엔진 자체에서는 클래스 이름 앞에 접두어를 붙이는 걸 권장함.

 

<접두어>

더보기
더보기
더보기
  • A (Actor): AActor를 상속받는 클래스입니다. 3D 게임 월드(레벨)에 직접 배치되거나 스폰될 수 있는 모든 객체를 의미합니다.
  • U (UObject): UObject를 상속받지만 AActor는 아닌 클래스입니다. 언리얼 엔진의 가비지 컬렉션, 리플렉션(블루프린트 연동 등), 직렬화 기능을 지원받는 객체입니다. 월드에 직접 배치될 수는 없으며, 주로 다른 액터의 구성 요소로 쓰이거나 데이터를 관리합니다.
  • F (Struct / Non-UObject): 구조체(Struct)이거나 UObject를 상속받지 않는 순수 C++ 클래스입니다. 가비지 컬렉션의 관리를 직접 받지 않는 가벼운 데이터 컨테이너나 유틸리티 클래스에 사용됩니다.
  • T (Template): C++ 템플릿(Template) 클래스 및 컨테이너입니다.
  • I (Interface): 언리얼 인터페이스(Interface) 클래스입니다. 여러 다른 클래스에 공통된 함수 규격을 제공할 때 사용합니다.
  • E (Enum): 열거형(Enumeration) 타입입니다.
  • b (Boolean): 부울(Boolean) 타입의 멤버 변수입니다. 소문자 'b'로 시작하며, 그 뒤는 대문자로 시작해야 합니다.
  • S (Slate): 슬레이트(Slate) UI 프레임워크를 구성하는 위젯 클래스입니다. 언리얼 에디터의 UI나 UMG의 기반이 되는 UI를 C++로 작성할 때 쓰입니다.

 

 

    • UCLASS()
      • 이 클래스를 리플렉션 시스템에 등록하는 매크로입니다. 언리얼 에디터에서 이 클래스를 블루프린트로 확장할 수 있게 하고, 에디터의 여러 기능과 연동하도록 합니다.
    • class SPARTAPROJECT_API AItem : public AActor
      • AActor를 상속받아 AItem 클래스를 정의한다는 의미입니다.
      • SPARTAPROJECT_API 는 이 클래스를 모듈 (현재 모듈은 SpartaProject) 외부에서도 클래스를 사용할 수 있게 하기 위한 매크로입니다.
    • GENERATED_BODY()
      • UCLASS()와 짝을 이루어, 언리얼 헤더툴 (UHT)이 자동 생성한 코드를 삽입해 주는 매크로입니다. (이후 강의에서 배우는 내용)
    • Actor 라이프사이클 관련 함수 (이후 강의에서 배우는 내용)
      • AItem() - C++ 생성자
        • Actor 객체가 메모리에 생성될 때 한 번 호출됩니다.
      • BeginPlay()
        • 액터가 월드에 완전히 배치되고, 게임 플레이 (Play)가 시작할 때 한 번 호출됩니다.
      • Tick(float DeltaTime)
        • 매 프레임마다 자동으로 호출됩니다.

 

<Item.cpp>

#include "Item.h" // 항상 첫 줄에! 자신과 짝이 되는 헤더를 가장 먼저 include해야 함.

// 생성자 구현부
AItem::AItem()
{
		PrimaryActorTick.bCanEverTick = true; 
}

// BeginPlay() 구현부
void AItem::BeginPlay()
{
		// 부모 클래스(AActor)의 BeginPlay()를 먼저 호출
		Super::BeginPlay();
}

// Tick() 구현부
void AItem::Tick(float DeltaTime)
{
		// 부모 클래스(AActor)의 Tick() 먼저 호출
		Super::Tick(DeltaTime);
}

 

  • #include "Item.h"
    • 자동 생성 매크로가 올바르게 확장되려면 짝이 되는 헤더를 가장 먼저 include해야 합니다.
    • .cpp 파일에서는 가장 첫 줄에 짝이 되는 헤더 파일을 포함해야 합니다. 그렇지 않으면 예기치 않은 링커 오류가 발생할 수 있습니다.
  • Actor 라이프사이클 관련 함수 구현부 
    • 생성자 AItem(), BeginPlay(), Tick(float DeltaTime)
      • 부모 구현을 호출해 엔진 내부 동작을 보장해준 후, 여기서 구현 로직을 자유롭게 추가할 수 있습니다.

 


<Actor 클래스에 컴포넌트 멤버 변수 추가하고 초기화하기>

Item 액터를 레벨 뷰포트에 배치해도 월드에서 보이지 않음 -> 3D 메시 (시각적 컴포넌트)를 설정하지 않았기 때문! 

3D 메시를 할당해주기 위해서는 액터에 추가적인 컴포넌트(Component)를 붙여줘야 함.

 

컴포넌트 (Component)란?

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

 

루트 컴포넌트와 Scene Component

 

모든 Actor는 루트 컴포넌트 (Root Component)를 가져야 합니다.

루트 컴포넌트는 액터의 트랜스폼 (위치, 회전, 크기)을 정의하는 최상위 컴포넌트이며, 모든 하위 컴포넌트가 이를 기준으로 트랜스폼이 계산됩니다.

보통 Scene Component를 루트 컴포넌트로 설정하여 액터의 트랜스폼을 관리하고, 그 아래에 다양한 컴포넌트를 계층적으로 붙입니다.

  • Scene Component는 모든 트랜스폼 속성만 가지는 비시각적인 컴포넌트입니다.
  • Scene Component는 직접적인 시각적 출력을 가지지 않지만, 다른 하위 컴포넌트들을 관리하는 기준점 역할을 할 수 있습니다.

Static Mesh Component란?

  • 애니메이션이나 스켈레탈 본 없이 (즉, 움직임이 없고 단순 이동·회전만 하는) 정적 (Static) 3D 모델을 그리는 컴포넌트입니다.
    • 환경 오브젝트 (건물, 바위), 아이템, 단순 기믹 오브젝트 등 “움직임이 단순하거나 없는” 오브젝트에 주로 적합합니다.
    • 3D 모델을 표현하고, 물리 충돌과 관련된 기능도 제공합니다.

static mesh : 모델. 생긴 모양을 말함!

materials : 표면에 붙이는 색상 질감 같은 걸 말함

 

Static Mesh Component와 Scene Component 연결하기

<Item.h>

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
		GENERATED_BODY()
	
public:	
		AItem();

protected:
		// 루트 컴포넌트를 나타내는 Scene Component 포인터
		USceneComponent* SceneRoot;
		// Static Mesh Component 포인터
		UStaticMeshComponent* StaticMeshComp;
};

 

<Item.cpp>

#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);
}

 

 

 

-> root 컴포넌트는 보이는데, static mesh 컴포넌트는 안 보임. 

C++로 구현했기 때문. 에디터 상에서 보이게 하려면 리플렉션 시스템에 등록이 되어야 함! (추후 배울 예정)

Root 컴포넌트는 무조건 리플렉션 시스템에 등록이 되어서 보이는 거임.

 


<메시 및 머티리얼 할당하고 Actor 배치하기>

생성자에서 static mesh랑 materials을 설정해줘야 함.

 

1. 어떤 걸 메시로 설정할지 언리얼 에디터의 resource에서 확인한 후, 우클릭 -> copy reference (위치 경로를 가져오는 것)

2. visual studio로 돌아와서 복붙

/Script/Engine.StaticMesh'/Game/Resources/Props/SM_Chair.SM_Chair'

(빨간색으로 표시된 부분이 경로임)

3. '/Game/Resources/Props/SM_Chair.SM_Chair' 만 남겨두고 나머지 부수적인 것들 삭제하기

4. 메시 등의 리소스를 로드하는 클래스 가져오기

static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("여기에 위에서 복사한 경로를 가져오는 것"))

(일종의 static 타입의 변수 선언)

static ConstructorHelpers::FObjectFinder MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"))

-> 리소스를 MeshAsset이라는 이름으로 가져오게 된 것.

5. MeshAsset이 가져와졌는지 확인하는 코드 작성

	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
	if (MeshAsset.Succeeded())
	{
		StaticMeshComp->SetStaticMesh(MeshAsset.Object);
	}

 

 

머티리얼도 똑같은 방식으로 가져오기!

	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Burnished_Steel.M_Metal_Burnished_Steel"));
	if (MaterialAsset.Succeeded())
	{
		StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
	}

 

다만 material은 여러 개를 가져올 수도 있어서(부분부분 질감이라던가 색이 다를 수도 있음) 인덱스를 붙여줘야 함.

위의 코드는 첫 번째 material을 가져오겠다는 의미로 0번 인덱스를 달아준 것.

 

 

 

<Item.cpp>

#include "Item.h"

AItem::AItem()
{
	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

	StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
	StaticMeshComp->SetupAttachment(SceneRoot);


	static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/Game/Resources/Props/SM_Chair.SM_Chair"));
	if (MeshAsset.Succeeded())
	{
		StaticMeshComp->SetStaticMesh(MeshAsset.Object);
	}

	static ConstructorHelpers::FObjectFinder<UMaterial> MaterialAsset(TEXT("/Game/Resources/Materials/M_Metal_Burnished_Steel.M_Metal_Burnished_Steel"));
	if (MaterialAsset.Succeeded())
	{
		StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
	}
}

 

+ Recent posts