Unreal 게임 개발/Unreal 강의 개인 정리

Unreal 기본타입 및 문자열

daisy0461 2023. 12. 26. 18:21

Unreal 기본 타입을 따로 지정하는 이유

  • 게임 제작 시 데이터 정보가 명확해야한다.
  • 단일 컴퓨터에서 최대 퍼포먼스를 뽑아내야한다.
  • 네트워크상에서 데이터 통신이 효율적이고 안정적이어야한다.

PC, PS, XBox등 다양한 기기에서 C++함수에 대해 다르게 해석하는 경우가 있었다. 이를 '플랫폼 파편화'라고 한다.

이러한 문제를 해결하기 위해서 기본 타입을 지정한다.

간단한 예시로 C++의 경우 최소 32비트를 보장하도록 규정되어 있지만 특정 플랫폼에서는 64비트로 해석될 수 있다.

그렇기에 데이터를 지정할 때 int의 크기를 확신할 수 없는 문제가 있었지만 Unreal은 int32, int64처럼 명확하게 지정한다.

C#의 경우 int는 항상 32비트로 해석하도록 되어있다.

이렇게 명확하게 함으로써 단일 컴퓨터에서 애매하게 해석하지 않아 최대 퍼포먼스를 뽑아내고 네트워크 상에서 애매한 데이터 타입을 사용하지 않아 효율적이고 안정적인 네트워크 통신을 할 수 있게 된다. 

bool 타입

  • 데이터 전송을 고려한 참/거짓 데이터의 지정
  • bool은 표준이 존재하지 않기 때문에 크기가 명확하지 않다.

bool타입의 위 특징을 보면 다시 정확한 크기가 지정되어 있지 않아서 많은 문제가 발생할 것으로 생각된다.

Unreal을 사용해서 코딩을 할 때 헤더 파일 내에서는 가급적 bool타입을 사용하지 않고 uint8을 사용하되 Bit Field 오퍼레이터를 사용한다.

Bit Field오퍼레이터를 사용해서 선언을 하는 방법은 uint bNetTemp:1; 처럼 선언하면 1비트로 만들어 데이터를 최소화할 수 있다. 추가적으로 이렇게 uint를 사용할 때 일단 uint와 구분하기 위해서 앞에 접두사b를 적는것이 표준이다.

이러한 정확한 데이터는 데이터를 저장하거나 전송하는것에 있어서 필요한 사항이기 때문에

헤더 파일에서 bool형을 사용하지 않고 cpp로직에서는 bool형을 사용해도 무방하다.

 

Character 인코딩

이전 Windows는 영미권에서 자주 사용했어서 동아시아권의 한글과 같은 언어는 제공을 하지 않았다.

이제 아시아권도 언어를 제공하면서 영어를 제공할 때 사용하는 1바이트로는 부족해져서 새로운 Character 인코딩 시스테템이 적용되었다.

크게 UTF-8과 UTF-16이 있다.

  • UTF-8
    메모리를 작게 사용한다. / 영어를 사용할 때는 1바이트, 동아시아권 Character에는 2바이트를 사용한다.
    ASCII 상위 집합으로 ASCII를 완벽히 호환한다.
  • UTF-16
    영어, 동아시아 상관없이 2바이트로 저장을 한다. -> 메모리를 UTF-8보다 더 사용한다.
    균일한 크기이기 때문에 데이터를 저장할 때 깔끔하게 작업이 가능하다.

그래서 Unreal은 무엇을 사용할까? 위를 읽고 왔으면 예상이 가겠지만 UTF-16을 사용한다.

하지만 C++소스 코드는 UTF-8 or Windows 인코딩이다. 그렇기에 가급적 동아시아 언어를 사용하지 않는 것이 좋다.

 

TCHAR와 FSting

TCHAR사용법은 간단하다.

TCHAR LogChar[] = TEXT("hello world");
UE_LOG(LogTemp, Log, LogChar);

위와 같이 코드를 작성해주면 hello world가 출력되는 것을 확인할 수 있다.

이 문자열을 다양하게 변환하고 편집하고 싶다면 FString을 사용해야한다.

FString LogString = LogChar;
UE_LOG(LogTemp,Log, TEXT("%s"), *LogString);

FString을 처음 초기화할 때 TEXT("~~~")로 해줘도 무관하지만 위와 같이 TCHAR를 넣을 수도 있다.

