게임 개발자를 향해

[ UE5 ] 언리얼 C++ 코딩 표준 본문

언리얼 엔진/C++

[ UE5 ] 언리얼 C++ 코딩 표준

뿌단이 2023. 12. 17. 19:57

코딩 표준이란?

 코딩할때 알아야할 작성방법, 규칙을 지정한 가이드라인을 말합니다. (코딩스타일, 코딩컨벤션이라고도함.)
좋은 소프트웨어 회사도 회사만의 표준이 있음 (구글도 있음)

사실 회사만의 코딩 방식이 같아야 업무 효울이 늘어나기 때문에 이를 위한 규칙이지

절대적으로 좋은 표준은 없음.(뭐든 결함이 있다.)

개발을 할 때, 언리얼엔진에서 구축된 시스템에서 게임을 만들어야 하기 때문에

엔진 개발과정에서 여러 규칙이 생길 수 밖에 없습니다.

언리얼 만의 코딩 표준을 반드시 따라야 합니다.

 

 

 

언리얼 공식 문서의 언리얼c++ 코딩 표준 Link!

 

코딩 표준

언리얼 엔진 4 코드베이스에 에픽게임즈가 사용하는 표준과 규칙입니다.

docs.unrealengine.com

 

 1. 명명규칙

명명규칙클래스변수, 함수 등의 이름을 명명할 때 사용되는 규칙으로 여러가지가 사용됩니다.

 

 

<명명규칙 종류>

파스칼케이싱(Pascal Casing) : 합성어의 첫글자를 대분자로 사용하기 ( UnrealEngine )
카멜 케이싱(Camel Casing) : 첫 합성어는 소문자로 나머지는 대문자로 사용 ( unrealEngine )
스네이크 케이싱(Snake Casing): 합성어 사이에 _를 붙임. ( unreal_engine )

 

위 처럼 여러가지 규칙이 있는데, 언리얼의 명명규칙 파스칼 케이싱(Pascal Casing)을 사용합니다.

이외에도 여러 규칙이 있습니다.

 

1. 단어 사이에 언더바(_)를 사용하지 않습니다.

Unreal_Engine  (X)

UnrealEngine  (O)

 

 

<클래스 접두사>

클래스를 생성할때 U A가 붙는것을 확인할 수 있는데 아래 규칙 때문입니다.

 

- UObject에서 상속하는 클래스 접두사는 U입니다.

UObject 를 상속받는 GameInstance도 정확한 클래스 명은 UGameInstance 입니다.

 


- AActor에서 상속하는 클래스 접두사는 A 입니다.

Actor를 상속받으면 클래스명에 A가 붙습니다.

UObject를 상속받는 Actor 클래스 부터는 A를 따라야합니다.


- SWidget에서 상속하는 클래스의 접두사는 S 입니다.


- 추상 인터페이스인 클래스 접두사는 I 입니다.

 

 

 

<이외 접두사>


- enum(열거형)의 접두사는 E 입니다.

enum class EType
{
    type1,
    type2,
    ...
}

 

 


Boolean 변수의 접두사는 b 입니다.

bool bValue;

 

 

 

Template 클래스는 접두사 T를 포함해야 합니다.

template<typename T1, T2>
void Func(T1 a, T2 b)
{
    ...
}

 

 

Typedef의 접두사

 구조체, 언리얼에서 상속받지 않았다면 F  
  언리얼에서 상속받았다면 U

 

일반적인 규칙

1. 유형과 변수명은 명사이다.
2. 메서드 이름은 동사로, 하는 일이나 반환값을 설명해야함.
3. 모든변수는 설명을 코멘트 해야함.


4. const

값을 수정하지 않을 때는 수정하지 않는다는것을 const로 명시해 주는것이 좋습니다.

void SomeMutatingOperation(FThing& OutResult, const TArray<Int32>& InArray)
{
    // InArray는 SomeMutatingOperation에 의해 수정되지 않지만, OutResult는 수정될 수도 있습니다.
}

void FThing::SomeNonMutatingOperation() const
{
    // 이 코드는 자신을 부른 FThing을 수정하지 않습니다.
}

TArray<FString> StringArray;
for (const FString& : StringArray)
{
    // 이 루프의 바디는 StringArray를 수정하지 않습니다.
}

 

포인터로 인자를 넘겨준다는것 자체로서 포인터 증감 연산을 하겠다는 의미가 됩니다.

포인터 증감연산을 하지 않는다면 const를 붙여주거나 래퍼런스를 붙여줍니다.

래퍼런스일 경우는 애초에 주소값을 바꿀 수 없기 때문

 

// 포인터를 상수로 받아서 증감 연산을 하지 않겠다는 의미
T* const Ptr = .. (O)

// 래퍼런스(&)자체로서 이미 변경이 불가능하기 때문에 const를 붙이는것은 의미가 없다.
T& const Ptr = .. (X)
T& Ptr = ..       (O)

 

 

 

5. auto
auto명시적이지 않아서 사용하는것을 권장하지 않습니다.
사용해도 되는 경우는 아래와 같습니다.


1. 람다를 binding해야하는 경우. (람다는 코드로 표현가능하지 않음)
2. 이터레이터 변수의 경우. (iterator 유형이 매우 길어 가독성이 떨어질때만 허용)
3. Template 코드에서 표현식의 유형을 쉽게 식별할 수 없는경우

 

 

 

Portable C++ 코드

포터블 코드동일한 소스코드 여러 플랫폼에서도 동일하게 동작할 수 있는 코드를 의미합니다.

정수형C++에서는 int라고만 사용했지만 언리얼에서는 byte 용량 크기를 지정해야합니다.


int8 : 8bit -> 1byte
uint8 : 8bit -> 1byte
int16 : 16bit -> 2byte (short)
uint16 : 16bit -> 2byte (unsinged short)

int32 : 32bit -> 4byte (int)
uint32 : 32bit -> 4byte (unsinged int)
etc..

float, double byte크기국제 표준으로 정해졌기 때문에 int와 같이 지정하지 않아도 됨.

TCHAR : character 대신 사용됨.

 

 

언리얼 엔진 표준 라이브러리 사용

엔진 개발 당시 STL과 같은 C++표준이 제대로 구축되어있지 않아서 언리얼 엔진에서는 
자체적으로 플랫폼에서 안정적으로 동작하는 표준 라이브러리구축을 했습니다.
memcpy(), memset() 등의 함수들도 언리얼에서 직접 만들어서 제공하고 있습니다.

 

근래에는 C++ 표준 라이브러리안정적이고 완성도가 높아졌습니다.

하지만 C++ 표준 라이브러리는 여러 플랫폼을 상대로 범용적으로 설계가 되어있어서

컴파일 시간이나 복잡도증가하게 됩니다.

 

그래서 언리얼 표준 라이브러리를 사용하는것이 좋습니다.