본문 바로가기

게임 프로그래밍

언리얼5 코딩표준

코딩 표준이란

게임개발 기업마다 프로젝트에 맞는 코딩 표준이 존재합니다

코딩표준이 사용되는 이유는 개발자들이 협업시 서로 다른 코딩 스타일로 발생되는 이슈또는

저하되는 작업 가독성을 방지하기위해 효율적인 코드의 사용을 권장하게끔 기업에서 지정하는것이 코딩표준을

지키는 이유입니다.

 

코딩표준의 옳바른 사용법

코딩 표준의 옳바른 사용법은 자신이 작성된 코드와 다른사람이 작성한 코드를 비교하였을 시

단 한사람이 작업한것처럼 보인다면 옳바르게 코딩 표준을 지키고 있는것입니다.

다른 예시로 자신이 사용하는 코드 스타일이 더욱 퍼포먼스가 좋을수있다 해서

기업에서 사용하는 코딩 표준을 지키지않는다면 이는 좋은 코딩습관이 될 수 없습니다.


언리얼 코딩표준

코딩표준이 기업에서 사용되는것과 같이 언리얼 코딩 표준이 존재합니다.

언리얼의 코딩표준을 지키는것이 언리얼을 사용하여 컨텐츠를 개발할경우 발생되는 예상치 못한 다양한 버그에서

보다 안전하게 개발이 가능할 수 있기에 반드시 지키는것이 좋습니다.

이번글은 우리가 언리얼 C++를 이용하여 게임개발을 하는경우 반드시 알아야할 코딩 표준에대해 정리해보겠습니다.

 

https://docs.unrealengine.com/4.27/ko/ProductionPipelines/DevelopmentSetup/CodingStandard/

 

코딩 표준

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

docs.unrealengine.com


작명 규칙

표기법

 

작명 규칙을 알아보기 위해 처음 확인해야할것은 표기법입니다.

다양한 코딩 표기법이 존재하지만 대표적인 camelCase ,PascalCase, snake_case 이렇게 3가지를 실무에서 보실 수 있게됩니다.

저도 실무에서 서버개발자분은 스네이크 표기법을 사용하시는분과 같이 협업한 경험이 있는가 하면

저는 카멜과 파스칼 표기법을 변수와 함수로 분리해서 사용합니다. 이런식으로 서로 다른 표기법을 사용하지만

언리얼에서는 이러한 표기법이 코딩표준으로 파스칼 표기법을 사용한다 명시되어있습니다.

 

자료형 변수 선언 규칙

  • bool - boolean 값 BOOL은 컴파일되지 않습니다.
  • TCHAR - character(TCHAE 크기 추정 금지)
  • uint8 - unsigend byte(1 바이트)
  • int8 - sigend byte(1 바이트)
  • uint16 - unsigend byte(2 바이트)
  • int16 - sigend byte(2 바이트)
  • uint32 - unsigend byte(4 바이트) 
  • int32 - sigend byte(4 바이트)
  • uint64 - unsigend byte(8 바이트)
  • int64 - sigend byte(8 바이트)
  • float - single precision floating point(4 바이트)
  • double - double precision floating point(8 바이트)
  • PTRINT - 포인터를 가질 수 있는 Integer(PTRINT 크기 추정 금지)

우리가 컨텐츠 개발에 자주 사용되는 정수(4byte)타입 변수를 선언할 경우 int가 아닌 int32를 사용하여 자료형의 타입을 확실히 지정하는것을 언리얼에서 권장합니다.

 

변수 클래스 작명 규칙

  • 템플릿 클래스의 접두사는 T입니다.
  • Uobject에서 상속하는 클래스 접두사는 U입니다.
  • AActor에서 상속하는 클래스 접두사는 A입니다.
  • Swidget에서 상속하는 클래스 접두사는 S입니다.
  • 추상 인터페이스의 클래스 접두사는 I입니다.
  • Enum(열거형)의 클래스 접두사는 I입니다.
  • Boolean 변수의 접두사는 b입니다.
  • 언리얼에 상속받지않는 대상의 클래스의 접두사는 f입니다.
  • UnrealHeaderTool은 대부분의 올바른 접두사가 필수이기에 제대로 붙여주는것이 중요합니다.

 

