TIL(44)

이채원·2026년 8월 24일

TIL

목록 보기
44/77

오늘의 할일
1. 코드카타 정리
2. 세션 정리
3. 궁금증 해결


코드카타 정리

<첫번째 코드카타>
문자열 s의 길이가 4 혹은 6이고, 숫자로만 구성돼있는지 확인해주는 함수

#include <string>
#include <vector>

using namespace std;

bool solution(string s) {
    bool answer = true;
    
    if (s.length() != 4 && s.length() != 6) {
        answer = false;
    }
    
    for (char c : s) {
        if (c < '0' || c > '9') {
            answer = false;
        }
    }
    return answer;
}

AI가 작성한 코드


#include <string>

using namespace std;

bool solution(string s) {
    // 1. 길이가 4 또는 6이 아니면 즉시 실패 처리 (Early Return)
    if (s.length() != 4 && s.length() != 6) return false;
    
    // 2. "0123456789"에 포함되지 않는 문자가 '없음(npos)'인지 확인
    return s.find_first_not_of("0123456789") == string::npos;
}
  1. 반복문 없는 극강의 가독성: for문을 해석할 필요 없이 "숫자가 아닌 문자를 처음으로 찾았는데, 그게 없어야(npos) 통과구나"라고 1초 만에 의도를 파악.

  2. 추가 헤더 불필요: <cctype>이나 <algorithm> 같은 다른 도구 상자를 가져올 필요 없이, 기본으로 주어지는 <string> 헤더 하나만으로 모든 작업이 끝납니다.

  3. 비즈니스 로직에 적합: find_first_not_of라는 내장 함수는 문자열 검증이라는 목적을 가장 직관적으로 보여주는 도구.

C++의 문자열에서 무언가를 찾는 함수들(예: find, find_first_not_of)은 목표물을 발견하면 그 글자가 있는 '위치 번호(인덱스)'를 반환

원하는 것을 못 찾았을 때

  • 0을 돌려주자니, "0번째 위치에서 찾았다"는 뜻이 되고
  • -1을 돌려주자니, size_t는 양수만 가질 수 있어서 음수를 담을 수가 없음

그래서 size_t라는 그릇이 담을 수 있는 최댓값을 꽉 채워서 반환하면, 그걸 "못 찾았다!"라는 신호로 취급하기로 한 것.

이 최댓값을 매번 외우고 다닐 수 없으니, 알아보기 쉽게 string::npos라는 이름표를 붙여준 것.

// 1. "0123456789" (숫자)가 아닌 글자를 찾아봐!
// 2. 그 결과가 npos (못 찾음) 이랑 똑같아? -> 모두 숫자라는 뜻!
return s.find_first_not_of("0123456789") == string::npos;

"숫자가 아닌 불순물을 찾아라!"라고 명령했는데, 컴퓨터가 다 뒤져보고는 "npos입니다! (불순물이 없는데요!)"라고 보고하는 상황입니다. 불순물이 없으니 문자열 전체가 완벽한 숫자로만 이루어져 있다고 증명되는 것.

<두번째 코드카타>

행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다. 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수 완성해주세요.

이중반복문이라 많이 복잡하지만 다른 방법이 안 떠올랐고 일단 진행함

#include <string>
#include <vector>

using namespace std;

vector<vector<int>> solution(vector<vector<int>> arr1, vector<vector<int>> arr2) {
    vector<vector<int>> answer = arr1;
    
    for (size_t i = 0; i < arr1.size(); ++i) {
        for (size_t j = 0; j < arr1[i].size(); ++j) {
            answer[i][j] += arr2[i][j];
        }
    }
    
    return answer;
}

AI가 작성한 코드

#include <vector>
#include <algorithm>

using namespace std;

