Deeper Learning

[UE5] 언리얼 엔진 C++ 코딩 표준 (Coding Standard) 본문

Game Development/Unreal Engine

[UE5] 언리얼 엔진 C++ 코딩 표준 (Coding Standard)

Dlaiml 2024. 6. 24. 20:25

언리얼 엔진 C++ 코드를 학습하면서 공개된 소스코드와 엔진 코드를 보면서 언리얼 엔진만의 코딩 컨벤션이 꽤 강하게 자리 잡고 있다는 느낌이 들었다.  인공지능 개발 직군으로 서비스를 개발할 때는 한 달이 다르게 급변하는 신생 라이브러리를 주로 사용하였고 모두의 코딩 스타일은 파이썬의 원칙하에 제각각이었다. (구글, 메타, OpenAI의 논문 구현 코드 스타일이 연구자에 따라 매우 달랐음)

 

반면 역사가 오래된 언리얼 엔진은 비교적 코딩 표준, 디자인의 Best Practice가 있다는 느낌을 받았다. 간단한 코딩 표준 가이드는 공식 문서에 소개되어 있었고 프로젝트마다 코딩 스타일이 바뀌는 것은 좋지 않다고 생각해서 이참에 공식 문서를 따라 기본적인 코딩 스타일, 표준을 맞추기로 하였다.

 

현재 개발하고 있는 프로젝트, 앞으로의 더 규모가 큰 협업에서도 도움이 될 것이라고 생각한다.

 

저작권 고지

// Copyright Epic Games, Inc. All Rights Reserved.

#include "PuzzlePlatformer.h"
#include "Modules/ModuleManager.h"

IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, PuzzlePlatformer, "PuzzlePlatformer" );

 

에픽 게임즈에서 제공한 모든 소스코드 파일 첫 줄에 저작권 고지를 해야 한다.

 

언리얼 엔진이 생성한 소스코드에는 자동으로 포함되어 있으며 에디터에서 생성한 클래스의 소스코드에는 프로젝트 세팅에서 저작권 고지를 작성하라는 아래 주석이 삽입된다.

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

 

네이밍 컨벤션

Pascal case를 사용해야 하며 Type 앞에는 대문자를 붙여 일반 변수명과 구분되도록 한다.

class TAttribute // T - Template Class
class UActorComponent // U - Inherit from UObject 
class AActor // A - Inherit from AActor
class SCompoundWidget // S - Inherit from Slate UI Widget
class IAnalyticsProvider // I - Abstract interfaces
enum class EColorBits // E - Enum
bPendingDestruction // b - Bool

 

표준 라이브러리 사용

이전에는 사용을 지양하였으나 c++ 표준 라이브러리의 완성도가 높아져 더 좋은 옵션이 있다면 사용해도 무방.

하지만 같은 API 내에서 언리얼 엔진 언어와 c++ 표준 라이브러리 언어를 섞어서 사용하는 것은 지양, 일관성을 유지하는 것이 중요

 

Const

아래와 같은 올바른 사용을 권장

void SomeMutatingOperation(FThing& OutResult, const TArray<Int32>& InArray)
{
    // InArray will not be modified here, but OutResult probably will be
}



void FThing::SomeNonMutatingOperation() const
{
    // This code will not modify the FThing it is invoked on
}



TArray<FString> StringArray;

for (const FString& : StringArray)
{
    // The body of this loop will not modify StringArray
}

 

반환 타입에 const를 사용하면 이동 시맨틱이 제한되고 복사가 일어나 성능 저하의 우려가 있으니 유의해서 사용

// Bad - returning a const array
const TArray<FString> GetSomeArray();

// Fine - returning a reference to a const array
const TArray<FString>& GetSomeArray();

// Fine - returning a pointer to a const array
const TArray<FString>* GetSomeArray();

// Bad - returning a const pointer to a const array
const TArray<FString>* const GetSomeArray();

 

Modern C++ Syntax

  • override와 final의 사용을 강력히 권함
  • NULL대신 nullptr 사용
  • 아래 경우를 제외하고 auto 사용 금지
    • 변수에 lambda 바인딩
    • Iterator 변수
    • 템플릿 코드에서 타입 식별이 어려운 경우
  • Range-Based For 사용을 추천
    • 버전이 바뀌면서 언리얼 엔진 코드도 Iterator에서 Range-Based로 변환한 것들이 있음 (TFieldIterator -> TFieldRange)
    • TMap<FString, int32> MyMap;
      
      // Old style
      for (auto It = MyMap.CreateIterator(); It; ++It)
      {
          UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), It.Key(), *It.Value());
      }
      
      // New style
      for (TPair<FString, int32>& Kvp : MyMap)
      {
          UE_LOG(LogCategory, Log, TEXT("Key: %s, Value: %d"), *Kvp.Key, Kvp.Value);
      }
  • lambda 사용가능, 두 구문정도가 최적
  • Enum class로 기존 namespace Enum 대체 
    • // Old enum
      UENUM()
      namespace EThing
      {
          enum Type
          {
              Thing1,
              Thing2
          };
      }
      
      // New enum
      UENUM()
      enum class EThing : uint8
      {
          Thing1,
          Thing2
      }
  • Default Member Initialzers - 사용 가능

 

Code Formatting

중괄호 이전 개행 (언리얼 엔진에서의 오래된 관행)

if (bAlive)
{
	return;
}

 

 

if else 문에서 중괄호 사용

if (bAlive)
{
	return;
}
else
{
	Cnt++;
}

 

개행 후 공백에 Tab 사용 Tab size는 4

Switch-case 문에서 default는 항상 만들고 break 명시 필수, break가 없는 case에는 //falls through 주석 필수

 

 

언리얼 엔진 스타일, API 가이드라인

  • TEXT() 매크로를 스트링에 사용할 것
  • Variable shadowing 금지 (scope 밖의 변수명을 선언하여 활용하는 방식)
  • Boolean parameter 지양
// Old style

FCup* MakeCupOfTea(FTea* Tea, bool bAddSugar = false, bool bAddMilk = false, bool bAddHoney = false, bool bAddLemon = false);
FCup* Cup = MakeCupOfTea(Tea, false, true, true);


// New style
enum class ETeaFlags
{
    None,
    Milk  = 0x01,
    Sugar = 0x02,
    Honey = 0x04,
    Lemon = 0x08
};

ENUM_CLASS_FLAGS(ETeaFlags)
FCup* MakeCupOfTea(FTea* Tea, ETeaFlags Flags = ETeaFlags::None);
FCup* Cup = MakeCupOfTea(Tea, ETeaFlags::Milk | ETeaFlags::Honey);

 

정리

나열한 것 이외에도 많은 항목들이 있었지만 직접 코딩하면서 고민했거나 또는 가이드를 따르지 않았던 항목들을 정리해 보았다. 대부분 기본적인 프로그래밍 원칙이지만 언리얼 엔진에서는 중괄호 열기 전 개행을 권장하는 등 재밌는 컨벤션도 많았다. 

아직 C++, UE는 개발자 시작부터 다뤄왔던 Python, Pytorch처럼 오래 사용하지 않은 언어와 프레임워크라 Github에서 Star를 많이 받은 좋은 프로젝트 위주로 참고하면서 디자인 패턴, 코딩 스타일에 대해 틈틈이 참고하려 한다.

 

 

Reference

[0] https://dev.epicgames.com/documentation/ja-jp/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine

 

Comments