[Day 14] Actor 클래스 #1

하돌·2025년 1월 20일
0
post-thumbnail

액터 클래스 생성 및 삭제

ArtResource 예제 프로젝트 셋업

이렇게 기존의 언리얼 프로젝트 공간에 폴더로 추가해주었다.

이런식으로 에셋들이 프로젝트 형태로 들어가 있다.

언리얼 엔진에서는 에셋들이 서로 참조하거나 연관되어 있는 경우가 많다.
만약 그 프로젝트 구조가 깨지게 되면 소스들이 깨지는 경우가 많다.
그 구조를 유지하며 에셋을 옮기기 위해 프로젝트 형태로 배포를 했다고 한다.

추가 후에 에픽 게임즈 런처를 재시작해보니 프로젝트가 추가된걸 확인할 수 있었다.


Migrate(이주) 기능 이용

Migrate 기능은 기존 프로젝트에 있던 에셋들이 묶여 있는 그 관계의 구조를 유지하면서 다른 프로젝트로 에셋을 옮길 수 있는 기능이다.

이주 전

이주 후

이렇게 이주가 끝나면 에셋이 다른 프로젝트로 복사되는게 아니라 실제로 이동되어서 기존 프로젝트에서는 아무것도 없는 빈 폴더 구조만 남게 된다.


레벨 기본과 저장

파일->새 레벨 만들기에서 이 창을 볼 수 있다. 기본을 선택 후 레벨을 만들면 장점이 있다.
최소한의 바닥, 조명, 스카이박스만 있어서 빨리 로드가 된다.

만든 레벨은 이런 순서로 저장이 가능하다!


프로젝트 세팅

프로젝트 세팅 창에 들어간다.
맵&모드에서 에디터 시작 맵과 게임 기본 맵을 내가 설정한 이름의 Level(MainLevel)으로 바꿔준다.


UObject와 AActor

UObject

모든 클래스들이 상속받을 수 있는 조상 클래스. 구조상 최상위 객체.

  • UObject는 데이터나 로직을 담당하며, 추상 클래스라 월드에 실체를 배치할 수가 없다.
  • 플레이어의 스탯 같이 뭔가 데이터를 담는 구조라면 보통 UObject를 상속받는다.

가비지 컬렉션(Garbage Collection)

메모리 자동 해제 시스템이다. new나 delete를 쓰지 않아도 된다.

  • 언리얼 엔진은 UObject를 자체 메모리 관리 시스템을 통해 관리
  • new나 delete를 사용하면 안 되는 이유는 언리얼 엔진은 객체를 트래킹하며,
    필요하지 않은 객체를 자동으로 정리.
    • new로 생성하면 엔진이 이를 관리할 수 없기 때문에
      메모리 누수나 충돌이 발생할 수 있다.

Reflection System

언리얼 엔진의 객체와 데이터를 런타임 또는 에디터에서
동적으로 접근하고 조작할 수 있게 해주는 시스템이다.

  • 언리얼의 UObject리플렉션(Reflection)을 지원한다.
  • 이 기능으로 클래스, 변수, 함수 등을 엔진에서 동적으로 조회하거나 사용 가능하다.
  • UCLASS, UFUNCTION, UPROPERTY 등의 매크로를 통해 리플렉션 데이터를 설정한다.
    • 여기서 매크로란? 일반적인 뜻의 매크로랑 같다.
      많은 기능을 매크로 하나로 정의해둬서
      매크로만 호출하면 많은 기능을 실행하는 것
      이다.

왜 리플렉션일까?

"거울에 비춰본다"는 의미에서 나온 개념으로, 프로그램이 자기 자신을 들여다보고
구조를 이해하거나 수정할 수 있는 능력을 말한다.
프로그램 실행 중에 실시간으로 특정 기능을 조회해서 사용 가능하도록 하기 위해서
나온 개념이다.


AActor

  • AActor는 UObject를 상속받으며 월드에 배치할 수 있는 클래스다.
  • 시각적인 요소(캐릭터, 몬스터, 아이템, 파티클)라면 보통 AActor를 상속받는다.
  • 액터는 위치와 회전, 스케일 같은 트랜스폼 데이터를 직접 저장하지 않는다.
  • 루트 컴포넌트트랜스폼을 가지는 컴포넌트를 의미한다.
  • 각 AActor는 무조건 하나 이상의 루트 컴포넌트를 가진다.