vector<vector<int>> solution(vector<vector<int>> arr1, vector<vector<int>> arr2) {
    for (size_t i = 0; i < arr1.size(); ++i) {
        // arr1의 i번째 행과 arr2의 i번째 행을 더해서(plus), 다시 arr1에 덮어씌웁니다.
        transform(arr1[i].begin(), arr1[i].end(), arr2[i].begin(), arr1[i].begin(), plus<int>());
    }
    return arr1;
}
  1. 의도의 명확성 (What, not How):
    이중 for문과 [i][j]를 쓰면 코드를 읽는 사람이 '아, 인덱스를 돌면서 뭔가를 계산하고 있구나'라고 기계적인 흐름을 먼저 파악해야 합니다. 하지만 transform과 plus<int>()를 사용하면 "두 배열을 가져와서 더해라(plus)!"라고 단번에 알아볼 수 있음.

  2. 인덱스 실수 원천 차단:
    가로세로 인덱스(i, j)를 헷갈려서 프로그램이 터지는 실수를 아예 할 수 없도록 구조적으로 막아버립니다. 반복자가 알아서 처음부터 끝까지 안전하게 탐색.

  3. 극강의 메모리 효율:
    answer라는 새로운 2차원 배열을 만들지 않고, 매개변수로 주어진 arr1을 재활용하여 덮어씌운 뒤 그대로 반환하기 때문에 메모리 낭비가 없습니다.

transform(
    arr1[i].begin(),  // ① 1번 재료의 시작점: "arr1의 i번째 줄 처음부터..."
    arr1[i].end(),    // ② 1번 재료의 끝점: "...끝까지 가져와."
    arr2[i].begin(),  // ③ 2번 재료의 시작점: "arr2의 i번째 줄도 처음부터 짝을 맞춰서 가져와."
    arr1[i].begin(),  // ④ 결과를 담을 위치: "계산이 끝나면 다시 arr1의 처음 위치부터 차곡차곡 덮어씌워."
    plus<int>()       // ⑤ 할 일: "가져온 두 숫자를 서로 더해(+)."
);

요약
"arr1의 이번 줄(①~②)과 arr2의 이번 줄(③)을 순서대로 하나씩 꺼내서 더한 다음(⑤), 그 결과를 다시 arr1의 원래 자리(④)에 덮어씌워 줘(transform)"


C++와 Unreal Engine으로 3D 게임 개발 : 7회차 정리

3-2 충돌 이벤트로 획득되는 아이템 구현

모든 아이템 클래스의 부모인 베이스아이템 클래스에 컴포넌트 구현해주기

씬 컴포넌트(루트)와 스태틱 메쉬 컴포넌트 필요하므로 구현
콜리전 담당 컴포넌트도 추가

  • 근데 스피어 컴포넌트는 미리 선언이 필요함
  • 에디터에서 수정이 가능하게 UPROPERTY도 추가해주기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ItemInterface.h"
#include "BaseItem.generated.h"

class USphereComponent;

UCLASS()
class TESTPROJECT2_API ABaseItem : public AActor, public IItemInterface
{
	GENERATED_BODY()
	
public:	
	ABaseItem();

protected:
	virtual void OnItemOverlap(AActor* OverlapActor) override;
	virtual void OnItemEndOverlap(AActor* OverlapActor) override;
	virtual void ActivateItem(AActor* Activator) override;
	virtual FName GetItemType() const override;

	virtual void DestroyItem();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	FName ItemType;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
	USceneComponent* Scene;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
	USphereComponent* Collision;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
	UStaticMeshComponent* StaticMesh;
};
  • cpp파일에서 초기화 해주기, 객체 만들기
#include "BaseItem.h"
#include "Components/SphereComponent.h"

ABaseItem::ABaseItem()
{
	PrimaryActorTick.bCanEverTick = false;

	Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
	SetRootComponent(Scene);

	Collision = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
	Collision->SetupAttachment(Scene);

	StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
	StaticMesh->SetupAttachment(Collision);
}
  • 콜리전 디폴트 설정하기
  • 그리고 이벤트 바인딩이라는 걸 작성하기
  • 그럴려면 이벤트 바인딩하는 함수의 매개변수랑 연결된 함수의 매개변수가 같아야함
  • 그래서 인터페이스 클래스로 돌아와서 매개변수를 바꿔줘야함
