[Unreal C++]Unreal 코딩 표준

const bool bIsLegalWindow = Blah->BlahP->WindowExists->Etc && Stuff;
const bool bIsPlayerDead = bPlayerExists && bGameStarted && bPlayerStillHasPawn && IsTuesday();
if (bIsLegalWindow && !bIsPlayerDead)
{
  DoSomething();
}

개요

언리얼 5.5 공식 문서에는 C++ 사용시의 코딩 표준에 대한 문서가 있다.

내용도 길고 본격적으로 언리얼에서 C++을 사용하기 전 훑어보면 좋을것 같아 기억해야된다고 생각한 부분들만 뽑아서 정리해봤다.

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/epic-cplusplus-coding-standard-for-unreal-engine

 

 

클래스 체계

클래스 구현에서 읽는 사람에 더 초점을 두어 public선언을 먼저 한 뒤 private 선언을 한다고 한다.

UCLASS()
class EXAMPLEPROJECT_API AExampleActor : public AActor
{
	GENERATED_BODY()
    
public:
	AExampleActor();
    
protected:
	virtual void BeginPlay() override;
}

명명 규칙

명명 규칙으로는 PascalCase 포맷을 사용한다. 규칙은 다음과 같다.

  • 첫번째 글자는 무조건 대문자
  • 단어 사이에 _ 사용금지

추가적으로 타입이름은 대문자로 이루어진 접두사를 포함해 변수 이름과 구분한다.

  • 템플릿 클래스 : class TTemplateClass
  • UObject를 상속하는 클래스 : class UUobjectClass
  • AActor를 상속하는 클래스 : class AActorClass
  • SWidget을 상속하는 클래스 : class SSWidgetClass
  • 추성적 인터페이스 : class IInterfaceClass
  • 열거형 : enum class EEnum { ECB_Red, ECB_Green };
  • bool 변수 : bBooleanVar
  • 그 외 대부분 : FOtherClass

 typedef의 경우에도 적절한 명명 규칙을 따라야한다.

  • 구조체의 typedef의 경우 F
  • UObject의 typedef의 경우 U
  • 특정 템플릿 인스턴스화의 typedef는 더이상 typedef가 아니므로 알맞은 접두사 사용해야함
    • typedef TArray<FMyType> FArrayOfMyType;

그 외 중요한 것들

  • 매크로는 모두 대문자에 단어가 언더스코어( _ )로 구분되야 한다.
  • 함수 파라미터가 레퍼런스이거나 함수에서 값에 Write할 것으로 예상되면 파라미터 이름에 'Out'을 붙여야한다.

포터블 C++ 코드

int, unsigned int는 크기가 플랫폼에 따라 다를 수 있기 때문에 명시적으로 크기를 지정한 포멧을 사용할 것을 권장한다.

최소길이로 32비트는 보장한다고 한다.

  • bool - 불 값, 크기 추정 금지
  • TCHAR - character, 크기 추정 금지
  • uint8 - usigned byte, 1byte
  • int8 - byte, 1byte
  • uint16 - unsigned shorts, 2byte
  • int16 - shorts, 2byte
  • uint32 - unsigned int, 4byte
  • int32 - int, 4byte
  • uint64 - unsigned long, 8byte
  • int64 - long, 8byte
  • float - 단정밀도 부동 소수점, 4byte
  • double - 배정밀도 부동 소수점, 8byte
  • PTRINT - 포인터를 가질 수 있는 정수, 크기 추정 금지

CONST 정확도

call by value함수에서의 파라미터나 로컬에서 const의 사용이 권장된다고 한다. by value 함수는 어짜피 값을 변경하지 않기 때문에 가독성이 좋아진다...!

void AddSomeThings(const int32 Count);
    
void AddSomeThings(const int32 Count)
{
    const int32 CountPlusOne = Count + 1;
    // Count와 CountPlusOne 모두 함수 바디에서 변경 불가합니다.
}

한가지 예외로 pass by value 파라미터가 있다고 하는데