컴포넌트의 의미
액터 하나가 모든 기능을 직접 처리하면 너무 복잡하다.
그래서 필요한 기능이나 속성을 작은 부품(컴포넌트) 단위로 나눠서 관리한다.
이 때, 컴포넌트는 액터에 붙여지는 특수한 오브젝트다!

Actor 클래스를 만들겠다 = AActor 클래스를 상속받는 클래스를 만들겠다.
위의 표현은 동일한 의미이다.


C++ 액터 클래스 생성

여기서 상속 받을 클래스가 안 보인다면..
이렇게 모든 클래스를 누르면 저 중 어떠한 클래스든지 상속받아 활용할 수 있다!

액터를 누르면 이런 화면으로 오는데 클래스 타입을 기본 퍼블릭으로하고 생성을 해주면 된다.
라이브 코딩이 활성화 되어있어 클래스 생성 후 바로 라이브 코딩창이 뜨며 결과가 실시간 반영됐다.

아 그리고, Visual Studio에서도 그냥 액터 클래스를 바로 만들 수 있지만
그렇게 하면 언리얼에서만 존재하는 특이한 키워드를 많이 써야하는데 그걸 다 수동으로 써야해서 귀찮기도 하고 정확성이 떨어지는 문제도 있으니 에디터에서 액터를 생성하자.


C++ 액터 클래스 삭제

일단 솔루션 탐색기에서 삭제를 해준다. 우클릭 후 제거를 누르면 사라진다.
하지만 솔루션 탐색기는 가상구조라 저기 보이는 파일을 삭제한다고
실제 물리적인 파일이 사라지지는 않는다.

그래서, 파일 탐색기에서 언리얼 프로젝트에 소스 폴더를 찾아들어가
각각 Private와 Public폴더에 만들어 둔 액터 클래스를 삭제하면 진짜 삭제가 된다!

여기 순서 중요!!
그 다음 에디터를 끄고 다시 Visual Studio에서 빌드하고 디버그를 하면

이렇게 아래쪽에 생성되었던 C++ 클래스 폴더가 다 사라진 것을 확인할 수 있다!

에디터 종료 → 솔루션 Remove → 물리 파일 삭제 → (Visual Studio 빌드) → 에디터 재실행
위 순서를 지키면 안전하다. 근데 여기서 에디터 종료는 굳이 할 필요가 없다.

어차피 제거하고 빌드할때 디버깅을 중지하면서 에디터가 종료되기 때문..
몇 번 실험을 해보았지만 딱히 오류가 난적도 없다.

<최적화 순서>
솔루션 Remove → 물리 파일 삭제 → (Visual Studio 빌드) → 에디터 재실행

물리 파일 삭제도 건너뛰어도 에디터가 실행되지만
그 경우 Visual Studio에 남아있는 가상구조의 파일이 사라지지 않는다. 그래서 나중에 지우는 걸 까먹거나 그러면 헷갈릴 수 있으니 위험하다.


액터 클래스에 컴포넌트 추가

언리얼 C++ 액터 클래스

분명 아이템 클래스를 Item이라고 이름 지었는데 Item앞에 A가 붙어 AItem이 되었다.
Actor 클래스도 마찬가지로 AActor가 되었다.
언리얼에서는 클래스 이름에 'A'같은 접두어를 붙이는 것을 권장한다.
그래서 클래스 생성할 때 자동으로 'A'를 붙여준다.
그 접두어 'A'의 의미는 '이 클래스는 액터 클래스다.'라고 알리는 의미다.

class와 AItem 사이에 있는 _API로 끝나는 키워드는 클래스를
모듈 밖에서도 쓸 수 있게 하기 위한 일종의 매크로라고 할 수 있다.
그리고 _API 앞에는 보통 작성한 프로젝트의 이름이 들어간다.
빌드할 때 중요한 매크로라는 것만 알고 넘어가자.

모듈이란? 관련된 기능끼리 모아둔 코드의 집합이다!
여기서 TutorialPractice는 코드 집합들을 하나로 모은 폴더라서 모듈이라고 할 수 있다.


언리얼 블루프린트 액터 클래스