ABaseItem::ABaseItem()
{
	PrimaryActorTick.bCanEverTick = false;

	Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
	SetRootComponent(Scene);

	Collision = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
	Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
	Collision->SetupAttachment(Scene);

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

	//이벤트 바인딩
	//이벤트 바인딩을 해주는 이유
	//게임 실행 전에는 사실 어떤 객체가 어떤 상황에서 이벤트를 발생시킬지 전혀 알 수 없음
	//플레이어가 움직이다가 특정 아이템과 충돌하거나 아니면 새로 생성된 적(예시)과 부딪히게 되는 상황들이 많이 발생
	//근데 그 어떤 객체, 어떤 함수인지 알 수가 없음, 그래서 그때그때 객체는 생성됐다 사라졌다 그러는데 그런 예측 불가능한 상황에
	//대비하려면 이 이벤트랑 함수를 게임 실행 중에 동적으로 연결을 해줘야함
	//따라서 일종의 동적으로 게임 환경 상에서 실시간으로 이 함수가 이어지고 그리고 그 객체로부터 함수를 부르는 것 = 이벤트 바인딩
	Collision->OnComponentBeginOverlap();
	Collision->OnComponentEndOverlap();
}
  • ItemInterface.h
  • 충돌 정보를 넘겨주려면 같은 함수의 시그니처야만 연결이 가능
class TESTPROJECT2_API IItemInterface
{
	GENERATED_BODY()

public:
	virtual void OnItemOverlap(
		UPrimitiveComponent* OverlappedComp,	//스피어 컴포넌트(오버랩이 발생한 자기 자신)
		AActor* OtherActor,						//콜리전 컴포넌트(스피어 컴포넌트에 부딪힌 상대방의 액터)
		UPrimitiveComponent* OtherComp,			//충돌을 일으킨 원인인 컴포넌트, 캡슐 컴포넌트
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult& SweepResult) = 0;
	virtual void OnItemEndOverlap(
		UPrimitiveComponent* OverlappedComp,	//스피어 컴포넌트
		AActor* OtherActor,						//콜리전 컴포넌트
		UPrimitiveComponent* OtherComp,			//충돌을 일으킨 원인인 컴포넌트, 캡슐 컴포넌트
		int32 OtherBodyIndex) = 0;
	virtual void ActivateItem(AActor* Activator) = 0;
	virtual FName GetItemType() const = 0;

};

인터페이스 클래스가 바뀌었기 때문에 베이스아이템 헤더도 매개변수 추가해주기

  • BaseItem.h와 .cpp에 추가하기
