동적으로 채우는 글자는 본 만큼 램에 쌓인다
게임 안의 글자는 대부분 정해져 있습니다. 버튼 문구, 설명, 퀘스트 이름은 만드는 쪽이 정합니다. 그런데 채팅, 유저 이름, 길드 이름, 외부에서 받아 오는 공지 는 무슨 글자가 나올지 미리 알 수 없습니다.
Unity 는 이런 글자를 실행 중에 굽습니다. 화면에 처음 나온 글자를 그때 만들어서 글자 텍스처에 붙입니다. 영어권 게임에서는 별일이 아닙니다. 알파벳과 숫자, 기호를 다 합쳐도 백 자가 안 됩니다. 한글과 중국어에서는 이야기가 달라집니다.
새 글자가 들어오면 벌어지는 일
한 번 구운 글자는 텍스처 안에 자리를 하나 차지합니다. 자리가 다 차면 같은 크기의 텍스처를 한 장 더 만듭니다. 글자가 늘어난 만큼 텍스처도 늘어납니다.
- 처음 보는 글자가 나온 프레임에는 굽는 시간이 듭니다. 긴 문장이 한꺼번에 들어오면 그 프레임만 눈에 띄게 걸립니다
- 텍스처는 한 픽셀에 한 바이트 를 씁니다. 1024 × 1024 한 장이 1 MB 입니다
- 글자를 화면에서 치워도 그 자리는 그대로 남습니다
한글과 중국어는 자릿수가 다릅니다
한글은 현대 글자를 다 세면 11,172 자이고, 많이 쓰는 완성형만 추려도 2,350 자입니다. 중국어는 표준 상용 한자가 3,500 자, 일본어 상용 한자가 2,136 자입니다. 채팅에 몇 백 명이 몇 시간 떠들면 그 언어의 상용 글자는 거의 다 한 번씩 나옵니다.
그러니까 채팅이 있는 게임에서 동적으로 굽는 방식은 “그 언어의 글자를 거의 다 텍스처에 올려 두는 것” 과 같은 말이 됩니다. 굽는 크기를 키울수록 한 장에 들어가는 글자가 확 줄어드는 것도 위에서 보이실 겁니다.
채팅창은 두 부분입니다
- 지나간 대화 목록 은 글자를 그리는 컴포넌트가 줄마다 하나씩 놓인 것입니다. 새 글자가 계속 들어오는 쪽이 여기입니다
- 내가 치는 입력칸 은
InputField입니다. 안의 글자를Text가 그리면 Text 기반 InputField, TextMeshPro 가 그리면 TMP_InputField 입니다 - 둘이 같은 폰트를 쓰면 글자도 같은 텍스처에 쌓입니다. 목록만 Text 기반으로 바꾸고 입력칸을 TMP 로 두면, 사람이 직접 친 글자는 TMP 쪽에 그대로 남습니다
- 그래서 “채팅은 Text 기반으로” 라고 할 때는 목록과 입력칸을 같이 말하는 것입니다
쌓이는 쪽과 버리는 쪽
여기서 두 컴포넌트가 갈립니다.
- TextMeshPro 로 그리면 동적 아틀라스가 본 글자를 계속 들고 있습니다. 비우라고 시키기 전까지 줄지 않습니다. 대신 한 번 구운 글자는 다시 구울 일이 없어서 그 뒤로는 빠릅니다
Text컴포넌트(TextMeshPro 가 나오기 전부터 있던 것)로 그리면, 그 폰트의 텍스처를 다시 만들 때 최근에 쓴 글자만 남기고 버립니다. 문서에도 “텍스처를 다시 만들면 지난 프레임에 쓰인 글자만 들어 있다” 고 적혀 있습니다. 그래서 오래 켜 둬도 화면에 보이는 양에서 맴돕니다- 대신
Text쪽은 텍스처를 다시 만들 때마다 그 폰트를 쓰는 글자를 전부 다시 그립니다. 채팅이 빠르게 흐르면 이쪽이 자주 일어납니다
채팅만 Text 기반으로 만드는 이유가 이것입니다. 대화 목록은 Text 로, 입력칸은 Text 기반 InputField 로 두면 화면에 보이는 몇 줄만 글자 텍스처에 남고 지나간 대화의 글자는 알아서 버려집니다. 나머지 화면은 TextMeshPro 로 두고 채팅만 다르게 가도 됩니다.
그대로 TextMeshPro 로 가려면
채팅까지 TextMeshPro 로 만들 수도 있습니다. 대신 쌓이는 것을 관리해야 합니다.
- 주기적으로 비웁니다. 채팅창을 닫을 때, 마을을 벗어날 때처럼 끊기는 지점에서 동적으로 채운 글자를 지웁니다. 지운 뒤 처음 나오는 글자는 다시 굽습니다
- 굽는 크기를 작게 잡습니다. 채팅 글자는 작으니 큰 크기로 구울 이유가 없습니다. 위의 그림에서 크기를 낮추면 한 장에 들어가는 글자가 몇 배로 늘어납니다
- 자주 쓰는 글자는 미리 구워 둡니다. 한글 완성형처럼 흔한 범위를 미리 구운 폰트를 기본으로 두고, 거기 없는 글자만 동적으로 받게 합니다
- 빌드에 굽힌 글자가 딸려 가지 않게 합니다. 에디터에서 테스트하며 채워진 글자가 그대로 빌드에 들어가면 쓰지도 않는 텍스처가 같이 나갑니다
장이 여러 개면 그리기도 갈립니다
글자 텍스처가 두 장, 세 장으로 늘어나면 서로 다른 장에 있는 글자는 한 번에 못 그립니다. 한 줄짜리 채팅에 장이 다른 글자가 섞여 있으면 그 줄을 나눠 그립니다. 메모리만 늘어나는 것이 아니라 그리는 횟수도 같이 늘어납니다.
빠져 있는 글자를 다른 폰트에서 찾아 오는 경우에도 같습니다. 그 폰트는 자기 텍스처를 따로 들고 있습니다. 이모지처럼 다른 폰트에서 오는 글자가 섞이면 그 줄은 나눠 그려집니다.
정할 것들
| 항목 | 정해 줄 것 |
|---|---|
| 지원 언어 | 한국어만인지, 중국어와 일본어도 받는지. 언어마다 글자 수가 다릅니다 |
| 채팅 표시 | 한 화면에 몇 줄까지 보이는지, 지난 대화를 얼마나 남기는지 |
| 이름 길이 | 유저 이름과 길드 이름의 최대 글자 수 |
| 글자 크기 | 채팅에 쓰는 크기가 몇 종류인지. 종류가 적을수록 텍스처가 작습니다 |
| 이모지 | 쓸 수 있는지, 쓴다면 어느 범위까지인지 |
| 비우는 지점 | 채팅창을 닫을 때처럼 글자 텍스처를 정리해도 되는 순간 |
정리
- 나올 글자를 미리 모르는 화면은 글자를 실행 중에 굽고, 그만큼 텍스처가 늘어납니다
- 한 픽셀에 한 바이트라 1024 × 1024 한 장이 1 MB 입니다. 자리가 차면 같은 크기로 한 장이 더 생깁니다
- 한글과 중국어는 상용 글자만 수천 자입니다. 채팅이 오래 흐르면 그 언어의 글자가 거의 다 올라옵니다
- 채팅창은 지나간 대화 목록과 입력칸 두 부분이고, 목록은 글자 컴포넌트, 입력칸은
InputField입니다 - TextMeshPro 로 그리면 본 글자를 계속 들고 있고,
Text로 그리면 텍스처를 다시 만들 때 최근에 쓴 글자만 남깁니다 - 그래서 채팅은 목록과 입력칸을 같이 Text 기반으로 두거나, TextMeshPro 로 두되 비우는 지점과 굽는 크기를 정해 둡니다
- 텍스처가 여러 장이면 서로 다른 장의 글자는 한 번에 못 그립니다. 다른 폰트에서 받아 오는 글자도 마찬가지입니다