124 lines
5.4 KiB
C#
124 lines
5.4 KiB
C#
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// 채팅 UI 의 색과 글꼴, 그리고 런타임에 만들어 쓰는 둥근 사각형 스프라이트.
|
|
///
|
|
/// 에셋을 하나도 만들지 않고 코드에서 전부 생성한다. 씬이나 프리팹에 UI 를 넣어두면
|
|
/// 캐릭터 로딩과 마찬가지로 병합 사고가 나기 쉽고, 무엇보다 이 프로젝트에는
|
|
/// TextMeshPro Essentials 가 임포트돼 있지 않다. TMP 는 기본 폰트 에셋이 없으면
|
|
/// 글자가 아예 안 나오므로, 한글이 확실히 나오는 레거시 Text + OS 동적 폰트로 간다.
|
|
/// </summary>
|
|
public static class ChatUiTheme
|
|
{
|
|
// 데스크톱 위에 반투명하게 얹히므로 알파를 남긴다. 완전 불투명이면 위젯처럼 보이지 않는다.
|
|
public static readonly Color PanelBackground = new Color(0.09f, 0.10f, 0.13f, 0.94f);
|
|
public static readonly Color HeaderBackground = new Color(0.13f, 0.15f, 0.19f, 0.98f);
|
|
public static readonly Color UserBubble = new Color(0.20f, 0.38f, 0.68f, 0.95f);
|
|
public static readonly Color AssistantBubble = new Color(0.19f, 0.21f, 0.26f, 0.95f);
|
|
public static readonly Color ErrorBubble = new Color(0.48f, 0.18f, 0.20f, 0.95f);
|
|
public static readonly Color InputBackground = new Color(0.16f, 0.18f, 0.22f, 0.98f);
|
|
public static readonly Color AccentButton = new Color(0.24f, 0.45f, 0.78f, 1f);
|
|
|
|
public static readonly Color PrimaryText = new Color(0.94f, 0.95f, 0.97f, 1f);
|
|
public static readonly Color DimText = new Color(0.62f, 0.66f, 0.72f, 1f);
|
|
|
|
/// <summary>글꼴 후보. 앞에서부터 설치돼 있는 것을 쓴다. 한글이 나오는 것이 최우선.</summary>
|
|
static readonly string[] FontCandidates =
|
|
{
|
|
"Malgun Gothic", // Windows 기본 한글 글꼴
|
|
"맑은 고딕",
|
|
"Noto Sans KR",
|
|
"Segoe UI",
|
|
"Arial",
|
|
};
|
|
|
|
static Font cachedFont;
|
|
static Sprite cachedRounded;
|
|
static Sprite cachedRoundedSmall;
|
|
|
|
/// <summary>
|
|
/// 한글이 나오는 동적 글꼴. OS 에 설치된 글꼴에서 만들기 때문에 프로젝트에
|
|
/// 폰트 에셋을 넣지 않아도 되고, 글리프를 필요할 때 아틀라스에 채워 넣는다.
|
|
/// </summary>
|
|
public static Font Font
|
|
{
|
|
get
|
|
{
|
|
if (cachedFont != null) return cachedFont;
|
|
|
|
cachedFont = UnityEngine.Font.CreateDynamicFontFromOSFont(FontCandidates, 16);
|
|
if (cachedFont == null)
|
|
{
|
|
// 이 경로로 오면 한글은 네모로 보인다. 그래도 앱이 죽지는 않게 한다.
|
|
cachedFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
|
Debug.LogWarning("[ChatUiTheme] OS 글꼴을 만들지 못했습니다. 한글이 깨질 수 있습니다.");
|
|
}
|
|
return cachedFont;
|
|
}
|
|
}
|
|
|
|
/// <summary>말풍선과 패널에 쓰는 둥근 사각형(9-슬라이스).</summary>
|
|
public static Sprite RoundedPanel => cachedRounded != null
|
|
? cachedRounded
|
|
: (cachedRounded = BuildRoundedRect(12));
|
|
|
|
/// <summary>입력칸과 버튼처럼 작은 요소에 쓰는 덜 둥근 사각형.</summary>
|
|
public static Sprite RoundedSmall => cachedRoundedSmall != null
|
|
? cachedRoundedSmall
|
|
: (cachedRoundedSmall = BuildRoundedRect(6));
|
|
|
|
/// <summary>
|
|
/// 모서리가 둥근 흰색 사각형 텍스처를 만들고 9-슬라이스 스프라이트로 감싼다.
|
|
/// 흰색으로 만들어 두면 Image.color 로 아무 색이나 입힐 수 있어 스프라이트 하나면 충분하다.
|
|
///
|
|
/// 크기는 (2r+2)². 테두리를 r 로 잡으면 가운데 2px 만 늘어나므로 어떤 크기로
|
|
/// 늘려도 모서리 곡률이 유지된다.
|
|
/// </summary>
|
|
static Sprite BuildRoundedRect(int radius)
|
|
{
|
|
int size = radius * 2 + 2;
|
|
var tex = new Texture2D(size, size, TextureFormat.RGBA32, false)
|
|
{
|
|
name = "ChatRounded" + radius,
|
|
filterMode = FilterMode.Bilinear,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
hideFlags = HideFlags.HideAndDontSave,
|
|
};
|
|
|
|
var pixels = new Color32[size * size];
|
|
for (int y = 0; y < size; y++)
|
|
{
|
|
for (int x = 0; x < size; x++)
|
|
{
|
|
float px = x + 0.5f;
|
|
float py = y + 0.5f;
|
|
|
|
// 모서리 원의 중심들이 이루는 안쪽 사각형 위의 가장 가까운 점.
|
|
// 그 점까지의 거리가 반지름을 넘어가면 바깥이다.
|
|
float nx = Mathf.Clamp(px, radius, size - radius);
|
|
float ny = Mathf.Clamp(py, radius, size - radius);
|
|
float dist = Mathf.Sqrt((px - nx) * (px - nx) + (py - ny) * (py - ny));
|
|
|
|
// +0.5 는 1픽셀 폭의 계단 완화. 이게 없으면 모서리가 거칠다.
|
|
float alpha = Mathf.Clamp01(radius - dist + 0.5f);
|
|
pixels[y * size + x] = new Color32(255, 255, 255, (byte)(alpha * 255f));
|
|
}
|
|
}
|
|
|
|
tex.SetPixels32(pixels);
|
|
tex.Apply(false, true);
|
|
|
|
var sprite = Sprite.Create(
|
|
tex,
|
|
new Rect(0f, 0f, size, size),
|
|
new Vector2(0.5f, 0.5f),
|
|
100f,
|
|
0,
|
|
SpriteMeshType.FullRect,
|
|
new Vector4(radius, radius, radius, radius));
|
|
sprite.name = "ChatRoundedSprite" + radius;
|
|
sprite.hideFlags = HideFlags.HideAndDontSave;
|
|
return sprite;
|
|
}
|
|
}
|