class TESTPROJECT2_API ABaseItem : public AActor, public IItemInterface
{
	GENERATED_BODY()
	
public:	
	ABaseItem();

protected:
	virtual void OnItemOverlap(
		UPrimitiveComponent* OverlappedComp,	//스피어 컴포넌트
		AActor* OtherActor,						//콜리전 컴포넌트
		UPrimitiveComponent* OtherComp,			//충돌을 일으킨 원인인 컴포넌트, 캡슐 컴포넌트
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult& SweepResult) override;
	virtual void OnItemEndOverlap(
		UPrimitiveComponent* OverlappedComp,	//스피어 컴포넌트
		AActor* OtherActor,						//콜리전 컴포넌트
		UPrimitiveComponent* OtherComp,			//충돌을 일으킨 원인인 컴포넌트, 캡슐 컴포넌트
		int32 OtherBodyIndex) override;
	virtual void ActivateItem(AActor* Activator) override;
	virtual FName GetItemType() const override;

ABaseItem::ABaseItem()
{
	Collision->OnComponentBeginOverlap.AddDynamic(this, &ABaseItem::OnItemOverlap);
	Collision->OnComponentEndOverlap.AddDynamic(this, &ABaseItem::OnItemEndOverlap);
}

void ABaseItem::OnItemOverlap(
	UPrimitiveComponent* OverlappedComp,	
	AActor* OtherActor,						
	UPrimitiveComponent* OtherComp,			
	int32 OtherBodyIndex,
	bool bFromSweep,
	const FHitResult& SweepResult) {
}

void ABaseItem::OnItemEndOverlap(
	UPrimitiveComponent* OverlappedComp,	
	AActor* OtherActor,						
	UPrimitiveComponent* OtherComp,			
	int32 OtherBodyIndex) {

}
  • 내부 동작 설계해주기 (충돌 확인하고 플레이어인지 확인하고 태그를 확인해야함)
  • 이따가 플레이어한테 태그 붙여주기
void ABaseItem::OnItemOverlap(
	UPrimitiveComponent* OverlappedComp,	
	AActor* OtherActor,						
	UPrimitiveComponent* OtherComp,			
	int32 OtherBodyIndex,
	bool bFromSweep,
	const FHitResult& SweepResult) {
	if (OtherActor && OtherActor->ActorHasTag("Player")) {
		ActivateItem(OtherActor);
	}
}
  • 리플렉션 시스템도 연동해줘야함(바인딩하는 함수에 UFUNCTION())
  • 인터페이스 파일에 붙여주기
class TESTPROJECT2_API IItemInterface
{
	GENERATED_BODY()

public:
	UFUNCTION()
	virtual void OnItemOverlap(
		UPrimitiveComponent* OverlappedComp,
		AActor* OtherActor,						
		UPrimitiveComponent* OtherComp,			
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult& SweepResult) = 0;
	UFUNCTION()
	virtual void OnItemEndOverlap(
		UPrimitiveComponent* OverlappedComp,
		AActor* OtherActor,						
		UPrimitiveComponent* OtherComp,			
		int32 OtherBodyIndex) = 0;
	virtual void ActivateItem(AActor* Activator) = 0;
	virtual FName GetItemType() const = 0;
};

여기까지 베이스 아이템의 기본적인 오버랩 충돌 처리 완료!!!

** 플레이어 태그 붙여주기(에디터에서)

  1. Character 블루프린트로 들어가기
  2. 디테일 창에서 Actor - Advanced - Tags - Player라고 추가
  3. 컴파일 & 저장

다 적용이 잘 됐는지 로그 찍어보기!

  • BaseItem.cpp 비긴 오버랩 함수에 로그 설정
  • 스크린 상에 찍히는 로그로
  • 어떤 메시지마다의 아이디 (-1이면 새로운 아이디가 그떄그때마다 생성)
  • 2.0f == 2초동안 띄워라
void ABaseItem::OnItemOverlap(
	UPrimitiveComponent* OverlappedComp,	
	AActor* OtherActor,						
	UPrimitiveComponent* OtherComp,			
	int32 OtherBodyIndex,
	bool bFromSweep,
	const FHitResult& SweepResult) {
	if (OtherActor && OtherActor->ActorHasTag("Player")) {
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Overlap!!!")));
		ActivateItem(OtherActor);
	}
}

아이템들에 약간의 처리 해주기

  • 잘 되어있나 체크해주기

  • 일단 CoinItem.h 파일에 ActivateItem을 오버라이딩 해줘야함

  • 그래야만 내부에서 각자의 코인마다 동작을 설정해줄 수 있음

UCLASS()
class TESTPROJECT2_API ACoinItem : public ABaseItem
{
	GENERATED_BODY()

public:
	ACoinItem();

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	int32 PointValue;
	
	virtual void ActivateItem(AActor* Activator) override;
};

<임시로 값을 초기화해주기 (안전을 위해서)>

  • CoinItem.cpp (로그도 찍어보기, 나중에 포인트 얻는 로직 짤거임 그부분에)
#include "CoinItem.h"

ACoinItem::ACoinItem() {
	PointValue = 0;
	ItemType = "DefaultCoin";
}

void ACoinItem::ActivateItem(AActor* Activator) {
	if (Activator && Activator->ActorHasTag("Player")) {
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player gained %d points!!"), PointValue));
		DestroyItem();
	}
}

코인 클래스 손봤으니까 밑에 자식 클래스는 크게 손 보지 않아도 됨

