Unity의 기본 Text 컴포넌트는 단일 색상만을 지원하지만, OnPopulateMesh 메서드를 오버라이드하면 텍스트의 각 정점(Vertex) 데이터를 직접 수정하여 그라데이션이나 그레이스케일 같은 시각적 효과를 부여할 수 있습니다.
CustomGradientText 구현
아래 코드는 Text 클래스를 확장하여 전체 텍스트 또는 개별 문자 단위로 그라데이션을 적용하고, 텍스트 전체를 회색조로 변경하는 기능을 포함합니다.
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class CustomGradientText : Text
{
[SerializeField] private bool useGrayscale = false;
[SerializeField] private bool useGradient = false;
[SerializeField] private bool isGlobalGradient = true;
[SerializeField] private Color colorTop = Color.white;
[SerializeField] private Color colorBottom = Color.black;
public bool IsGrayscale
{
get => useGrayscale;
set
{
if (useGrayscale != value)
{
useGrayscale = value;
SetVerticesDirty();
}
}
}
protected override void OnPopulateMesh(VertexHelper vh)
{
base.OnPopulateMesh(vh);
if (useGrayscale)
{
ApplyGrayscale(vh);
}
else if (useGradient)
{
ApplyGradient(vh);
}
}
private void ApplyGrayscale(VertexHelper vh)
{
int count = vh.currentVertCount;
UIVertex vertex = new UIVertex();
Color32 gray = new Color32(128, 128, 128, 255);
for (int i = 0; i < count; i++)
{
vh.PopulateUIVertex(ref vertex, i);
vertex.color = gray;
vh.SetUIVertex(vertex, i);
}
}
private void ApplyGradient(VertexHelper vh)
{
int count = vh.currentVertCount;
if (count == 0) return;
UIVertex vertex = new UIVertex();
if (!isGlobalGradient)
{
// 각 문자별 개별 그라데이션 (4개의 정점 단위)
for (int i = 0; i < count; i += 4)
{
SetVertexColor(vh, i, colorTop); // Left Top
SetVertexColor(vh, i + 1, colorTop); // Right Top
SetVertexColor(vh, i + 2, colorBottom); // Right Bottom
SetVertexColor(vh, i + 3, colorBottom); // Left Bottom
}
}
else
{
// 텍스트 전체 영역 기준 그라데이션
float minY = float.MaxValue;
float maxY = float.MinValue;
for (int i = 0; i < count; i++)
{
vh.PopulateUIVertex(ref vertex, i);
minY = Mathf.Min(minY, vertex.position.y);
maxY = Mathf.Max(maxY, vertex.position.y);
}
float height = maxY - minY;
for (int i = 0; i < count; i++)
{
vh.PopulateUIVertex(ref vertex, i);
float normalizedY = (vertex.position.y - minY) / height;
vertex.color = Color32.Lerp(colorBottom, colorTop, normalizedY);
vh.SetUIVertex(vertex, i);
}
}
}
private void SetVertexColor(VertexHelper vh, int index, Color color)
{
if (index >= vh.currentVertCount) return;
UIVertex v = new UIVertex();
vh.PopulateUIVertex(ref v, index);
v.color = color;
vh.SetUIVertex(v, index);
}
}
인스펙터 커스터마이징
사용자 정의 컴포넌트의 가독성을 높이기 위해 전용 에디터 클래스를 생성하여 특정 옵션이 활성화될 때만 관련 설정이 보이도록 구성합니다.
using UnityEngine;
using UnityEditor;
using UnityEditor.UI;
using System;
[CustomEditor(typeof(CustomGradientText))]
[CanEditMultipleObjects]
public class GradientTextInspector : Editor
{
private Editor baseEditor;
private SerializedProperty propGrayscale;
private SerializedProperty propGradient;
private SerializedProperty propGlobal;
private SerializedProperty propTop;
private SerializedProperty propBottom;
private void OnEnable()
{
Type textEditorType = Type.GetType("UnityEditor.UI.TextEditor, UnityEditor.UI");
if (textEditorType != null)
{
baseEditor = CreateEditor(targets, textEditorType);
}
propGrayscale = serializedObject.FindProperty("useGrayscale");
propGradient = serializedObject.FindProperty("useGradient");
propGlobal = serializedObject.FindProperty("isGlobalGradient");
propTop = serializedObject.FindProperty("colorTop");
propBottom = serializedObject.FindProperty("colorBottom");
}
private void OnDisable()
{
if (baseEditor != null) DestroyImmediate(baseEditor);
}
public override void OnInspectorGUI()
{
if (baseEditor != null) baseEditor.OnInspectorGUI();
serializedObject.Update();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Custom Effects", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(propGrayscale);
EditorGUILayout.PropertyField(propGradient);
if (propGradient.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(propGlobal);
EditorGUILayout.PropertyField(propTop);
EditorGUILayout.PropertyField(propBottom);
EditorGUI.indentLevel--;
}
if (serializedObject.ApplyModifiedProperties())
{
(target as CustomGradientText).SetVerticesDirty();
}
}
}
VertexHelper의 주요 인터페이스
텍스트 렌더링 최적화와 효과 구현을 위해 VertexHelper에서 제공하는 주요 메서드들은 다음과 같습니다.
- PopulateUIVertex: 특정 인덱스의 정점 데이터를 가져옵니다.
- SetUIVertex: 수정된 정점 데이터를 다시 메시에 적용합니다.
- currentVertCount: 현재 메시에 포함된 전체 정점의 개수를 반환합니다.
- AddUIVertexQuad: 4개의 정점을 이용해 사각형(문자 하나 단위)을 추가합니다.
텍스트의 정점 좌표는 기본적으로 해당 UI 요소의 Pivot을 기준으로 하는 로컬 좌표계를 사용합니다. 따라서 그라데이션 계산 시 vertex.position.y 값을 활용하면 텍스트의 높이에 따른 정확한 보간 처리가 가능합니다.