
구조체란 기존에 존재하는 데이터 타입들을 조합하여 새로운 데이터 타입을 만들어 내는 유용한 개념이다
예를 들어 정수인 int, 실수인 float, 문자열인 string, 이 3가지 타입을 이용해서 int로 hp 회복량, float으로 물약 재사용 쿨타임, string으로 물약 이름을 정의해서 구조체로 묶으면 물약의 데이터를 저장할 수 있는 새로운 데이터 타입을 만들어 내는 방식이다
일반적인 C++ 프로젝트에서는 구조체를 대충 이렇게 정의하고 사용한다
하지만 언리얼 엔진 프로젝트에서 이러한 구조체는 C++ 코드 내부에서는 사용할 수 있지만, 에디터의 디테일 패널에 노출되지 않고 블루 프린트에서도 사용이 불가능하다
그렇다면 이제 이러한 언리얼 구조체에 대해서 알아보자
우선 구조체를 만들어보기 위해 StarterContent는 포함할 필요 없이 C++ 타입으로 빈 프로젝트를 생성한다
만약 블루프린트에서만 사용할 구조체라면 이렇게 콘텐츠 브라우저 패널의 Content폴더 내에서 우클릭하여 [Bluepirnts > Structure]를 선택해서 생성하는 것으로 만들 수 있다
하지만 이렇게 생성한 블루프린트 구조체들은 언리얼 C++ 코드에서는 사용할 수 없다
이와 반대로 C++ 코드에서 만든 구조체는 C++ 코드는 물론 블루프린트에서도 사용할 수 있다는 장점이 있다
하지만 언리얼 C++ 구조체는 블루프린트 구조체의 간단한 생성 방법과 비교했을 때, 엔진 내부에서 명시적인 생성 방법이 없기 때문에 생성 과정이 조금 복잡하다
우선 직접 정의한 언리얼 구조체를 담을 헤더를 만들어야 한다
만약 구조체가 특정한 클래스에서만 자주 사용될 것이라면 그 클래스의 헤더파일 하단에 구조체를 정의하는 편이 좋지만, 범용적으로 사용될 구조체라면 커스텀 구조체만 정의할 헤더를 만들어서 거기에 몰아서 정의해 두고 사용할 때마다 헤더를 include 하는 편이 좋다
지금은 범용적으로 사용하는 구조체를 위해서 전용 헤더 파일을 생성한다고 가정하고 진행해 보자
메뉴바에서 [Tools > New C++ Classes] 항목을 선택해서 부모 클래스를 "None"으로 선택한 다음 클래스를 생성한다
(이름은 TestStruct로 지어보자)
TestStruct 클래스가 생성되고 나면 TestStruct 클래스의 헤더 파일로 가서 #include "CoreMinimal.h" 선언 아래에
#include "TestStruct.generated.h"를 선언해준다 (클래스 이름.generated.h)
그다음에는 먼저 일반 C++에서 사용되는 기본 구조체를 만들어본다
[여기서 AActor은 언리얼엔진에서 사용되는 클래스이며, 게임 세계에서 존재하는 모든 객체(액터)의 기본 클래스로 사용된다. 그래서 물리엔진과 상호작용하며 게임 세계 내에서 위치, 회전, 스케일 등을 나타내는 기능을 제공한다]
여기서부터 언리얼 구조체 만의 특별한 작업을 진행해 볼 것이다
이전 글에서 변수를 생성하면서 UPROPERTY 매크로를 사용해봤고, C++클래스를 생성할 때 클래스 앞에는 UCLASS 매크로가 붙는 걸 본 적이 있을 것이다
언리얼 구조체에서는 이와 비슷하게 USTRUCT 매크로를 붙여주면 된다.
지정자로는 Atomic과 BlueprintType을 넣어주자[Atomic은 멤버 변수에 대해 멀티스레딩 환경에서 안전한 동시 접근을 보장하고, BlueprintType은 이 구조체가 블루프린트에서 사용될 수 있게 한다]
그리고 언리얼 구조체의 이름은 그냥 C++ 구조체 와는 다르게, F(Framework)로 시작하도록 언리얼 코딩 표준에 명시되어 있다
언리얼엔진에서 제공하는 다양한 구조체들은 대부분 'F'로 시작되고, 나중에 다룰 내용인 FVector, FRotator, FString, FTransform 등이 여기에 해당된다. F로 시작하는 구조체를 정의하지 않더라도 문법적으로는 문제가 없지만, 코드의 가독성을 해치고, 표준 규정에 어긋나므로 F를 넣어주는 것이 좋다
그리고 구조체 제일 첫 줄에 GENERATED_USTRUCT_BODY 매크로를 넣어준다
그다음엔 댕글링 포인터 문제에 대해 보호받기 위해서 구조체의 모든 멤버 변수에 UPROPERTY 매크로를 붙인다
[UPROPERTY 매크로의 지정자는 EditAnywhere과 BlueprintReadWrite를 넣어준다]
구조체가 디테일 창에서는 보이지 않고 코드 내부나 블루프린트에서만 사용되기를 원한다면 USTRUCT 매크로의 지정자를
BlueprintType으로, UPROPERTY 매크로의 지정자를 BlueprintReadWrite로만 설정하면 된다
(추가로 구조체의 멤버 변수로 포인터를 사용한다면 깊은 복사와 얕은 복사 문제에도 주의를 해야 한다)
이제 완성한 구조체를 사용해 보자
코드를 모두 저장 후 에디터로 돌아간 다음, 부모 클래스로 Actor 클래스를 선택해서 구조체 테스트용 TestActor를 생성해 보자
Actor클래스 생성이 완료되면 커스텀 구조체가 들어있는 헤더 파일을 TestActor 클래스의 헤더에 include 선언해준다
그리고 UPROPERTY 매크로에 EditAnywhere 지정자를 넣어주고 아까 TestSturct 클래스 안에서 생성했던 FCustomStruct 구조체를 사용해서 구조체 변수를 만들어준다
코드 작성이 끝나면 [Ctrl + Shift + 'S']로 모두 저장하고 컴파일한다
컴파일이 끝나고 난 뒤 TestActor를 월드에 배치하고 디테일 패널을 보면 커스텀 구조체의 내용이 수정 가능하도록 노출된 것을 확인할 수 있다
그렇다면 이렇게 생성한 구조체를 블루프린트에서 사용하는 방법을 알아보자
C++ 코드에서 정의한 구조체를 블루프린트에서 사용하는 방법은 아주 간단하다
우선 상단 툴 바에서 블루프린트 드롭다운을 열고 New Empty Blueprint Class를 선택해서 Actor를 부모클래스로 생성한다
그리고 블루프린트를 생성할 경로를 선택하고 이름을 지정해 준 다음 Save 버튼을 누르면 블루프린트 클래스가 생성이 된다
그리고 생성한 클래스를 더블클릭해서 열어주고, 블루프린트 에디터의 내 블루프린트 패널에서 변수를 새로 추가한다
그다음 My Blueprint 패널에서 VARIABLES 옆에 + 버튼을 눌러서 변수를 생성하고, 여기서 변수 타입을 Custom Struct로 바로 변경하거나, 디테일 패널에서 구조체를 선택해 주면 된다
이렇게 변수로 사용하는 방법 외에도 이벤트 그래프에 우클릭한 뒤 구조체의 이름을 검색하고 이벤트 진행 도중에 구조체 변수를 만들거나 구조체를 분해해서 변수를 따로 뽑아 사용할 수도 있다
그렇다면 이제 언리얼 에디터에서 기본으로 제공하는 대표적인 구조체 몇 가지를 알아보자
언리얼엔진의 프로젝트 폴더의 헤더파일을 보면 굉장히 많은 구조체가 선언되어 있는데, 시진으로 보이는 구조체들은 그저 일부이다
그 중에서도 우선 FVector 타입에 대해서 알아보자면, FVector 구조체는 3차원 공간에서의 벡터를 표현한다. 이 구조체는 순서대로 'X', 'Y', 'Z' 세 개의 구성원을 가지며, 각 요소는 float 값을 나타내고, 위치, 방향 크기 등을 표현하는데 사용된다. 예를 들어, 오브젝트의 위치를 설정하거나 두 오브젝트 간의 거리를 구하는데 사용할 수 있다
예시로 다시 TestActor 클래스의 헤더파일로 가서 FVector 타입의 MyVector를 선언해주고, 선언으로만 끝나면 기본값인 (0, 0, 0)이 할당되므로 다른 값을 할당해준다
이때, 구조체 같이 복잡한 타입의 경우에는 생성자를 사용해서 값을 넣어주어햐 하는데, 생성자는 항상 데이터 타입과 이름이 같다
그래서 FVector(10, 20, 30)값을 지정해주게 되면 X값에는 10, Y값에는 20, Z값에는 30이 할당하게 될 것이다
그리고 이 코드가 언리얼 에디터에서 보이게 할 수 있도록 UPROPERTY 매크로를 붙이고 EditAnywhere 지정자를 넣어준다
실제 값을 확인하기 위해 코드를 모두 저장을 한 다음 컴파일을 한다
그리고 TestActor가 배치된 상태에서 디테일 패널을 보면, My Vector값이 우리가 지정해준 (10, 20, 30)으로 변경되어 있는 것을 확인 할 수 있다
만약 이 값을 다른 변수에서 사용하고 싶다고 한다면 어떻게 해야 할까?
그대로 또 다른 FVector 타입의 변수를 생성하고 값을 할당해도 되겠지만, 지금은 이 3요소(X, Y, Z) 중에서 하나의 값만 가져올 것이다
앞서 각각의 요소들은 float값을 나타낸다고 했는데, 그렇기 때문에 FVector 전체 값을 float 변수 하나에 가져오면 당연히 에러가 생긴다
그러면 우선 X의 값만 가져오기 위해서 float 타입으로 GetX 변수를 선언해주고 값을 초기화 한 다음, UPROPERTY 매크로를 넣어준다
그리고 소스파일로 가서, 게임이 플레이 되자마자 코드가 실행될 수 있도록 BeginPlay( )함수로 간 다음, MyVector의 X요소를 가지고 와서 float 타입의 GetX 변수에 할당해준다
이때 쓰인 멤버 접근 연산자 중 하나인 '점 연산자(.)'는 특정 클래스나 구조체의 멤버(변수, 함수)에 직접적으로 접근할 때 사용되는 연산자이다. 그래서 MyVector 구조체의 X요소에 직접적으로 접근해서 그 값을 float 타입에 넣어준 것이다
이제 확인해보기 위해 코드를 모두 저장하고 컴파일을 해준다
게임을 플레이하기 전에는 0.0으로 값이 초기화 되어 있지만 BeginPlay( )함수에 의해 게임이 플레이 되고 나서는 TestActor를 선택해 보면, GetX의 값이 MyVector의 X요소 값과 동일해진 것을 볼 수 있다
VR게임 개발을 위한 언리얼엔진/C++ 공부한 내용 끄적이기...
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!