  • 빅코인클래스.cpp
#include "BigCoinItem.h"

ABigCoinItem::ABigCoinItem() {
	PointValue = 50;
	ItemType = "BigCoin";

}

void ABigCoinItem::ActivateItem(AActor* Activator) {
	Super::ActivateItem(Activator);
}
  • 스몰코인클래스.cpp
#include "SmallCoinItem.h"

ASmallCoinItem::ASmallCoinItem() {
	PointValue = 50;
	ItemType = "SmallCoin";

}

void ASmallCoinItem::ActivateItem(AActor* Activator) {
	Super::ActivateItem(Activator);
}
  • 힐링아이템.cpp
#include "HealingItem.h"

AHealingItem::AHealingItem() {
	HealAmount = 20.0f;
	ItemType = "Healing";

}

void AHealingItem::ActivateItem(AActor* Activator) {
	if (Activator && Activator->ActorHasTag("Player")) {
		GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player gained %d HP"), HealAmount));
		DestroyItem();
	}
}
  • MineItem
  • 5초 뒤에 폭발하고 반경 내에 데미지를 입는다
  • 에디터로 넘어가기
  • 마인 아이템 BP 들어가기
  • 스피어콜리전 크기 가늠하기(Sphere Radius = 300)
UCLASS()
class TESTPROJECT2_API AMineItem : public ABaseItem
{
	GENERATED_BODY()
	
public:
	AMineItem();

	USphereComponent* ExplosionCollision;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	int32 ExplosionDelay;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	int32 ExplosionRadius;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
	int32 ExplosionDamage;

	// 타이머 핸들러 선언
	FTimerHandle ExplosionTimerHandle;

	virtual void ActivateItem(AActor* Activator) override;

	//폭발 함수 따로 지정해주기
	void Explode();
};
  • 5초 뒤에 오버래핑된 플레이어가 있으면 데미지 입히기
#include "MineItem.h"
#include "Components/SphereComponent.h"

AMineItem::AMineItem() {
	ExplosionDelay = 5.0f;
	ExplosionRadius = 300.0f;
	ExplosionDamage = 30.0f;
	ItemType = "Mine";


	ExplosionCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionCollision"));
	ExplosionCollision->InitSphereRadius(ExplosionRadius);
	ExplosionCollision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
	ExplosionCollision->SetupAttachment(Scene);
}

void AMineItem::ActivateItem(AActor* Activator) {
	// 타이머 핸들러 = 각자의 하나의 시계
	// 게임 월드 -> 타이머 매니저 -> 타이머 핸들러 관리
    // Activate가 되고 5초 뒤에 실행

	// 이 타이머를 세팅해주기
    // 이 객체에서 타이머를 하나 달아주고 5초뒤에 Explore라는 함수를 실행
    // 시간 = ExplosionDelay
    // 반복할지 말지 = false
	GetWorld()->GetTimerManager().SetTimer(
		ExplosionTimerHandle,
		this,
		&AMineItem::Explode,
		ExplosionDelay,
		false
	);
}


void AMineItem::Explode() {
	//액터 검색
    //겹친 액터들을 저장해줄 배열 생성
	TArray<AActor*> OverlappingActors;
	//OverlappingActor들을 전부 가져와주고 넣어주기
	ExplosionCollision->GetOverlappingActors(OverlappingActors);
	
    //액터들을 돌면서 태그 확인하기
	for (AActor* Actor : OverlappingActors) {
		if (Actor && Actor->ActorHasTag("Player")) {	
        	//여기다가 폭발처리 나중에 만들어주기
			GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player damaged %f by MineItem"), ExplosionDamage));

		}
	}

	DestroyItem();
}

궁금증 해결하기

Q. 미리 선언이 필요한 컴포넌트는 어떻게 알아?

A. 헤더 파일 위에 class USphereComponent;라고 적어두는 것을 C++에서는 전방 선언이라고 부름.

어떤 컴포넌트를 전방 선언해야 하는지 알아내는 기준은 아주 명확하고 간단함.

💡 핵심 기준: "이 변수가 포인터(*)인가?"

헤더 파일(.h) 안에서 변수를 선언할 때, 그 타입이 포인터(*)나 참조(&) 형태라면 무조건 전방 선언을 할 수 있습니다. (그리고 하는 것이 권장됨.)

예시 코드

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
USceneComponent* Scene;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
USphereComponent* Collision;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Component")
UStaticMeshComponent* StaticMesh;

여기서 Scene, Collision, StaticMesh는 모두 끝에 *가 붙어있는 포인터 변수.
따라서 이 세 가지는 모두 헤더 파일 상단에 #include 대신 전방 선언을 쓰는 것이 좋음.

위 코드의 정석적인 형태는 아래와 같이 세 개 모두 전방 선언을 해주는 것.

class USceneComponent;
class USphereComponent;
class UStaticMeshComponent;

UCLASS()
class TESTPROJECT2_API ABaseItem : public AActor, public IItemInterface
// ... (생략)

(참고: USceneComponent나 UStaticMeshComponent를 전방 선언 안 했는데도 에러가 안 났다면, 위쪽에 인클루드 된 GameFramework/Actor.h 내부 어딘가에서 이미 포함되어 있어서 운 좋게 넘어간 것일 뿐. 명시적으로 다 적어주는 것이 좋습니다.)

🛠️ 왜 #include 대신 전방 선언을 쓸까? (성능 이슈 방지)

