ThreeKingdomsSlingshot/Assets/Scripts/OutGame/UI/UiKit.cs

538 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace TKS.OutGame
{
/// <summary>
/// 아웃게임 UI 도우미 — uGUI + Malgun Gothic(TextUtil) · 기준 1080×1920 · 배치 = 확정 목업(img03·05·06·07·08·11·13·14·16·17).
/// 부품·색 = UI 테마(PD #1100 · Layer Lab GUI Pro-SuperCasual 팩 · UISkin) · 테마가 없으면 기존 도형 와이어프레임.
/// 위치 인자는 전부 「부모 안 기준점(anchor) · 피벗 · 기준점에서의 거리 · 크기」(UI px).
/// </summary>
public static class UiKit
{
// 팔레트 = 테마 색 id(ui_theme.csv) · 괄호 값 = 테마가 없을 때(구 와이어프레임)
public static Color Bg { get { return UISkin.Col("bg", new Color(0.95f, 0.92f, 0.85f)); } }
public static Color Paper { get { return UISkin.Col("paper", new Color(0.99f, 0.97f, 0.92f)); } }
public static Color Header { get { return UISkin.Col("header", new Color(0.92f, 0.87f, 0.78f)); } }
public static Color HeaderText { get { return UISkin.Col("header_text", Ink); } }
public static Color HeaderSub { get { return UISkin.Col("header_sub", InkSoft); } }
public static Color Ink { get { return UISkin.Col("ink", new Color(0.17f, 0.12f, 0.10f)); } }
public static Color InkSoft { get { return UISkin.Col("ink_soft", new Color(0.45f, 0.39f, 0.34f)); } }
public static Color Dark { get { return UISkin.Col("dark", new Color(0.21f, 0.16f, 0.14f)); } }
public static Color Red { get { return UISkin.Col("red", new Color(0.74f, 0.26f, 0.19f)); } }
public static Color Line { get { return UISkin.Col("line", new Color(0.33f, 0.26f, 0.22f)); } }
public static Color Slot { get { return UISkin.Col("slot", new Color(0.80f, 0.76f, 0.70f)); } }
public static Color Coin { get { return UISkin.Col("coin", new Color(0.88f, 0.67f, 0.20f)); } }
public static Color Jade { get { return UISkin.Col("jade", new Color(0.27f, 0.60f, 0.52f)); } }
public static Color Grain { get { return UISkin.Col("grain", new Color(0.76f, 0.58f, 0.38f)); } }
public static Color Ok { get { return UISkin.Col("ok", new Color(0.36f, 0.62f, 0.31f)); } }
public static Color Star { get { return UISkin.Col("star", new Color(0.93f, 0.68f, 0.13f)); } }
public static Color Disabled { get { return UISkin.Col("disabled", new Color(0.66f, 0.62f, 0.58f)); } }
public static Color Purple { get { return UISkin.Col("purple", new Color(0.58f, 0.36f, 0.70f)); } }
public static Color BarColor { get { return UISkin.Col("bar", new Color(0.15f, 0.11f, 0.09f)); } }
public static Color Stroke { get { return UISkin.Col("stroke", new Color(0.12f, 0.1f, 0.1f)); } }
public static readonly Color White = Color.white;
public static Color Col(string id, Color fallback)
{
return UISkin.Col(id, fallback);
}
static Sprite round;
/// <summary>둥근 모서리 9-slice(모서리 22 px).</summary>
public static Sprite Round
{
get
{
if (round == null) round = MakeRound(64, 22);
return round;
}
}
public static Sprite Circle
{
get { return ProceduralSprites.Circle; }
}
static Sprite MakeRound(int size, int radius)
{
var tex = new Texture2D(size, size, TextureFormat.RGBA32, false);
tex.wrapMode = TextureWrapMode.Clamp;
tex.hideFlags = HideFlags.DontSave;
var px = new Color[size * size];
for (int y = 0; y < size; y++)
for (int x = 0; x < size; x++)
{
float cx = Mathf.Clamp(x + 0.5f, radius, size - radius);
float cy = Mathf.Clamp(y + 0.5f, radius, size - radius);
float d = Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(cx, cy));
px[y * size + x] = new Color(1f, 1f, 1f, Mathf.Clamp01(radius - d + 0.5f));
}
tex.SetPixels(px);
tex.Apply();
float b = radius + 2;
var sp = Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f), 100f, 0, SpriteMeshType.FullRect, new Vector4(b, b, b, b));
sp.hideFlags = HideFlags.DontSave;
return sp;
}
// ---------- 기본 ----------
public static RectTransform Rect(string name, Transform parent, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = rt.anchorMax = anchor;
rt.pivot = pivot;
rt.anchoredPosition = pos;
rt.sizeDelta = size;
return rt;
}
/// <summary>부모 전체에서 안쪽 여백(왼·오·위·아래)만 뺀 영역.</summary>
public static RectTransform Fill(string name, Transform parent, float l = 0f, float r = 0f, float t = 0f, float b = 0f)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.pivot = new Vector2(0.5f, 0.5f);
rt.offsetMin = new Vector2(l, b);
rt.offsetMax = new Vector2(-r, -t);
return rt;
}
/// <summary>위쪽 띠(부모 가로 전체 · 위에서 top 아래 · 높이 h).</summary>
public static RectTransform TopBand(string name, Transform parent, float top, float h, float l = 0f, float r = 0f)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0f, 1f);
rt.anchorMax = new Vector2(1f, 1f);
rt.pivot = new Vector2(0.5f, 1f);
rt.offsetMin = new Vector2(l, -top - h);
rt.offsetMax = new Vector2(-r, -top);
return rt;
}
/// <summary>
/// 칠하기 — rounded = 팩 카드(흰 바탕 + 굵은 외곽선 · 색 틴트 · 반투명이면 외곽선 없는 판) · false = 평면 띠.
/// </summary>
public static Image Paint(RectTransform rt, Color c, bool rounded = true, bool raycast = false)
{
var img = rt.gameObject.AddComponent<Image>();
img.color = c;
if (rounded && !UISkin.Apply(img, c.a >= 0.95f ? "card" : "soft"))
{
img.sprite = Round;
img.type = Image.Type.Sliced;
}
img.raycastTarget = raycast;
return img;
}
/// <summary>스킨 id 로 칠하기(없으면 둥근 도형).</summary>
public static Image Skin(RectTransform rt, string skin, Color c, bool raycast = false)
{
var img = rt.gameObject.AddComponent<Image>();
img.color = c;
if (!UISkin.Apply(img, skin))
{
img.sprite = Round;
img.type = Image.Type.Sliced;
}
img.raycastTarget = raycast;
return img;
}
/// <summary>아이콘(테마 id · 없으면 null).</summary>
public static Image Icon(Transform parent, string id, Vector2 anchor, Vector2 pivot, Vector2 pos, float size)
{
return UISkin.IconImage(parent, id, anchor, pivot, pos, new Vector2(size, size));
}
/// <summary>아이콘이 있으면 아이콘 · 없으면 원 + 글자(구 표시).</summary>
public static Image IconOrDot(Transform parent, string id, string mark, Color c, Vector2 anchor, Vector2 pivot, Vector2 pos, float size)
{
var ic = Icon(parent, id, anchor, pivot, pos, size);
if (ic != null) return ic;
var dot = Dot("Icon", parent, anchor, pivot, pos, size, c);
FillLabel(dot.transform, mark, Mathf.RoundToInt(size * 0.5f), White);
return dot;
}
/// <summary>제목 폰트(Sen-ExtraBold + 한글 폴백) + 흰 글자 외곽선.</summary>
public static Text Title(Text t, bool stroke = false)
{
t.font = UISkin.TitleFont;
if (stroke && UISkin.On) UISkin.Stroke(t, Stroke, 3f);
return t;
}
public static Image Box(string name, Transform parent, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size, Color c, bool rounded = true)
{
return Paint(Rect(name, parent, anchor, pivot, pos, size), c, rounded);
}
public static Image Dot(string name, Transform parent, Vector2 anchor, Vector2 pivot, Vector2 pos, float d, Color c)
{
var img = Rect(name, parent, anchor, pivot, pos, new Vector2(d, d)).gameObject.AddComponent<Image>();
if (d > 280f || !UISkin.Apply(img, "dot")) img.sprite = Circle; // 큰 원(후광 등)은 팩 외곽선이 흐려져서 평면 원
img.preserveAspect = true;
img.color = c;
img.raycastTarget = false;
return img;
}
/// <summary>테두리(외곽선 효과 · 둥근 상자에 굵은 선처럼 보임) · 테마 카드는 외곽선을 이미 가져서 기본 선 색이면 생략.</summary>
public static void Border(Graphic g, Color c, float w = 4f)
{
if (UISkin.On && c == Line) return;
if (UISkin.On) w = Mathf.Min(w, 3f);
var o = g.gameObject.AddComponent<Outline>();
o.effectColor = c;
o.effectDistance = new Vector2(w, -w);
o.useGraphicAlpha = false;
var o2 = g.gameObject.AddComponent<Outline>();
o2.effectColor = c;
o2.effectDistance = new Vector2(-w, w);
o2.useGraphicAlpha = false;
}
public static Text Label(Transform parent, string s, int size, Color c, TextAnchor align, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 box, bool bold = true)
{
var rt = Rect("Text", parent, anchor, pivot, pos, box);
return Style(rt.gameObject.AddComponent<Text>(), s, size, c, align, bold);
}
/// <summary>부모 전체에 꽉 찬 글자(가운데 정렬 기본).</summary>
public static Text FillLabel(Transform parent, string s, int size, Color c, TextAnchor align = TextAnchor.MiddleCenter, bool bold = true, float pad = 0f)
{
var rt = Fill("Text", parent, pad, pad, 0f, 0f);
return Style(rt.gameObject.AddComponent<Text>(), s, size, c, align, bold);
}
static Text Style(Text t, string s, int size, Color c, TextAnchor align, bool bold)
{
t.font = TextUtil.Font;
t.fontSize = size;
t.fontStyle = bold ? FontStyle.Bold : FontStyle.Normal;
t.alignment = align;
t.color = c;
t.horizontalOverflow = HorizontalWrapMode.Wrap;
t.verticalOverflow = VerticalWrapMode.Overflow;
t.supportRichText = true;
t.raycastTarget = false;
t.text = s;
return t;
}
public static Button Btn(string name, Transform parent, string label, int fontSize, Color bg, Color fg, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size, Action onClick)
{
var img = Skin(Rect(name, parent, anchor, pivot, pos, size), "button", bg, true);
var b = img.gameObject.AddComponent<Button>();
var cb = b.colors;
cb.disabledColor = new Color(0.75f, 0.75f, 0.75f, 0.85f);
b.colors = cb;
if (onClick != null) b.onClick.AddListener(() => onClick());
var t = FillLabel(img.transform, label, fontSize, fg, TextAnchor.MiddleCenter, true, 6f);
if (UISkin.On)
{
// 팩 버튼 = 아래 턱(약 16 px)이 있어 글자를 조금 올리고 흰 글자는 외곽선
var trt = t.rectTransform;
float side = Mathf.Min(16f, size.x * 0.08f); // 팩 버튼 외곽선 안쪽으로
trt.offsetMin = new Vector2(side, Mathf.Min(10f, size.y * 0.1f));
trt.offsetMax = new Vector2(-side, trt.offsetMax.y);
Title(t, fg.r + fg.g + fg.b > 2.4f);
}
return b;
}
public static Text LabelOf(Button b)
{
return b.GetComponentInChildren<Text>(true);
}
/// <summary>둥근 막대 게이지 · 반환 = 채움(SetBar 로 0~1).</summary>
public static RectTransform Bar(string name, Transform parent, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size, Color bg, Color fg)
{
if (UISkin.Has("bar_bg"))
{
// 팩 슬라이더: 어두운 홈(스킨 원색) + 안쪽 홈에 채움(용도 색 틴트)
var back = Skin(Rect(name, parent, anchor, pivot, pos, size), "bar_bg", Col("bar_track", White));
float pad = Mathf.Clamp(size.y * 0.16f, 3f, 6f);
var track = Fill("Track", back.transform, pad, pad, pad, pad);
var f = Fill("Fill", track);
f.anchorMax = new Vector2(0f, 1f);
Skin(f, "bar_fill", fg);
return f;
}
var b0 = Box(name, parent, anchor, pivot, pos, size, bg);
var fill = Fill("Fill", b0.transform);
fill.anchorMax = new Vector2(0f, 1f);
Paint(fill, fg);
return fill;
}
public static void SetBar(RectTransform fill, float v)
{
v = Mathf.Clamp01(v);
fill.anchorMax = new Vector2(v, 1f);
fill.gameObject.SetActive(v > 0.001f);
}
/// <summary>세로 스크롤 격자 목록(칸 크기 cell · 열 columns) · 반환 = 내용(자식 = 칸).</summary>
public static RectTransform ScrollGrid(string name, Transform parent, float l, float r, float t, float b, Vector2 cell, Vector2 spacing, int columns, out ScrollRect scroll)
{
var vp = Fill(name, parent, l, r, t, b);
var vimg = vp.gameObject.AddComponent<Image>();
vimg.color = new Color(1f, 1f, 1f, 0f);
vimg.raycastTarget = true;
vp.gameObject.AddComponent<RectMask2D>();
var content = new GameObject("Content", typeof(RectTransform)).GetComponent<RectTransform>();
content.SetParent(vp, false);
content.anchorMin = new Vector2(0f, 1f);
content.anchorMax = new Vector2(1f, 1f);
content.pivot = new Vector2(0.5f, 1f);
content.offsetMin = content.offsetMax = Vector2.zero;
var grid = content.gameObject.AddComponent<GridLayoutGroup>();
grid.cellSize = cell;
grid.spacing = spacing;
grid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
grid.constraintCount = columns;
grid.childAlignment = TextAnchor.UpperCenter;
grid.padding = new RectOffset(0, 0, 6, 12);
var fit = content.gameObject.AddComponent<ContentSizeFitter>();
fit.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
scroll = vp.gameObject.AddComponent<ScrollRect>();
scroll.viewport = vp;
scroll.content = content;
scroll.horizontal = false;
scroll.vertical = true;
scroll.movementType = ScrollRect.MovementType.Clamped;
scroll.scrollSensitivity = 60f;
return content;
}
public static void Clear(Transform t)
{
for (int i = t.childCount - 1; i >= 0; i--)
{
var c = t.GetChild(i).gameObject;
c.SetActive(false);
UnityEngine.Object.Destroy(c);
}
}
/// <summary>목록 칸(격자 자식) — 둥근 카드 + 클릭.</summary>
public static Image Cell(Transform content, string name, Color fill, Action onClick)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(content, false);
var img = Paint((RectTransform)go.transform, fill);
img.raycastTarget = true;
if (onClick != null)
{
var b = go.AddComponent<Button>();
b.onClick.AddListener(() => onClick());
}
return img;
}
// ---------- 영웅 표시 ----------
public static Color TypeColor(UnitType t)
{
switch (t)
{
case UnitType.Infantry: return Col("type_infantry", new Color(0.62f, 0.33f, 0.22f));
case UnitType.Spear: return Col("type_spear", new Color(0.30f, 0.50f, 0.30f));
case UnitType.Cavalry: return Col("type_cavalry", new Color(0.28f, 0.32f, 0.45f));
case UnitType.Archer: return Col("type_archer", new Color(0.70f, 0.52f, 0.20f));
case UnitType.Strategist: return Col("type_strategist", Purple);
}
return Dark;
}
/// <summary>병종 아이콘 id(테마 · 신규 제작 cls_*).</summary>
public static string TypeIconId(UnitType t)
{
switch (t)
{
case UnitType.Infantry: return "cls_infantry";
case UnitType.Spear: return "cls_spear";
case UnitType.Cavalry: return "cls_cavalry";
case UnitType.Archer: return "cls_archer";
case UnitType.Strategist: return "cls_strategist";
}
return null;
}
/// <summary>신규 제작(B) 치환이 들어온 아이콘인가(임시 팩 아이콘이 뜻과 어긋나는 곳은 그때만 아이콘을 쓴다 · 예: 병종).</summary>
public static bool HasFinalIcon(string id)
{
var e = UISkin.Theme != null ? UISkin.Theme.IconEntryOf(id) : null;
return e != null && e.samguk != null;
}
/// <summary>등급(승급 green… · 장비 white…) 표시 이름.</summary>
public static string GradeKo(string g)
{
switch (g)
{
case "white": return "흰";
case "green": return "녹";
case "blue": return "청";
case "purple": return "자";
case "orange": return "주";
case "gold": return "금";
}
return g;
}
public static Color GradeColor(string g)
{
switch (g)
{
case "white": return Col("grade_white", new Color(0.80f, 0.79f, 0.76f));
case "green": return Col("grade_green", new Color(0.38f, 0.64f, 0.33f));
case "blue": return Col("grade_blue", new Color(0.29f, 0.48f, 0.78f));
case "purple": return Col("grade_purple", Purple);
case "orange": return Col("grade_orange", new Color(0.90f, 0.50f, 0.18f));
case "gold": return Col("grade_gold", new Color(0.92f, 0.74f, 0.16f));
}
return Slot;
}
/// <summary>등급 칸(팩 ItemFrame 등급 색 · 신규 제작 frame_* 오면 치환) — 스킨이 없으면 등급 색 카드.</summary>
public static Image GradeFrame(Image img, string grade)
{
if (!string.IsNullOrEmpty(grade) && UISkin.Apply(img, "frame_" + grade))
{
img.color = White;
return img;
}
UISkin.Apply(img, "card");
img.color = GradeColor(grade);
return img;
}
/// <summary>별 줄(★ 아이콘 n개 · 테마 없으면 글자).</summary>
public static void StarRow(Transform parent, int n, int max, Vector2 anchor, Vector2 pivot, Vector2 pos, float size)
{
if (UISkin.Icon("star_on") == null)
{
Label(parent, Stars(n, max), Mathf.RoundToInt(size), Star, TextAnchor.MiddleCenter, anchor, pivot, pos, new Vector2(size * max, size + 6f));
return;
}
var row = Rect("Stars", parent, anchor, pivot, pos, new Vector2(size * max * 0.92f, size));
for (int i = 0; i < max; i++)
Icon(row, i < n ? "star_on" : "star_off", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(i * size * 0.92f, 0f), size);
}
public static string Stars(int n, int max)
{
var sb = new System.Text.StringBuilder();
for (int i = 0; i < max; i++) sb.Append(i < n ? "★" : "☆");
return sb.ToString();
}
static readonly Dictionary<string, Sprite> artCache = new Dictionary<string, Sprite>();
/// <summary>일러 = Resources/Units/&lt;art&gt; front_idle 첫 프레임(없으면 다른 클립 첫 프레임 · 없으면 null = 도형).</summary>
public static Sprite ArtSprite(string art)
{
if (string.IsNullOrEmpty(art)) return null;
Sprite sp;
if (artCache.TryGetValue(art, out sp)) return sp;
var a = UnitArt.Load(art);
Sprite[] frames = null;
if (a != null && !a.Clips.TryGetValue("front_idle", out frames))
foreach (var kv in a.Clips)
{
frames = kv.Value;
break;
}
sp = frames != null && frames.Length > 0 ? frames[0] : null;
artCache[art] = sp;
return sp;
}
/// <summary>
/// 초상(틀 안을 마스크) — 일러가 있으면 그 그림(zoom &gt; 1 = 위쪽 얼굴 부분 확대) · 없으면 병종 색 원 + 병종 글자.
/// circle = 원형 틀(편성 자리) · false = 둥근 사각.
/// </summary>
public static RectTransform Portrait(string name, Transform parent, HeroDb.Hero h, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size, bool circle, float zoom, Color frame)
{
var outer = Rect(name, parent, anchor, pivot, pos, size);
var rt = outer;
bool framed = frame.a > 0.01f && UISkin.On;
if (framed)
{
// 팩 틀(외곽선) 바깥 + 안쪽 마스크(외곽선 안으로 그림이 들어가게)
var fimg = outer.gameObject.AddComponent<Image>();
fimg.color = frame;
fimg.raycastTarget = false;
if (circle)
{
if (!UISkin.Apply(fimg, "circle_frame")) fimg.sprite = Circle;
fimg.preserveAspect = true;
}
else UISkin.Apply(fimg, "card");
float pad = Mathf.Max(5f, Mathf.Min(size.x, size.y) * 0.05f);
rt = Fill("Mask", outer, pad, pad, pad, pad + (circle ? 0f : 2f));
size = new Vector2(size.x - pad * 2f, size.y - pad * 2f);
}
var bg = rt.gameObject.AddComponent<Image>();
bg.color = framed ? new Color(frame.r * 0.9f, frame.g * 0.9f, frame.b * 0.9f, 1f) : frame;
bg.raycastTarget = false;
if (circle) bg.sprite = Circle;
else if (!framed || !UISkin.Apply(bg, "portrait_mask"))
{
bg.sprite = Round;
bg.type = Image.Type.Sliced;
}
var mask = rt.gameObject.AddComponent<Mask>();
mask.showMaskGraphic = frame.a > 0.01f;
if (frame.a <= 0.01f) bg.color = Color.white; // 투명 틀: 알파 0 그래픽은 스텐실을 안 써서 자식이 안 보임 → 불투명 + 그리지 않음
var sp = h != null ? ArtSprite(h.art) : null;
if (sp != null)
{
// zoom ≤ 1 = 전신 가운데 · zoom > 1 = 위쪽(얼굴) 확대
bool bust = zoom > 1f;
var a = bust
? Rect("Art", rt, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, size.y * 0.06f * zoom), size * zoom)
: Rect("Art", rt, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, size * zoom);
var img = a.gameObject.AddComponent<Image>();
img.sprite = sp;
img.preserveAspect = true;
img.raycastTarget = false;
}
else if (h != null)
{
float d = Mathf.Min(size.x, size.y) * 0.72f;
var dot = Dot("Shape", rt, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, d, TypeColor(h.type));
Title(FillLabel(dot.transform, UnitTypeInfo.Mark(h.type), Mathf.RoundToInt(d * 0.5f), White), true);
}
return outer;
}
/// <summary>병종 배지(병종 색 칩 + 글자 · 신규 제작 cls_* 아이콘이 오면 아이콘).</summary>
public static Image TypeBadge(Transform parent, UnitType t, Vector2 anchor, Vector2 pivot, Vector2 pos, float h)
{
var b = Box("Type", parent, anchor, pivot, pos, new Vector2(h * 1.3f, h), UISkin.On ? TypeColor(t) : Dark);
string icon = TypeIconId(t);
if (HasFinalIcon(icon)) Icon(b.transform, icon, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 1f), h * 0.8f);
else Title(FillLabel(b.transform, UnitTypeInfo.Mark(t), Mathf.RoundToInt(h * 0.62f), White), true);
return b;
}
}
}