블루프린트로 액터 클래스를 만들면 안에 루트 컴포넌트가 포함되어 있기 때문에
좌표가 0, 0으로 가지 않으며, 원하는 좌표에다 갖다 놓으면 그 좌표에다 액터가 생성된다!
반면, C++ 액터 클래스는 루트 컴포넌트가 없어 트랜스폼을 저장할 무언가가 없기 때문에
자동으로 좌표가 0, 0으로 설정된다.


블루프린트 컴포넌트 사용법

블루프린트 액터 클래스에 컴포넌트란게 있는데 액터에 붙여쓰는 부품 정도로 생각하면 된다.
저렇게 오디오를 추가해주면 해당 액터는 오디오 기능이 있는 액터가 되는 것이다!
그렇다면 어떤 컴포넌트가 루트 컴포넌트로 적합할까?

  • 루트 컴포넌트는 액터의 계층 구조에서 최상위에 위치하는 컴포넌트로,
    액터의 기본적인 트랜스폼(좌표 정보)을 관리한다.
  • 씬 컴포넌트는 시각적 요소는 없지만 트랜스폼 정보를 가지고 있기 때문에
    루트 컴포넌트로 사용하기에 최적화
    되어 있다.
  • 만약 액터에 하나의 컴포넌트만 사용한다면,
    해당 컴포넌트를 루트 컴포넌트로 지정해도 문제가 없다.

Static Mesh 컴포넌트

Static은 고정된, Mesh는 모델이란 뜻으로 고정된 모델.
즉, 게임에서 아이템, 배경, 건물, 오브젝트 등이 Static Mesh가 되는 것이다.

특징

  • Static Mesh 속성을 가지고 있다. 3d 모델의 모양을 결정한다.
  • Material 속성을 가지고 있다. 3d 모델의 스킨을 결정한다.
    ㄴ그래서 다양한 Material을 주는 것으로 같은 모델이라도 다양한 느낌을 줄 수 있다.
    ㄴ롤이나 오버워치 스킨을 생각하면 된다.

C++에서 컴포넌트 사용법

각 컴포넌트들을 포인터 타입의 멤버변수로 추가한다.
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));

  • 포인터에 SceneComponent를 가리키도록 컴포넌트를 생성하고 그 주소를 가리키는 함수
    CreateDefaultSubobject<>()를 대입한다.
  • <>에는 컴포넌트의 타입, ()에는 컴포넌트의 이름을 지정한다.

SetRootComponent(SceneRoot);

  • SetRootComponent()를 이용하면 괄호 안의 컴포넌트를 루트 컴포넌트로 만들 수 있다!

StaticMeshComp->SetupAttachment(SceneRoot);

  • StaticMesh컴포넌트는 루트 컴포넌트 안에 자식 관계로 형성이 되어야 한다.
  • ->SetupAttachment()를 이용하면 해당 컴포넌트를 괄호 안의 컴포넌트에 붙여
    부모, 자식 관계를 만들 수 있다.

C++ 액터 클래스의 컴포넌트 차이 확인

컴포넌트 없는 버전

트랜스폼을 저장하는 루트 컴포넌트가 없으면 이렇게 0, 0에 좌표가 고정된다.

컴포넌트 있는 버전

이렇게 원하는 좌표대로 Item 액터가 배치된 것을 확인할 수 있다.

에디터의 디테일 창에서 컴포넌트 확인

분명히 C++에서 씬 컴포넌트에서 RootComponent를 만들고 거기다 Static Mesh라는 컴포넌트를 붙였지만 디테일 창에서는 확인이 되지 않는다.
C++에서 만든 것을 에디터 상에서 확인하려면 리플렉션 시스템에 등록을 해야 한다!

루트 컴포넌트가 보이는 이유는 언리얼에서 루트 컴포넌트는
자동으로 리플렉션 시스템에 등록이 되기 때문이다.


C++로 스태틱 메시, 머티리얼 설정

우선 컨텐츠 드로어에서 Props에 들어가면 스태틱 메시들을 확인할 수 있다. 거기서 원하는 스태틱 메시를 고르고 레퍼런스 복사 후 C++로 돌아와서 복붙해준다.

그럼 이렇게 복사가 되는데 앞에 흰 글씨는 지워줬다.
참고로 /Game 이란 경로는 언리얼에서 /Content 경로를 말한다.

머티리얼도 마찬가지로 가져왔다. 머티리얼과 머티리얼 인스턴스는 다른 것이니 주의하자.