  1. 포인터의 크기는 고정
    컴파일러가 .h 파일을 읽을 때, USphereComponent*라는 녀석이 메모리를 얼마나 차지할지 계산해야함. 그런데 포인터는 가리키는 대상이 얼마나 크든 상관없이 무조건 8바이트(64비트 기준).
  2. "이름만 알아도 충분!"
    따라서 컴파일러 입장에서는 USphereComponent의 내부가 어떻게 생겼는지(변수가 몇 개인지, 함수가 뭔지) 당장 .h 파일에서는 알 필요가 없음. "이런 이름의 클래스가 존재한다"는 사실만 알려주면 됨.
  3. 컴파일 시간 단축
    만약 헤더 파일에서 #include "Components/SphereComponent.h"를 해버리면, 컴파일러는 SphereComponent.h 안에 있는 모든 내용과 그 파일이 참조하는 또 다른 파일들을 수만 줄에 걸쳐 다 긁어와야 함. 전방 선언을 쓰면 이 과정을 .cpp 파일로 미룰 수 있어서 빌드 속도가 빨라짐.

📌 요약 가이드: 언제 뭘 써야 할까?

  • 헤더 파일(.h)에서:
  • 컴포넌트를 포인터(*)로만 가지고 있을 때 👉 전방 선언 (class Uxxx;)
  • (예외) 다른 클래스를 상속(public AActor)받거나, 포인터가 아닌 일반 객체로 가지고 있을 때 👉 #include 필수
  • 소스 파일(.cpp)에서:
  • 실제로 그 컴포넌트의 기능(함수)을 사용할 때 (Collision->SetSphereRadius(50.f); 등) 👉 #include "Components/SphereComponent.h" 필수

🤔 컴포넌트 이름은 어떻게 알 수 있나요?
언리얼 에디터에서 컴포넌트를 추가할 때 보이는 이름(예: Sphere Collision)에 접두사 U를 붙여서 유추하거나, 공식 문서 검색, 또는 라이브 코딩 중 IDE(비주얼 스튜디오 등)의 자동 완성 기능을 활용하면 쉽게 정확한 클래스명(USphereComponent)을 알 수 있음.

profile
초보 개발자

0개의 댓글