예시를 보면 InNewArray는 MoveTemp()로 객체의 소유권을 이전해야하는데 이를 위해 원본 객체를 변경해야한다.

따라서 const로 선언하면 안된다!

이동시맨틱에 대한 코맨트

더보기

이동 시맨틱

TArray , TMap , TSet , FString 과 같은 모든 주요 컨테이너 타입에는 move 컨스트럭터와 move 할당 연산자가 있습니다. 이러한 타입을 값으로 전달 또는 반환할 때 종종 자동으로 사용되지만, std::move 의 UE 해당 버전인 MoveTemp 를 통해 명시적으로 호출할 수도 있습니다.

값으로 컨테이너나 스트링을 반환하는 것은 보통 임시로 복사하는 비용이 없어 표현성에 유용하게 작용할 수 있습니다. 값 전달 관련 규칙 및 MoveTemp 사용법은 아직도 확립 중이지만, 최적화된 코드베이스 영역 일부에서는 이미 찾아볼 수 있습니다.

void FBlah::SetMemberArray(TArray<FString> InNewArray)
{
    MemberArray = MoveTemp(InNewArray);
}

예시 포맷

에픽에서는 JavaDoc 기반 시스템을 사용해서 코드에서 코맨트(주석)를 추출한 뒤 자동으로 문서를 만든다고 한다.

따라서 특수한 코맨트 포맷 규칙대로 코맨트를 작성해야한다고 한다.

클래스, 메서드, 변수 코맨트 포멧이 각각 있는데 나중에 예시와 함께 따로 정리할 예정,,,

< 정리글 링크 하기>

Override & Final

override, final 키위드 사용 적극 권장!!

Nullptr

NULL 매크로 대신 모든 경우에 nullptr 사용하기!

Auto

auto 사용을 권장하지 않지만(가독성의 이유로)

사용가능한 경우

  • 변수에 람다를 바인딩하는 경우
  • iterator 변수의 경우
  • 템플릿 코드에서 표현식 타입을 쉽게 식별할 수 없는 경우

사용할 때는 const, &, *을 통해 가독성을 높이자!!

 

 코드 포맷

중괄호

새 줄에 중괄호 사용하기!!

if (bThing)
{
    return;
}

if - else

항상 중괄호 사용하기 + 새 줄에 중괄호 하기

if (bHaveUnrealLicense)
{
    InsertYourGameHere();
}
else
{
    CallMarkRein();
}

추천 스타일

1. 다음과 같은 스타일은

if ((Blah->BlahP->WindowExists->Etc && Stuff) &&
      !(bPlayerExists && bGameStarted && bPlayerStillHasPawn &&
      IsTuesday())))
  {
      DoSomething();
  }

다음과 같이 바꾸는게 좋다.

 

2. 함수 호출에서 익명 리터럴 사용을 피하자.

// 기존 스타일
  Trigger(TEXT("Soldier"), 5, true);.

  // 새 스타일
  const FName ObjectName                = TEXT("Soldier");
  const float CooldownInSeconds         = 5;
  const bool bVulnerableDuringCooldown  = true;
  Trigger(ObjectName, CooldownInSeconds, bVulnerableDuringCooldown);

3. 헤더에 특수한 스태틱 변수를 정의하지 않도록 하자

// SomeModule.h
static const FString GUsefulNamedString = TEXT("String");

//이러한 코드는 다음으로 대체해야 함

// SomeModule.h
extern SOMEMODULE_API const FString GUsefulNamedString;

// SomeModule.cpp
const FString GUsefulNamedString = TEXT("String");

 

'Unreal' 카테고리의 다른 글

[Unreal] Collision 채널  (1) 2025.01.08
[Unreal C++] 객체 Class 타입 비교하기  (1) 2025.01.07
[Unreal] 모델링 모드로 Mesh 수정하기  (0) 2025.01.03
[Unreal] Animation 몽타주  (0) 2025.01.01
[Unreal C++] AnimInstance  (2) 2024.12.31