[UE5] C++ 개발을 하려니, 호기심이 넘쳐서 고민.

 

본격적으로 뭔가를 개발하려면 C++든 블루프린트든, 아무튼 게임 로직 구현을 위해서 스크립팅을 하는 방법을 알아야 한다.

애초에 블루프린트는 애초에 워낙 간단하기도 하고, 나는 이미 UE4 시절 가끔가다 언리얼4를 깔아서 찍먹도 해본 상태였다.

 

즉, 아무튼 '블루프린트'라는 방식 자체는 나중에 배워도 어렵지 않을 것 같아서, 일단 C++를 도전해보기로 했다.

 

 

먼저, C++로 언리얼 엔진 게임 개발을 어떻게 하는지 영상을 좀 찾아봤다.

 

대충 어떻게 만드는지 처음부터 쭉 진행하는 튜토리얼식 코스를 조금 시청했다.

세계에서 널리 쓰이는 만큼, 엔진 자체가 이미 굉장히 완성도가 높기 때문에 거의 모든 기능을 담고 있다고 해도 과언이 아닌 것 같았고, 편의성 또한 우수해보였다.

다행히 이전에 이미 유니티 엔진을 하다가 온 경험이 있기에 강좌가 설명하는 것을 따라가는 것도 그렇게 크게 어렵진 않았다.

 

1. 원하는 로직을 만들기 위해서 필요한 C++ 클래스를 만든다.

2. 헤더에 변수 및 함수를 정의하고, 필요하다면 블루프린트에 노출될 수 있게 UPROPERTY, UFUNCTION등의 매크로를 붙인다.

3. 언리얼 엔진에서 제공하는 모듈들의 API들을 활용해 기능을 구현한다.

4. 필요하다면 블루프린트로 C++ 클래스를 상속받아서, 추가 기능을 구현한다.

5. 결과를 확인한다.

 

 

하지만 문제는 이거였다. 

 

1. 유니티에서도 처음에 비슷한 문제를 겪었었는데, 저렇게 많은 레퍼런스를 어떻게 다 알고 사용해야 하지?

2. 유니티는 그나마 System 네임스페이스의 라이브러리들을 쉽게 활용할 수 있었는데, 언리얼은 STD/STL을 안쓴다.

 

 

결국 어느 정도는 언리얼 엔진의 C++ 레퍼런스를 좀 봐둬야겠다고 판단이 섰다.

그래서 C++ 레퍼런스를 읽어보고 있던 와중이었다.

 

근데 레퍼런스를 쭉 보고 있자니, 깨달은 사실은 언리얼은 C++ 표준을 거의 안갖다 쓴다고 해도 과언이 아니었다.

즉, reinterpret_cast 대신에 C스타일 괄호 캐스팅을 주로 사용하고, 템플릿도 std::enable_if 같은게 이미 있음에도 TEnableIf 같은 템플릿이 따로 존재했으며, std::map, std::vector 등이 이미 존재함에도 TMap, TList 등이 또 따로 다 있었다.

언리얼 엔진4를 개발할 때 이미 C++11 표준은 커녕 C++98도 이미 std::map, std::vector는 지원하는 판국에 어째서 자체적으로 이 모든걸 다 구현하고 STD는 거의 배제하다시피 해놓은 건지가 궁금했다.

 

대충 찾아보니 아래와 같은 포럼 스레드를 발견할 수 있었다.

 

꼽아본다면 몇 가지가 나오는데,

1. C++98 표준은, '언리얼1'이 출시된 해와 같은 1998년에 발표되었다. 이미 엔진 기반이 만들어진 상태에서 뒤늦게 나온 표준이다.

2. 옛날에는 C++ 표준의 구현이 그다지 성능이나 완성도가 좋지 못했고, 심지어 사실 상 플랫폼 종속적이었다.

3. 현재에도 모바일 기기, 콘솔 등 모든 플랫폼을 독립적으로 동일하게 아우으려면 어쩔 수 없다.

4. STD/STL은 언리얼 입장에서는 서드파티기 때문에, 엔진에 맞게 최적화를 할 수 없다.

 

요런 의견이 나온 것 같다. 뭐 이해가 안되는 내용인건 아닌 것 같다. 그래서 일단은 납득하고 넘어가려고 하는데..

 

 

 

하지만 여전히 reinterpret_cast, dynamic_cast 같은걸 쓰면 안되는 이유는 궁금했다.

그리고 그것도 검색을 해보니 언리얼 C++의 캐스팅 원리에 대해 정리한 글을 통해 어느 정도 알 수 있었다. 

 

 

자세하고 깊게 정말 잘 쓰여져 있어서, 재미있게 읽을 수 있었다. 그리고 몇 가지 사실을 알 수 있었다.

 

1. 타입 안전한 dynamic_cast를 언리얼에서 Cast<T>로 새로 구현했다. 타입 형변환에서 static_cast 멈춰!

2. dynamic_cast에 비해서, 이미 언리얼 내부적으로 구현된 리플렉션을 활용하므로 성능상의 커다란 이점을 갖는다.

3. TCastImpl, TCastFlags 같은 템플릿으로 캐스팅에 필요한 템플릿 구조체를 만들어 활용한다.

4. 전달되는 객체의 유형을 이미 안다면, ExactCast<T>를 사용하면 더 빠르다. 

 

 

아직도 갈 길이 멀다. 레퍼런스나 계속 읽어보러 가야겠다..

 

 


(추가1)

레퍼런스를 보다가, 무려 공식 레퍼런스에서 C/C++의 표준 함수를 안쓰는 이유에 대해서 설명하고 있는걸 찾았다.

바로 여기에서 발견할 수 있었는데, 읽어보면 이런 내용이 있다.

 

Historically, UE has avoided direct use of the C and C++ standard libraries. There have been several reasons for this, including: replacing slow implementations with our own, allowing additional control over memory allocation, adding new functionality before it's widely available, making desirable but non-standard behavioral changes, having consistent syntax across the codebase, or avoiding constructs which are incompatible with UE's idioms.

 

위에 스레드에서 얻은 내용과 거의 비슷해서 추가적으로 다른 부분은 없다. 최신 C++ 표준에 대한 언급이 있어서 이 부분에 대해서 신경을 좀 써서 읽어봤다.

 

해당 부분의 대략적인 내용은, 같은 구현이 있다면 레거시 UE 코드 대신 최신 표준 라이브러리로 전환할 수 있지만, 일관성에 신경을 쓰라는 대목이다. 같은 API에서 UE 레거시 코드와 표준 라이브러리를 혼용하지 말라는 부분이다.

 

<atomic>, <type_traits>, <initializer_list>, <regex> 같은건 필요하면 가져다 쓸 수 있는 것 같다. 특히 언리얼 자체의 atomic 지원이 부실하다고 적혀있는데, C++ 표준의 <atomic>을 활용하 수 있다면 큰 이점이 될 것 같다.

댓글

Designed by JB FACTORY