자, 이제 메시랑 머티리얼 같은 것을 언리얼로 로드해주는 클래스가 있다.

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

그 클래스는 ConstructorHelpers다.
FObjectFinder는 말그대로 오브젝트를 찾아주는 기능이다.
<>안에는 등록할 오브젝트의 종류를 넣으면 된다.
그 뒤에 변수의 이름을 설정한다.(MeshAsset)
()안에는 스태틱 메시를 설정할 것이니 방금 전 복사한 스태틱 메시의 경로를 넣어준다!

if (MeshAsset.Succeeded)
{

}

메시 에셋이란 변수가 경로를 잘 찾았는지 확인해준다.


자, 이제 스태틱 메시 설정은 다 했으니 똑같이 머티리얼 설정을 하면 된다.

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

이렇게 이름만 바꿔서 만들어주면
StaticMeshComp->SetStaticMesh(MaterialAsset.Object); 여기서 오류가 난다.
머티리얼은 스킨이라고 이해했다. 한 챔피언의 스킨은 여러 개가 될 수 있기 때문에
몇번째 스킨으로 설정할건지 괄호 안에 index를 넣어야한다.

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

이렇게하면 끝!!

C++에서 선택한 스태틱 메시인 의자빨간 포션의 머티리얼이 잘 적용되는 것을 볼 수 있다!


숙제

1. 컴포넌트에 컴포넌트를 붙여보기

- `UAudioComponent` 같은 다른 컴포넌트를 `CreateDefaultSubobject`로 생성해 보고, StaticMeshComp에 `AudioComp->SetupAttachment(StaticMeshComp)` 사용해봅시다.
- `Audio` 폴더에 마음에 드는 사운드 큐 (Sound Cue)를 에셋으로 할당해봅시다.

Item.h 파일에 UAudioComponent를 추가했을 때는 딱히 문제가 없었다.
하지만 이런식으로 추가하려다 보니
포인터가 불완전한 형식이라고 오류가 났다. 구글링했더니 UAudioComponent 헤더 파일이 없어서 그렇다고 한다.

위 Item.h 파일에서 GameFramework/Actor.h을 포함하면
모든 컴포넌트를 쓸 수 있을 줄 알았다.

하지만, UAudioComponent는 Actor.h에 포함되어 있지 않아서
UAudioComponent 타입에 대한 클래스 정의가 없기 때문에 오류가 난 것이다.

그래서, 위 Item.h파일에 "Components/AudioComponent.h"
명시적으로 추가
해주면 문제가 해결된다.

만약 다른 컴포넌트를 추가할 때도 이런 불완전한 포인터 오류가 나면
같은 방법으로 해결이 가능할 것이라 예측한다.

static ConstructorHelpers::FObjectFinder<USoundCue> AudioAsset(TEXT("/Game/Resources/Audio/Starter_Background_Cue.Starter_Background_Cue"));
if (AudioAsset.Succeeded())
{
	AudioComp->SetSound(AudioAsset.Object);
}

위 코드에서 USoundCue타입이 필요해서 헤더에 #include "Sound/SoundCue.h"를 추가!

이렇게 사운드 큐가 추가된 것까지 확인했다!


2. 다양한 컴포넌트 생성하고 초기화하기

  • 이 “액터 + 컴포넌트” 구조에 익숙해지는 것이 중요합니다.
  • 다양한 컴포넌트를 Root Component에 한 번씩 붙여 보면서,
    각각 에디터에서 어떻게 보이는지 실험해보세요.

일반적으로 많이 쓰이는 나이아가라 파티클 시스템 컴포넌트와 포인트 라이트 컴포넌트까지 실험해보았다.


포인트 라이트 컴포넌트 추가

#include "Components/PointLightComponent.h" 헤더 파일에 이것만 추가하면 되었다.
그 후 포인트 라이트는 ConstructorHelpers::FObjectFinder방식을
쓰지 않는다는 것을 깨달았다.

ConstructorHelpers::FObjectFinder란?

  • 외부 리소스(예: USoundCue, UStaticMesh, UMaterial 등)를
    런타임 전에 로드하는 데 사용
  • 리소스는 Content 폴더 아래의 에셋으로 존재하며, 특정 경로로 참조
  • 예) 사운드 큐, 메시, 머티리얼 등과 같은 에셋을 로드할 때 사용

