diff --git a/Assets/Resources/Units/art_size.csv b/Assets/Resources/Units/art_size.csv
index 8e63d6b7..7e33b88f 100644
--- a/Assets/Resources/Units/art_size.csv
+++ b/Assets/Resources/Units/art_size.csv
@@ -125,4 +125,4 @@ zhoucang,0,173.3,538.3,453,598.0,276.0,856,1091.8,,1.092,
zhoutai,0,162.2,609.1,429,599.5,273.0,885,993.6,,1.026,
zhugejin,0,160.2,562.8,372,501.0,270.0,870,954.7,,1.08,
zhugeliang,0,140.2,529.3,336,469.0,264.0,886,962.8,,1.18,
-zhurong,0,118.3,458.4,0,0,0,773,821.6,,1.18,눈 수동 실측 2026-11-10(눈 간격·눈 높이): 눈 덩어리 중심 x 524.4 · 642.7(짙은 피부 = 피부 판정 밖)
+zhurong,0,118.6,458.1,261,524.0,258.0,773,758.1,,1.02,눈 수동 실측 2026-11-10(눈 간격·눈 높이): 눈 덩어리 중심 x 524.4 · 642.7(짙은 피부 = 피부 판정 밖)
diff --git a/Assets/Resources/Units/zhurong/front_action/f000.png b/Assets/Resources/Units/zhurong/front_action/f000.png
index c39af9e2..29288658 100644
Binary files a/Assets/Resources/Units/zhurong/front_action/f000.png and b/Assets/Resources/Units/zhurong/front_action/f000.png differ
diff --git a/Assets/Resources/Units/zhurong/front_idle/f000.png b/Assets/Resources/Units/zhurong/front_idle/f000.png
index b6aea447..d24b7318 100644
Binary files a/Assets/Resources/Units/zhurong/front_idle/f000.png and b/Assets/Resources/Units/zhurong/front_idle/f000.png differ
diff --git a/Assets/Resources/Units/zhurong/front_ready/f000.png b/Assets/Resources/Units/zhurong/front_ready/f000.png
index 0bd3b3e5..56e96b08 100644
Binary files a/Assets/Resources/Units/zhurong/front_ready/f000.png and b/Assets/Resources/Units/zhurong/front_ready/f000.png differ
diff --git a/Assets/Scripts/OutGame/UI/LobbyScreen.cs b/Assets/Scripts/OutGame/UI/LobbyScreen.cs
index b40ba2ce..26a8725e 100644
--- a/Assets/Scripts/OutGame/UI/LobbyScreen.cs
+++ b/Assets/Scripts/OutGame/UI/LobbyScreen.cs
@@ -16,6 +16,8 @@ namespace TKS.OutGame
Text sortieSub;
Text chestBadge;
RectTransform field;
+ /// 로비 출전 영웅 표준 키(px · 크기 표 size_px 가 이 높이) — 유비가 예전 칸 맞춤 때와 같은 크기(797×826 틀 → 196 칸 = ×0.246 · size 837 × 0.246 ≈ 206).
+ const float LobbyUnitPx = 206f;
static RectTransform At(Transform p, string name, float x, float y, float w, float h)
{
@@ -176,7 +178,9 @@ namespace TKS.OutGame
{
var h = S.Def(lineup[i].id);
float x = (i - (n - 1) / 2f) * w;
- UiKit.Portrait("Hero" + i, field, h, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(x, 20f), new Vector2(w, 340f), false, 1f, Color.clear);
+ // 크기 표 기준으로 발 맞춰 세움(PD 2026-11-10 「로비 관우만 작게 보임」 = 칸에 그림 틀을 맞추면 틀이 넓은 관우만 작아짐) · 일러 없으면 초상
+ if (UiKit.UnitStand("Hero" + i, field, h, new Vector2(0.5f, 0f), new Vector2(x, 20f), LobbyUnitPx, cfg.mountedArtMaxTop) == null)
+ UiKit.Portrait("Hero" + i, field, h, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(x, 20f), new Vector2(w, 340f), false, 1f, Color.clear);
}
}
}
diff --git a/Assets/Scripts/OutGame/UI/OutGameRoot.cs b/Assets/Scripts/OutGame/UI/OutGameRoot.cs
index e22fa0f5..434ce1e3 100644
--- a/Assets/Scripts/OutGame/UI/OutGameRoot.cs
+++ b/Assets/Scripts/OutGame/UI/OutGameRoot.cs
@@ -273,12 +273,17 @@ namespace TKS.OutGame
if (UISkin.On)
{
tabImages[i].gameObject.SetActive(on);
- tabTexts[i].gameObject.SetActive(on);
+ // 글자 = 선택 안 된 탭도 항상(PD 2026-11-10 「선택되지 않았을 때에도 글자가 보이게」) · 선택 = 흰 큰 글자 · 아니면 흐린 작은 글자(동맹 = 잠김 색 그대로)
+ var t = tabTexts[i];
+ t.gameObject.SetActive(true);
+ t.color = on ? UiKit.White : tabOffText;
+ t.fontSize = on ? TabLabelOn : TabLabelOff;
+ t.rectTransform.anchoredPosition = new Vector2(0f, on ? TabLabelYOn : TabLabelYOff);
var ic = i < tabIcons.Count ? tabIcons[i] : null;
if (ic != null)
{
- ic.sizeDelta = Vector2.one * (on ? 118f : 104f);
- ic.anchoredPosition = new Vector2(0f, on ? 48f : 30f);
+ ic.sizeDelta = Vector2.one * (on ? TabIconOn : TabIconOff);
+ ic.anchoredPosition = new Vector2(0f, on ? TabIconYOn : TabIconYOff);
}
}
else tabImages[i].color = on ? UiKit.Red : UiKit.Dark;
@@ -438,6 +443,24 @@ namespace TKS.OutGame
// ---------- 하단 탭 ----------
+ // 탭 배치(px · 탭 바 아래 기준) — 글자를 항상 보이게 바꾸며(PD 2026-11-10) 아이콘을 글자 위로 올림 · 선택 탭 = 솟음 판(168) 위로 크게
+ const float TabIconOff = 96f, TabIconOn = 118f, TabIconYOff = 46f, TabIconYOn = 52f;
+ const int TabLabelOff = 30, TabLabelOn = 34;
+ const float TabLabelYOff = 24f, TabLabelYOn = 30f;
+ Color tabOffText = Color.gray;
+
+ /// 글자가 켜진 하단 탭 수(테스트 · PD 2026-11-10 「선택되지 않은 탭도 글자」).
+ public int VisibleTabLabels
+ {
+ get
+ {
+ int n = 0;
+ foreach (var t in tabTexts)
+ if (t != null && t.gameObject.activeSelf) n++;
+ return n;
+ }
+ }
+
void BuildTabBar()
{
var bar = UiKit.Rect("TabBar", mainCanvas.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), Vector2.zero, new Vector2(Reference.x, TabBarH));
@@ -451,6 +474,7 @@ namespace TKS.OutGame
string[] marks = { "영", "보", "내", "잠", "거" };
string[] icons = { "tab_hero", "tab_treasure", "tab_domestic", "tab_alliance", "tab_trade" };
Color off = UISkin.Col("bar_text_off", new Color(0.62f, 0.58f, 0.55f));
+ tabOffText = off;
for (int i = 0; i < names.Length; i++)
{
int k = i;
@@ -470,7 +494,7 @@ namespace TKS.OutGame
img = UiKit.Skin(focus, "tab_focus", UiKit.Col("tab_focus", UiKit.Red));
if (UISkin.Has("tab_focus_light"))
UiKit.Skin(UiKit.Rect("Light", focus, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -6f), new Vector2(188f, 70f)), "tab_focus_light", UiKit.Col("tab_focus_light", UiKit.White));
- var ic = UiKit.Icon(cell, icons[i], new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 30f), 104f);
+ var ic = UiKit.Icon(cell, icons[i], new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, TabIconYOff), TabIconOff);
if (ic != null)
{
if (i == 3) ic.color = UiKit.Col("tab_locked", off);
@@ -489,7 +513,7 @@ namespace TKS.OutGame
UiKit.FillLabel(dot.transform, marks[i], 32, i == 3 ? new Color(0.7f, 0.66f, 0.62f) : UiKit.White);
}
var t = UISkin.On
- ? UiKit.Label(cell, names[i], 36, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0.5f), new Vector2(0f, 36f), new Vector2(204f, 50f))
+ ? UiKit.Label(cell, names[i], TabLabelOff, off, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0.5f), new Vector2(0f, TabLabelYOff), new Vector2(204f, 50f))
: UiKit.Label(cell, names[i], 32, i == 3 ? off : UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 14f), new Vector2(200f, 48f));
if (UISkin.On) UiKit.Title(t, true);
tabImages.Add(img);
@@ -499,6 +523,12 @@ namespace TKS.OutGame
public void OnTab(int i)
{
+ // 선택된 탭을 다시 누르면 닫기 = 로비(PD 2026-11-10 「해당 메뉴가 선택된 상태일 경우 X(닫기 버튼)처럼 동작」)
+ if (current != null && current != Lobby && current.Tab == i)
+ {
+ ShowLobby();
+ return;
+ }
switch (i)
{
case 0: Show(HeroList); break;
diff --git a/Assets/Scripts/OutGame/UI/UiKit.cs b/Assets/Scripts/OutGame/UI/UiKit.cs
index 9319b5f8..67a2bbfb 100644
--- a/Assets/Scripts/OutGame/UI/UiKit.cs
+++ b/Assets/Scripts/OutGame/UI/UiKit.cs
@@ -768,6 +768,28 @@ namespace TKS.OutGame
return sp;
}
+ ///
+ /// 서 있는 유닛(발 = anchor + pos) — 크기 표 size_px 기준 배율(standardPx ÷ size_px = 머리 크기 통일 · 영웅 목록·전투와 같은 기준) ·
+ /// 기마 = 머리 끝이 standardPx × mountedMaxTop 을 넘지 않게(전투 FrameAnimator 와 같은 상한) · 일러·피벗이 없으면 null(호출 쪽이 초상으로 대체).
+ /// PD 2026-11-10 「로비에 배치된 관우의 크기만 작게 보임」 — 칸에 그림 틀을 맞추면 틀이 넓은 유닛(관우 언월도)만 작아짐.
+ ///
+ public static RectTransform UnitStand(string name, Transform parent, HeroDb.Hero h, Vector2 anchor, Vector2 pos, float standardPx, float mountedMaxTop)
+ {
+ var sp = h != null ? ArtSprite(h.art) : null;
+ var a = h != null ? UnitArt.Load(h.art) : null;
+ if (sp == null || a == null || a.Meta.pivot == null || a.Meta.pivot.Length < 2) return null;
+ float size = a.SizePx > 0.5f ? a.SizePx : a.BodyPx;
+ if (size <= 0.5f) return null;
+ float k = standardPx / size;
+ float top = a.BodyPx * k;
+ if (a.Mounted && mountedMaxTop > 0.01f && top > standardPx * mountedMaxTop) k *= standardPx * mountedMaxTop / top;
+ var rt = Rect(name, parent, anchor, new Vector2(0.5f, a.Meta.pivot[1]), pos, sp.rect.size * k);
+ var img = rt.gameObject.AddComponent();
+ img.sprite = sp;
+ img.raycastTarget = false;
+ return rt;
+ }
+
///
/// 초상(틀 안을 마스크) — 일러가 있으면 그 그림(zoom > 1 = 위쪽 얼굴 부분 확대) · 없으면 병종 색 원 + 병종 글자.
/// circle = 원형 틀(편성 자리) · false = 둥근 사각.
diff --git a/Assets/Tests/PlayMode/OutGameTests.cs b/Assets/Tests/PlayMode/OutGameTests.cs
index 92f9fbac..772b831a 100644
--- a/Assets/Tests/PlayMode/OutGameTests.cs
+++ b/Assets/Tests/PlayMode/OutGameTests.cs
@@ -478,6 +478,14 @@ namespace TKS.Tests
og.OnTab(0);
Assert.AreEqual(og.HeroList, og.Current);
yield return null;
+ // PD 2026-11-10: 하단 탭 글자 = 선택 안 된 탭도 · 선택된 탭 다시 누르면 닫기(로비)
+ Assert.AreEqual(5, og.VisibleTabLabels, "하단 탭 글자 5개 모두 보임");
+ og.OnTab(0);
+ Assert.AreEqual(og.Lobby, og.Current, "선택된 탭 다시 = 닫기(로비)");
+ Assert.AreEqual(5, og.VisibleTabLabels, "로비에서도 탭 글자 보임");
+ og.OnTab(0);
+ Assert.AreEqual(og.HeroList, og.Current, "다시 열림");
+ yield return null;
Assert.AreEqual(og.Session.Owned(false).Count, og.HeroList.CardCount, "보유 장수 카드");
// 미보유 장수 = 빈 카드가 아니라 그 영웅 그림 카드(PD 2026-11-10 「유닛 목록에 장수들의 모습이 보이도록」)
int heroTotal = 0;
diff --git a/Assets/UI/Samguk/menu/tab_alliance.png b/Assets/UI/Samguk/menu/tab_alliance.png
index a7c4ae31..7121676b 100644
Binary files a/Assets/UI/Samguk/menu/tab_alliance.png and b/Assets/UI/Samguk/menu/tab_alliance.png differ
diff --git a/Assets/UI/Samguk/menu/tab_domestic.png b/Assets/UI/Samguk/menu/tab_domestic.png
index 14f7609d..c2b4209b 100644
Binary files a/Assets/UI/Samguk/menu/tab_domestic.png and b/Assets/UI/Samguk/menu/tab_domestic.png differ
diff --git a/Assets/UI/Samguk/menu/tab_hero.png b/Assets/UI/Samguk/menu/tab_hero.png
index b167d046..930cd7e3 100644
Binary files a/Assets/UI/Samguk/menu/tab_hero.png and b/Assets/UI/Samguk/menu/tab_hero.png differ
diff --git a/Assets/UI/Samguk/menu/tab_trade.png b/Assets/UI/Samguk/menu/tab_trade.png
index 79701881..9af78bb1 100644
Binary files a/Assets/UI/Samguk/menu/tab_trade.png and b/Assets/UI/Samguk/menu/tab_trade.png differ
diff --git a/Assets/UI/Samguk/menu/tab_treasure.png b/Assets/UI/Samguk/menu/tab_treasure.png
index c08ff5b7..fb2072e6 100644
Binary files a/Assets/UI/Samguk/menu/tab_treasure.png and b/Assets/UI/Samguk/menu/tab_treasure.png differ
diff --git a/Assets/UI/Samguk/ui_delivered.csv b/Assets/UI/Samguk/ui_delivered.csv
index a3232d9d..4fdc71b3 100644
--- a/Assets/UI/Samguk/ui_delivered.csv
+++ b/Assets/UI/Samguk/ui_delivered.csv
@@ -70,11 +70,11 @@ skill_spear_a,logo_skill,Assets/UI/Samguk/logo_skill/skill_spear_a.png,1ee53fa18
skill_spear_p,logo_skill,Assets/UI/Samguk/logo_skill/skill_spear_p.png,1ea22555662c8c19934064e0d54a4076,ui_logo_skill
skill_strategist_a,logo_skill,Assets/UI/Samguk/logo_skill/skill_strategist_a.png,1caceeaaa3e566e50ef9e80bdd3d30f7,ui_logo_skill
skill_strategist_p,logo_skill,Assets/UI/Samguk/logo_skill/skill_strategist_p.png,67a37d7ef0e37f64169a7c3014aa937a,ui_logo_skill
-tab_alliance,menu,Assets/UI/Samguk/menu/tab_alliance.png,b43aa0b723ce2953c5b1b179feae9969,ui_menu
-tab_domestic,menu,Assets/UI/Samguk/menu/tab_domestic.png,3f9a7e8b0110ff3543b5d346618d639d,ui_menu
-tab_hero,menu,Assets/UI/Samguk/menu/tab_hero.png,b552c5a1d72dc1e50381b665500dd41e,ui_menu
-tab_trade,menu,Assets/UI/Samguk/menu/tab_trade.png,46c477c1f8defb62b16630045891ae99,ui_menu
-tab_treasure,menu,Assets/UI/Samguk/menu/tab_treasure.png,a5b6125f6b46748a69b677a50bb01dc7,ui_menu
+tab_alliance,menu,Assets/UI/Samguk/menu/tab_alliance.png,baef297a314e2848f0104a8930ac8426,ui_tab_casual_20261110
+tab_domestic,menu,Assets/UI/Samguk/menu/tab_domestic.png,e7df3d5127965baf0904d86356780b19,ui_tab_casual_20261110
+tab_hero,menu,Assets/UI/Samguk/menu/tab_hero.png,6e9a32f84d1e2221755e792decd002c7,ui_tab_casual_20261110
+tab_trade,menu,Assets/UI/Samguk/menu/tab_trade.png,aebe733f146eb20a22fd25ccfd4d0c81,ui_tab_casual_20261110
+tab_treasure,menu,Assets/UI/Samguk/menu/tab_treasure.png,f7dc5942a8caf78310541826105b477f,ui_tab_casual_20261110
tile_bridge,gimmick,Assets/UI/Samguk/gimmick/tile_bridge.png,80728d733f5477174a209c42feab1aba,ui_gimmick
tile_burning,gimmick,Assets/UI/Samguk/gimmick/tile_burning.png,a49ab2ede2802a68b40f3410817f9bc6,ui_gimmick
tile_fog,gimmick,Assets/UI/Samguk/gimmick/tile_fog.png,7b9ab096a721c3efcc229dabb53efb41,ui_gimmick