Practical Unity Text Mesh Pro

2022년 04월 28일
제작기간 2022년 04월 28일 ~ 04월 29일
태그 Unity
GitHub

TextMesh Pro User Docs

TextMesh Pro User Guide

TMPro

모바일 게임을 제작하며 다양한 형식의 UI를 제작할 일이 많았다. UI 작업을 하면서 요근래 많이 접한 TextMesh Pro(TMPro) 컴포넌트에 대해 정리한다.

TMPro는 Text 기능과 Text mesh 기능을 한 번에 사용할 수 있는 유니티 컴포넌트다. 시각적인 개선에 더하여 기능적인 지원까지 이루어지기 때문에 기존에 사용하던 Text 컴포넌트보다 유연하게 사용할 수 있다. 성능에도 큰 부담을 주지 않는다.

이러한 장점들에 힘입어 unity 2021부터는 기존의 Text 기반 UI 컴포넌트에 대한 접근성을 줄이고, TMPro 기반의 UI 컴포넌트에 대한 접근성이 확대되었다.

샘플 프로젝트

이 설명을 적으면서 간단한 샘플 프로젝트를 생성하였다. 상단에 git 링크를 첨부한다.

Font Asset

생성

TMPro의 폰트는 font asset이라는 파일 형식을 사용한다. 폰트 파일을 이용해 폰트 어셋을 생성할 수 있다. Window/TextMeshPro/Font Asset Creator에서 생성할 폰트, 대상 폰트 어셋, 생성할 폰트 범위 및 padding을 설정한 후 폰트를 생성할 수 있다.

Custom Range에서 생성하고 싶은 글자의 범위를 직접 지정할 수 있다. 32-126,44032-55203,12593-12643,8200-9900는 차례로 ‘영어, 한글, 한글 자모음, 특수 문자’의 범위를 의미한다. 없는 글자인 경우 대체 문자(주로 네모)로 출력되니, 자주 많이 사용하는 폰트의 경우 처음부터 범위를 지정해서 문자를 생성해두는 것도 방법이다.

font asset creator

오랜 시간 생성해도 저장하지 않으면 폰트 어셋 생성이 마무리되지 않는다. Atlas Resolution이 너무 작으면 정상적으로 폰트가 생성되지 않으니 적절이 사이즈를 설정해준다.

인스펙터 창

인스펙터 창에서 TextMesh Pro 컴포넌트의 설정을 쉽게 바꿀 수 있다.

폰트에 없는 문자를 지원하고 싶은 경우, Fallback Font Asset을 지정해주면 된다. 그렇게 되면 서치에 실패한 문자가 대체 문자로 대체되기 전에, Fallback으로 지정된 Font Asset에서 다시 서치한다.

Sprite Font

텍스트를 쓰다보면 이런 🥳 식의 이모지를 중간에 첨부하고 싶은 경우가 있다. 이럴 때, Sprite Font를 사용할 수 있다.

Sprite mode가 Multiple인 텍스쳐 이미지를 우클릭한 후, Create/TextMeshPro/Sprite Asset을 통해서 Sprite Font를 만들 수 있다. 이 또한 Fallback 지정이 가능하니, 여러 이미지로 Sprite Font를 만들어서 분류별로 관리할 수 있다.

이렇게 만들어진 Asset에서 Sprite Character Table을 살피면, 텍스쳐 이미지에 포함되어있던 이미지들이 보인다. 최초에 지정된 이미지의 이름으로 name이 지정되는데, 편의를 위해서 적절한 uid를 지정해주는 편이 낫다.

Sprite Font

이미 생성 시에 Cutting된 정보로 세팅되었음으로 이미지 meta 정보(개별 이미지의 위치나 크기 등의 정보)를 바꿔도 Sprite Font 데이터는 변경되지 않는다. 대신, 이미지 각각 세부 조정(위치나 크기)를 원한다면 Sprite Glyph Table에서 수정이 가능하다.

생성이 마무리가 되었으면 지정이 필요하다. TextMesh Pro/Resources/TMP SettingsDefault Sprite Asset을 보면 기본 이모지 어셋이 지정되어있다. 이 참조를 원하는 Sprite Asset으로 교체해야 사용하고자 하는 이모지를 사용할 수 있게 된다.

<sprite name="disk">Practical TMPro

위와 같은 포맷으로 텍스트를 지정하면 아래와 같이 이모지가 텍스트 중간에 출력된다.

이모지 출력되는 모습

Rich Text

Rich Text Manual