그래서, 포인트 라이트는 에셋이 아닌 엔진 내장 클래스라 위 방식이 필요없는 것이다!
아래처럼 바로 포인트 라이트의 속성을 지정하면 된다.

정리

  • 에셋은 ConstructorHelpers::FObjectFinder로 로드해서 컴포넌트에 설정
  • 내장 클래스는 바로 생성 후 속성을 설정
PointLightComp = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLight"));
PointLightComp->SetupAttachment(SceneRoot);
PointLightComp->SetIntensity(3000.0f); // 밝기 조절
PointLightComp->SetLightColor(FLinearColor::White); // 빛 색상 조절


나이아가라 파티클 시스템 컴포넌트 추가

우선 나이아가라 파티클은 에셋 형태로 받아와야 한다. 그러려면 기존의 캐스케이드 파티클을 최신 버전의 나이아가라 파티클로 바꾸어 주어야 했다.
내가 직접 나이아가라 생성하는 방법도 있지만 일단 플러그인을 이용해
기존 에셋을 나이아가라 파티클로 바꾸었다.
이렇게 P_Explosion이 바뀐 것을 확인할 수 있다!

나이아가라 컴포넌트를 쓰기 위해서 헤더파일을 추가해야했다. 하지만 헤더 포함에서 오류가 나버린다.

나이아가라 관련 라이브러리를 쓰기 위해선 모듈에 "Niagara"를 추가해야 한다.

Why? 언리얼 엔진의 모듈 시스템 때문

Unreal Engine은 대규모 게임 엔진이므로, 기능을 분리하여 관리하기 위해 모듈(Module) 개념을 사용한다.

  • 각 기능(예: 렌더링, 물리, 오디오, 나이아가라 등)은 독립적인 모듈로 나뉘어져 있다.
  • 이러한 모듈들은 필요할 때만 프로젝트에 추가되며,
    필요하지 않은 기능은 빌드 과정에 포함되지 않도록 최적화되어 있다.
  • 그래서, 특정 기능(예: 나이아가라)을 사용하려면 Build.cs 파일에
    해당 모듈을 추가해야 한다.

그렇게 해야 관련된 헤더 파일을 포함할 수 있고,
컴파일과 빌드 과정에서 제대로 작동하게 된다.

이렇게 나이아가라를 모듈에 추가한다.
그래도 해결이 안되어서

  • .uproject 파일에도 모듈 추가하기
  • 임시 빌드, 캐시 파일 삭제하기
  • 나이아가라 관련 라이브러리 다 추가하기(의존성 고려)
  • 언리얼 에디터에서 나이아가라 관련 플러그인 다 활성화하기

를 해보았지만 다 답이 없었다.
그리고 마지막에 에디터에서 Visual Studio 2022 프로젝트 새로고침을 눌렀다..

문제가 해결되었다!! 의존성 고려해서 넣은 헤더 포함을 빼도 오류가 안 나는 것을 보아하니 그것은 문제가 아니였다.
모듈 추가/변경 후에는 Visual Studio를 재시작하는 과정이 필요하다는 것을 깨달았다.

안심한 후 빌드를 하려는데 또 오류가 떴다..
gpt에게 물어보니 포인터 타입이 잘못되었다고 한다. 아래 설명을 보자..

기존 리소스 타입 (SoundCue, Material, StaticMesh)

  • UObject* 기반으로 관리
  • FObjectFinder 방식과 완벽히 호환
  • Unreal Engine 4부터 사용된 방식이며, 현재도 유지

NiagaraSystem

  • Unreal Engine 5에서 TObjectPtr이라는 스마트 포인터를 통해 관리
  • TObjectPtr은 기존 UObject*보다 안전하고 최적화된 방식.
  • 하지만 FObjectFinder는 UObject*와만 호환되므로, NiagaraSystem과 충돌이 발생

결론은 NiagaraSystem은 Unreal Engine 5로 업데이트 되면서 만들어진 최신 기능이라
포인터 타입이 기존 UObject*와 다르기 때문에
FObjectFinder 방식과 호환되지 않아 아래 같은 방식을 써야한다.
LoadObject<UNiagaraSystem> 방식!

