언리얼 엔진의 코딩 표준
코딩 표준은 프로그래밍을 작성해야하는데 지켜야하는 프로그래밍 이름 규칙이나 작성 방법들을 지정한 가이드라인이다. 코딩 스타일이나 코딩 컨벤션이라고도 한다.
그렇다면 좋은 코딩 스타일은 무엇일가??
- 절대적으로 좋은 것은 없다.
- 중요한 것은 코딩 스타일을 정하고 잘 따르는 것에 있다.
- 이미 프로젝트에 적용된 코딩 스타일을 잘 숙지하고 따라야한다.
그렇다면 이제부터 우리만의 코딩 스타일을 정해서 작성하면 될까? 또는 이미 C++의 코딩 스타일을 유지하면 될까?
아니다. 언리엘 엔진 자체에서 코딩 표준을 정해서 문서화 해놓았다.
이는 권고따위가 아니라 반드시 지켜야하는 수준이다.
언리얼 공식 사이트에 따르면 코딩 스타일을 따르는 이유는 다음과 같다고 한다.
- 소프트웨어의 총 수명 비용 중 80%는 유지보수에 소모됩니다.
- 최초 작성자가 그 소프트웨어의 수명이 다할 때까지 유지보수하는 경우는 거의 없습니다.
- 코딩 규칙을 사용하면 소프트웨어의 가독성을 향상하여 엔지니어가 새로운 코드를 더 빠르고 완벽히 이해할 수 있습니다.
- 에픽에서 소스 코드를 모드 개발자 커뮤니티에 공개할 경우 코딩 규칙을 알고 있으면 이해하기 더 쉽습니다.
대다수의 코딩 규칙이 크로스 컴파일러 호환성에 필요합니다.
여기서 중점적으로 바라보는 것은 업무의 효율성이다.
1. 클래스 체계
클래스를 생성할 때에는 누구든 알아보기 쉽게 public과 private를 반드시 선언해야한다.
UCLASS()
class TASKPROJECT_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public :
private:
};
2. 명명규칙
- 파스칼 케이싱 : 합성어의 첫 자를 대문자로 사용하는 방법
- 템플릿 클래스에는 접두사 T를 포함합니다.
- UObject 에서 상속받는 클래스에는 접두사 U를 포함합니다.
- AActor 에서 상속받는 클래스에는 접두사 A를 포함합니다.
- SWidget 에서 상속받는 클래스에는 접두사 S를 포함합니다.
- 추상적 인터페이스인 클래스에는 접두사 I를 포함합니다.
- 에픽의 개념이 유사한 클래스 타입( TModels 타입 특성에 첫 번째 아규먼트로 사용)에는 접두사 C를 포함합니다.
- 열거형에는 접두사 E를 포함합니다.
- 부울 변수는 접두사 b를 포함합니다(예: bPendingDestruction 또는 bHasFadedIn ).
- 그 외 대부분의 클래스는 접두사 F를 포함합니다. 그러나 일부 서브시스템은 다른 글자를 사용하기도 합니다. (언리얼에서 상속받지 않는 클래스의 경우 이를 사용한다)
- Typedef의 경우 해당 타입에 적합한 접두사를 사용합니다. 예를 들어 구조체의 typedef인 경우 F, UObject 의 typedef인 경우 U를 사용합니다.
- bool 타입은 질의형의 이름으로 선언한다.
3. 포터블 C++ 코드
언리얼에는 우리가 사용하는 int가 없다. 그 이유는 int는 기본적으로 32비트를 보장하는 것이지 이는 64비트가 될 수 도 있다. 그렇기 때문에 확실한 값인 __int32를 int32로 파싱해 사용한다.
bool - 부울 값(부울 크기 추정 금지). BOOL 은 컴파일되지 않습니다.
TCHAR - character(문자) (TCHAR 크기 추정 금지)
uint8 - unsigned byte(부호 없는 바이트) (1바이트)
int8 - signed byte(부호 있는 바이트) (1바이트)
uint16 - unsigned 'shorts'(부호 없는 'short') (2바이트)
int16 - signed 'short'(부호 있는 'short')(2바이트)
uint32 - unsigned int(부호 없는 int) (4바이트)
int32 - signed int(부호 있는 int) (4바이트)
uint64 - unsigned 'quad word'(부호 없는 '쿼드 단어') (8바이트)
int64 - signed 'quad word'(부호 있는 '쿼드 단어') (8바이트)
float - 단정밀도 부동 소수점(4바이트)
double - 배정밀도 부동 소수점(8바이트)
PTRINT - 포인터를 가질 수 있는 정수(PTRINT 크기 추정 금지)
4. Const 정확도
const로 선언하는 것은 불변한다.
일반적으로 const 값으로 동작할 수 있는 경우는 좀 많다. 몇번 고정적으로 값이 바꾸지 않은 상태로 loop를 도는 경우 등 이럴 때마다 항상 고정적으로 const를 명명하라는 것이다.
누군가 코드를 봤을 때 이 친구는 바꾸면 안되는 변수구나라는 것을 한눈에 알 수 있고 컴파일 타임에서 사전에 체크를 동반하기 때문에 정확성이 높아지게 된다.
void SomeMutatingOperation(FThing& OutResult, const TArray<Int32>& InArray)
{
// InArray는 여기서 수정되지 않지만, OutResult는 수정될 수도 있습니다.
}
void FThing::SomeNonMutatingOperation() const
{
// 이 코드는 자신을 호출한 FThing을 수정하지 않습니다.
}
TArray<FString> StringArray;
for (const FString& : StringArray)
{
// 이 루프의 바디는 StringArray를 수정하지 않습니다.
}
//const가 아닌 경우에는 어떻게 내가 돌아가는지 명확하게 아는 경우에만 사용하는 것이 좋음.
//이럴떄는 주석을 다는 것이 좋음.
//포인터의 Case
T* const Ptr = ...;
//특정 포인터에 대해서 증감 연산자를 사용하기 싫다면 const를 붙여서 지정하는 방법이 있음.
//하지만 포인터에 존재하는 데이터는 변경이 가능함.
// 틀림
T& const Ref = ...;
//Reference의 경우에는 사실 애초에 const로 명명되는 것이다 보니까 또 const 붙여봤자 큰 의미도 없음.
// 나쁜 예 - const 배열 반환
//이렇게 하면 복사가 일어나기 때문에 메모리 할당이 증가.
const TArray<FString> GetSomeArray();
// 좋은 예 - const 배열로의 레퍼런스 반환
const TArray<FString>& GetSomeArray();
// 좋은 예- const 배열로의 포인터 반환
// 포인터가 가리키는 데이터는 변경이 불가하나, 증감 연산은 가능.
const TArray<FString>* GetSomeArray();
// 나쁜 예 - const 배열로의 const 포인터 반환
// 증감 연산도 안되고 바꾸기도 안된다. 아니 그러면 순회는 어떻게 하냐
const TArray<FString>* const GetSomeArray();
이외에도 중요한 부분이 몇개 있지만 생략하겠다.
기본 타입과 문자열
언리얼에서는 문자를 다룰 때 내부적으로 UTF-16을 사용한다.
배열로 사용할 때에는 TCHAR를 사용하는데 뒤에 나올 대부분 문자열로 사용한다.
TCHAR LogCharArray[] = TEXT("Hello Unreal");
문자열을 사용할 때에는 FString을 이용한다.
주의해야할 점은 TCHAR[]도 문자열이긴 하지만 배열인 것이고 FString도 문자열이지만 배열이 아니다.
FString LogCharString = FString(TEXT("Hello Unreal"));
UE_LOG(LogTemp, Log, TEXT("%s"), *LogCharString);
//FString에서는 FString이 감싸고 있는 문자열이 뭔지를 알고 싶다면 포인터를 이용해서 까봐야 한다.
- 언리얼은 유니코드를 사용해서 문자열 처리를 통일화 시킨다는 것을 알았다!⇒ 유니코드를 위한 언리얼 표준 캐릭터 타입인 : TCHAR을 사용하였다~
- ⇒ 그 중에도 2바이트 사이즈로 균일한 UTF-16을 사용하였고,
- 문자열은 언제나 TEXT 매크로를 사용해서 지정하였다.
- ⇒ TEXT 매크로로 감싼 문자열은 TCHAR 배열로 지정이 된다.
- 문자열을 다루는 클래스로 FString을 제공한다.⇒ 다양한 조작은 가능하지만 가져 올라면 포인터를 사용해야 했지?
- ⇒ FString은 TCHAR 배열을 포함하는 헬퍼 클래스이다.
유용한 문자열 함수를 FCString에서 제공한다. FString은 TCHAR를 관리하고 이를 사용하려면 *를 이용해야한다. 그런데 이러한 FString을 하나더 감싸는게 바로 FCString이다.
- Printf (출력하기)
- SanitizeFloat, FromInt (Float나 Int에서 문자열을 끌고 오는 방법)
- C 런타임 수준에서 문자열을 처리하는 클래스 FCString
- Atoi , Atof (아토이, 아토프) ⇒ 근데 이거 포인터 연산하는 거라서 주의하자.
if (LogCharString.Contains(TEXT("unreal"), ESearchCase::IgnoreCase))
{
int32 index = LogCharString.Find(TEXT("unreal"), ESearchCase::IgnoreCase);
FString EndString = LogCharString.Mid(index);
UE_LOG(LogTemp, Log, TEXT("Find Text : &s"), *EndString);
}
//ESearchCase의 경우에는 대소문자를 구별할 것인지 말것인지를 지정할 수 있는, CaseSensitive 와
//IgnoreCase가 있다.
//보이는 것 처럼 Find의 경우에는 만약에 존재한다면 어디에 존재하는 지 인덱스를 찾아주는
//기능을 하고, Mid의 경우에는, index를 기준으로 잘라줄 수 있는 기능을 한다.
FString Left, Right;
if (LogCharString.Split(TEXT(" "), &Left, &Right))
{
UE_LOG(LogTemp, Log, TEXT("Split Test : %s 와 %s"), *Left, *Right);
}
//이런 식으로 왼쪽과 오른쪽을 나눠서 Split을 진행할 수 있음.
//이번에는 한글을 넣어서 해보기로 하는데,
문자열을 사용하는 것이 유니티와 다르게 쉽지 않다.
언리얼 오브젝트
언리얼은 C++을 이용한다. 다만 이것이 우리가 아는 C++이 아니라 객체지향 언어와 C++의 장점을 합쳐서 만든 언리얼 만의 C++이 있다.
따러서 언리얼의 C++를 사용하기 위해서는 UObject를 상속받고 UCLASS 매크로를 사용하여 처리할 수 있다.
*/
UCLASS()
class UNREALSTRING_API UMyClass : public UGameInstance
{
GENERATED_BODY()
public :
virtual void Init() override;
private:
};
위 코드를 보면 UCLASS 매크로가 클래스 바로 위에 올려져있는데 이게 바로 UObject니까 인식해 라는 것이다.
그렇다면 UObject는 어떤 역할을 할까?
UObject 특징들
- 가비지 컬렉션
- 레퍼런스 업데이트
- 리플렉션
- 시리얼라이제이션
- 디폴트 프로퍼티 변경사항 자동 업데이트
- 자동 프로퍼티 초기화
- 자동 에디터 통합
- 실행시간에 유형 정보 사용가능
- 네트워크 리플리케이션
C#에서 쓰이는 것들이 많이 들어가 있는 모습이다.
언리얼 오브젝트의 특징
- 클래스 기본 객체 : 클래스의 기본 값과 타입 정보의 제공
- 리플렉션 : 런타임에서 클래스 정보의 참조 기능
- 인터페이스 : 모던 객체 지향 언어가 제공하는 인터페이스의 제공
- 향상된 열거형 : 보다 향상된 열거형의 지원
- 델리게이트 : 객체간의 결합을 낮출 수 있는 델리게이트 기능의 제공
- 가비지 컬렉션 : 자동 메모리 관리
- 향상된 구조체 : 리블렉션이 가능한 구조체의 지원
- 직렬화 : 객체 정보를 바이트 스트림으로 저장, 전송, 불러들이는 기능.
코드 중간에 보면 GENERATED_BODY 매크로가 있는데 이와 헤더를 통해 언리얼 헤더 툴을 가지고 만들어 낸 클래스를 포함하여 하나의 클래스, 언리얼 오브젝트를 만드는 것이다.
'Unreal Engine 5 > 강의' 카테고리의 다른 글
[UE5] 언리얼 컨테이너 라이브러리 - Struct와 TMap, 언리얼 메모리 관리 (0) | 2024.11.25 |
---|---|
[UE5] 언리얼 컨테이너 라이브러리 - TArray, TSet (0) | 2024.11.22 |
[UE5] 언리얼 C++ 설계 - 인터페이스, 컴포지션, 델리게이트 (0) | 2024.11.21 |
[UE5] 언리얼 오브젝트의 이해 - 리플렉션 시스템 (3) | 2024.11.20 |
[UE5] 언리얼 오브젝트의 이해 - 프로젝트 세팅 및 로그 찍기 (0) | 2024.11.18 |