TMPro는 Rich Text라는 기능을 옵셔너블하게 제공하며, 이를 사용하기 위해 마크업 문법을 채택한다. 태그를 통해서 다양한 기능을 제공하고 있고, 여러 복잡한 Text기반 기능을 쉽게 구현할 수 있다. 유용하게 사용한 기능은 다음과 같다.

Color와 스타일, 사이즈 등을 태그 형식으로 지정할 수 있기 때문에 하나의 오브젝트로 시각적인 텍스트 연출이 가능하다. 매핑 시스템을 구현한다면, 더욱 쉽게 텍스트의 일부를 강조할 수 있다.

TMP_TextInfo

TMPro 컴포넌트는 TMP_TextInfo를 가지고 있다. 이 구조를 통해서 link 태그 정보, 단어 정보, 라인 정보 등, 다양한 기준으로 텍스트에 접근할 수 있다.

TMP_TextUtilities

TMPro는 인터랙션도 지원한다. TMP_TextUtilities는 마우스 포지션과 카메라를 참조하여 클릭한 곳의 정보를 제공한다.

Link, Word, Character 등 원하는 정보를 가져올 수 있다. 그 중 Link 기능이 정말 반가웠다.

TMP_TextUtilities.FindIntersectingLink(mousePosition, Camera)는 클릭한 단어의 링크 인덱스를 리턴한다. 링크를 가지고 있지 않은 단어라면 -1을 리턴한다.

다음은 클릭된 단어에 링크가 있다면 그 링크 ID에 해당하는 단어 정보를 보여주는 스크립트이다.

public class InteractionDetector : MonoBehaviour
{
    [SerializeField]
    private GameManager _gameManager;
    [SerializeField]
    private TextMeshProUGUI _targetText;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var mousePosition = Input.mousePosition;
            var cam = Camera.main;
            var clickedLinkIndex = TMP_TextUtilities.FindIntersectingLink(_targetText, mousePosition, cam);
            if (clickedLinkIndex < 0)
            {
                SetEmptyLinkInfoBox();
                return;
            }
            var linkInfo = _targetText.textInfo.linkInfo[clickedLinkIndex];
            var linkID = linkInfo.GetLinkID();
            UpdateLinkInfoBox(linkID);
        }
    }

    private void SetEmptyLinkInfoBox()
    {
        // Default Word Object
        UpdateLinkInfoBox(string.Empty);
    }

    private void UpdateLinkInfoBox(string uid)
    {
        var word = StaticData.WordDictionary.GetWord(uid);
        _gameManager.UpdateLinkInfoBox(word);
    }
}

Word는 link id로 식별되는 단어 정보 모음 클래스이다.

단어 정보가 출력되는 모습

Material

인스펙터 창에서도 설정이 가능한 Material Preset은 폰트의 Material을 의미한다. 폰트 어셋마다 material을 만들어 추가할 수 있다.

Material 생성하기

이렇게 생성한 Material은 스크립트 상에서 Material 타입으로 취급하며, 런타임에서도 교체할 수 있다.

public void ChangeMaterial(Material material)
{
    _targetText.fontMaterial = material;
}

TMPro material preset을 수정하기 위해서는 fontMaterial에 원하는 material을 지정해주면 된다. material과 헷갈리면 안된다.

Material 변경하기

예시로 런타임에 여러 material로 변경하는 버튼과 기능을 만들어보았다.

Others

ContentSizeFitter 활용

ContentSizeFitter를 이용하면 text의 길이에 맞게 오브젝트 사이즈를 자동 조정할 수 있다. 그러나, 런타임에서 텍스트가 변경된 뒤, 사이즈가 변경된 것을 확인하기 위해서는 해당 프레임이 완료될 때까지 대기해야 한다.

다음은 변경된 텍스트 길이에 맞춰 컨테이너 박스의 크기를 조정하는 스크립트이다.

public void UpdateSize()
{
    StartCoroutine(WaitEndOfFrameAndUpdateSize());
}

private IEnumerator WaitEndOfFrameAndUpdateSize()
{
    yield return _waitEndOfFrame;
    var x = TEXT_BOX_BASE_SIZE_X + _textBox.sizeDelta.x;
    _textContainerBox.sizeDelta = new Vector2(x, TEXT_BOX_SIZE_Y);
}

애니메이션

DOTween이라고 불리는 애니메이션 third party package의 유료 버전에서는 TMPro를 타겟으로 하는 애니메이션 기능을 제공한다. 대표적으로 DOText는 글자를 한 글자씩 띄워주는 확장 메소드인데, TMPro에서 제공하는 태그의 경우에는 자동으로 가시적인 문자열에서 제외된다. 스타일리쉬한 문자열을 쉽게 애니메이션을 동반하며 유저에게 제공할 수 있기에 애용했다.