UnrealHeaderTool(UHT)란?

 

*UnrealHeaderTool(UHT)는 Uobject 시스템을 지원하는 커스텀 구문 분석 및 코드 생성 툴입니다.

UHT의 코드 컴파일은 두 단계로 진행됩니다.

  1. Unreal 관련 클래스 메타데이터에 대한 C++ 헤더를 구문 분석하고 다양한 UObject 관련 기능을 구현하기 위한
    커스텀 코드를 생성하기위해 UHT가 호출됩니다.
  2. 결과를 컴파일하기 위해 일반 C++ 컴파일러가 호출됩니다.

간략설명

에디터 실행 시 또는 재컴파일이 일어나는 순간 우리가 작성한 코드를 언리얼 뼈대와 같은  언리얼 리플렉션의 기능을 활용하여

커스텀 코드를 생성합니다 이때 커스텀 코드를 사용기위해 필요한 도구가 UHT입니다.


Const 사용 권장

Const는 문서이자 컴파일러 지시자이기도 하므로 모든 코드는 const 정확도를 맞추도록 해야합니다.

 

const 사용에 포함되는 경우

  • 함수매개변수로 전달된 인수가 함수에 의해 수정되지 않아 함수 인수를 Const를 사용합니다.
  • 메서드가 오브젝트를 수정하지 않게 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를 수정하지 않습니다.
}

 


사용금지 또는 제한되는 C++ 매크로

nullptr

모든 경우 C스타일 NULL 매크로 대신 nullptr을 사용해야 합니다.

auto 

몇 가지 예외를 제외하고 C++ 에서 auto를 사용해서는 안됩니다. 항상 초기화시키려는 유형은 명시해 줘야 합니다.

그 유형이 협업자에게 명확히 보여야 한다는 뜻입니다. 이 규칙은 C#의 var 키워드 사용에도 적용됩니다.

 

단, 복잡한 이터레이터 구문이 사용되는경우는 가독성을 위해 사용해도 괜찮습니다.


언리얼 C++ 클래스 상속시 주의점

한달간 언리얼5를 사용해보면서 제가 자주 실수했던 개발 이슈입니다.

상속받은 부모클래스에 가상함수가 존재하는경우가 자주 있습니다 언리얼에서 지원하는 다양한 구조의 클래스에서

무슨 함수가 가상인지 알 수 없기에 부모 클래스에서 가상함수가 있는지 확인하고 발견했다면 오버라이딩하여 재정의해주는것을

기억하시는 편이 좋습니다 저는 맥 개발환경에서 언리얼을 사용해서 IntelliScene가 동작을 안하거나 빌드 실패가 되어도 원인 코드를 제대로 로그에 출력되지 않아 사소한 문제에서도 해결이 느린경우가 자주 발생했습니다 여러분도 저와 비슷한 경험이 없으면 하는 바람으로

코드를 적습니다.

 

 언리얼 클래스 상속시 확인

  1. 상속 부모 클래스를 탐색하여 가상함수를 체크한다.
  2. 가상함수를 발견했다면 자식클래스로 돌아와 반드시 가상함수를 오버라이딩한다.
  3. 정의된 함수에 Super(부모클래스)의 Init을 호출한다.

 

 

예시 MyGameInstance

//####### Class MyGameInstance.cpp #########

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"

/**
 * 
 */
UCLASS() //UObject이기에 접두사 U 사용
class TEST_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()
public:
	virtual void Init() override; //UGameInstance의 가상함수 오버라이딩	
};

//####### Class MyGameInstance.h #########

#include "MyGameInstance.h"

void UMyGameInstance::Init()
{
	Super::Init();
	UE_LOG(LogTemp, Log, TEXT("%s"), TEXT("Hello World"));
}