UE_LOG의 세번째 파라미터는 배열만 들어가기 때문에 FString을 %s로 받아온다.

UE_LOG 마지막을보면 *로 반환하는 것을 확인할 수 있다. %s에 대응될 때는 TCHAR의 포인터 Array를 반환해야하는데

FString을 그냥 사용하면 TCHAR 포인터가 반환되지 않기 때문에 *연산자를 사용해서 반환해준다.

*연산자를 입력하지 않고 빌드를 하면 에러가 발생한다.

 

FString

FString을 선언하면 빈 껍데기가 만들어지고 초기화를 해주면 안에 해당 문자열이 들어간다.

FString은 TCHAR의 동적배열이라고 생각하면 편하다.

FString은 TCHAR가 할 수 없는 문자열을 자르거나 추가하거나 하는 다양한 연산이 가능하다.

이 다양한 연산을 해주는 것은 FString 내부에 FCString이라는 클래스가 있다.

FCString 클래스는 C라이브러리에서 제공하는 기본적인 String 관련 함수를 포함하고 있는 Wrapper Class라고 생각하면 된다. 즉, 연산은 FCString을 통해 이루어진다.

 

FString 구조

TEXT("HELLO")라고 선언을 하면 이것은 TCHAR 배열로 선언이 되는 것이다.

이 TCHAR 배열을 FString으로 넣는 순간 TArray라는 Unreal이 제공하는 동적배열 클래스가 있다.

이 TArray방식으로 HELLO라는 문자열이 저장이 된다.

이 FString에 들어있는 실제 데이터를 꺼냈을 때는 동적배열이 속하고 있는 포인터를 가져와서 넘겨준다.

이게 위에서 설명했던 *연산자를 사용하는 것이고 이러한 작업을 디레퍼런싱이라고 한다.

정확하게 *LogString을 하면 LogString이 포함하고 있는 동적배열 TArray에서 첫번째 인자(h)의 포인터를 반환한다.

 

정리를 하자면 Unreal에서는 FString의 형태로 문자열을 관리하고 TCHAR의 형태가 필요할 때는 포인터 연산자를 통해 얻으면 된다.

FName

에셋 관리를 위해 사용되는 문자열 체계

 

FName의 특징

  1. 대소문자 구분이 없다. 
    FName A(TEXT("TEST");  FName B(TEXT("test");
    A == B; //True이며 4번에 설명이 되어있지만 Key값이 같기 때문에 동일한 것으로 간주한다.
  2. 한번 선언되면 바꿀 수 없다.
    바꾸기 위해선 FString으로 변환하여 사용해야한다.
  3. 에셋의 이름을 빠르게 찾고 싶을 때 간단하고 빠르게 사용가능하다.
    해시값을 사용하기에 빠른 속도로 사용가능하다.
  4. FName은 문자표현용이 아닌 에셋 키를 지정하는 용도로 사용되며 빌드 시 해시값으로 바뀐다.
    해시값을 사용하기 때문에 문자표현용이 아니다. 그렇기에 FString처럼 자르거나 추가할 수 없다.
  5. 생성자를 반복할 시 Overhead가 발생할 수 있다.
    FName A = FName(TEXT("test"));와 같이 생성자에 문자열을 넣으면 test라는 문자열을 Key로 변환한 후 FNamePool에서 존재하는지 찾고 없으면 새 Key를 부여하기 때문에 Overhead가 발생할 수 있다.

FName의 구조

FName을 보관하는 FNamePool이 있다. FNamePool은 Key,Vaule쌍의 형태이며 Name만 모아놓은 자료이다.

FName A(TEXT("Test Text"); 라고 입력을 했을 때 문자열이 저장되는 것이 아니라 Key값이 FNamePool에 저장이 되고 문자열은 다른곳에 저장이 된다. 이 Key값을 사용해서 FNamePool에 있는지 빠르게 찾고 없다면 추가한다.

FName은 해시 값을 사용하기에 찾거나 더하는 작업만 할 수 있다.(Find or Add)

 

FText

다국어 지원을 위한 문자열 관리 체계 - 보통 UI에서 다국어 지원을 할 때 사용함.

  1. 일종의 키로 작용
  2. 별도의 문자열 테이블 정보가 추가로 요구
  3. 게임 빌드 시 자동으로 다양한 국가별 언어로 변환된다.