FString AssetPath = TEXT("/Game/Resources/Particles/P_Explosion_Converted.P_Explosion_Converted");
UNiagaraSystem* NiagaraSystemAsset = LoadObject<UNiagaraSystem>(nullptr, *AssetPath);
if (NiagaraSystemAsset)
{
	NiagaraComp->SetAsset(NiagaraSystemAsset);
}

이제 모든 오류는 해결되었다.

하지만 파티클이 처음 1번만 실행되니 사진을 찍을 수가 없었다..
그래서 BeginPlay()와 약간의 설정을 통해 파티클 무한 재생을 해주었다!

파티클 무한 재생은 나이아가라 파티클 파일을 컨텐츠 드로어에서 찾아 에디터를 열고 원하는 파티클의 Emitter State를 Infinite로 바꿔주면 파티클 무한 재생이 가능하다!
이 때 Tick 함수를 써도 되지만 계속 프레임당 함수가 호출되는 것이니 메모리 낭비가 심해서 웬만하면 쓰지말라고 들어서 일부러 쓰지 않았다!

그렇게 지금까지 만든 씬 컴포넌트, 스태틱 메시 컴포넌트, 오디오 컴포넌트, 포인트 라이트 컴포넌트, 나이아가라 파티클 시스템 컴포넌트를 합쳐 불타는 의자를 만들었다!!


최종 작성 코드

Item.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/AudioComponent.h"
#include "Components/PointLightComponent.h"
#include "NiagaraComponent.h"
#include "NiagaraSystem.h"
#include "Sound/SoundCue.h"
#include "Item.generated.h"

UCLASS()
class TUTORIALPRACTICE_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();
	
protected:
	USceneComponent* SceneRoot;
	UStaticMeshComponent* StaticMeshComp;
	UAudioComponent* AudioComp;
	UPointLightComponent* PointLightComp;
	UNiagaraComponent* NiagaraComp;

	virtual void BeginPlay() override;
};

Item.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Item.h"

// Sets default values
AItem::AItem()
{
	SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
	SetRootComponent(SceneRoot);

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

	AudioComp = CreateDefaultSubobject<UAudioComponent>(TEXT("Audio"));
	AudioComp->SetupAttachment(StaticMeshComp);

	PointLightComp = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLight"));
	PointLightComp->SetupAttachment(SceneRoot);
	PointLightComp->SetIntensity(3000.0f); // 밝기 조절
	PointLightComp->SetLightColor(FLinearColor::White); // 빛 색상 조절

	NiagaraComp = CreateDefaultSubobject<UNiagaraComponent>(TEXT("NiagaraParticle"));
	NiagaraComp->SetupAttachment(SceneRoot);

	// '/Game/Resources/Props/SM_Chair.SM_Chair'
	// '/Game/Resources/Materials/M_Potion.M_Potion'
	// '/Game/Resources/Audio/Starter_Background_Cue.Starter_Background_Cue'
	// '/Game/Resources/Particles/P_Explosion_Converted.P_Explosion_Converted'

	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_Potion.M_Potion"));
	if (MaterialAsset.Succeeded())
	{
		StaticMeshComp->SetMaterial(0, MaterialAsset.Object);
	}

	static ConstructorHelpers::FObjectFinder<USoundCue> AudioAsset(TEXT("/Game/Resources/Audio/Starter_Background_Cue.Starter_Background_Cue"));
	if (AudioAsset.Succeeded())
	{
		AudioComp->SetSound(AudioAsset.Object);
	}

	FString AssetPath = TEXT("/Game/Resources/Particles/P_Explosion_Converted.P_Explosion_Converted");
	UNiagaraSystem* NiagaraSystemAsset = LoadObject<UNiagaraSystem>(nullptr, *AssetPath);
	if (NiagaraSystemAsset)
	{
		NiagaraComp->SetAsset(NiagaraSystemAsset);
	}

	// 자동 실행 설정
	NiagaraComp->bAutoActivate = true; // 액터가 스폰될 때 자동으로 실행
}

void AItem::BeginPlay()
{
	Super::BeginPlay();

	if (NiagaraComp)
	{
		NiagaraComp->Activate(); // 파티클 실행
	}
}
profile
게임 개발자로서 배운 것을 정리한 블로그입니다. 벨로그 서버가 불안정한 거 같아서 티스토리로 옮겼습니다.

0개의 댓글