2026-11-05 12:11:10 +00:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
|
|
namespace TKS.OutGame
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2026-11-09 08:10:25 +00:00
|
|
|
|
/// 본영 로비(Fable 제안서 §4-1 · PD 2026-11-09 「카피바라GO·탕탕특공대 참고 상업 수준」) — 키아트 + 위 비네트 · 진행 띠(지도 핀 · 장 이름 · 진행 바 n/m) ·
|
|
|
|
|
|
/// 좌우 세로 버튼 열(임무·우편·동맹 / 상점·상자·이벤트 · 미구현 = 잠금 자리) · 출전 편성 5인 · 아래 버튼 행(편성 원 · 출정 3층 · 연무장 원) + 뒤 어두운 띠 · 하단 탭 5.
|
|
|
|
|
|
/// 본문 좌표 = 절대 y − 224.
|
2026-11-05 12:11:10 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class LobbyScreen : OutGameScreen
|
|
|
|
|
|
{
|
|
|
|
|
|
Text progress;
|
2026-11-09 08:10:25 +00:00
|
|
|
|
Text progressNum;
|
|
|
|
|
|
RectTransform progressFill;
|
|
|
|
|
|
Text sortieSub;
|
|
|
|
|
|
Text chestBadge;
|
2026-11-05 12:11:10 +00:00
|
|
|
|
RectTransform field;
|
2026-11-11 14:51:31 +00:00
|
|
|
|
RectTransform scene; // 배경 그림 좌표 층(그림을 꽉 채움 · null = 배경 없음)
|
|
|
|
|
|
float artW;
|
|
|
|
|
|
float artH;
|
2026-11-10 04:50:57 +00:00
|
|
|
|
/// <summary>로비 출전 영웅 표준 키(px · 크기 표 size_px 가 이 높이) — 유비가 예전 칸 맞춤 때와 같은 크기(797×826 틀 → 196 칸 = ×0.246 · size 837 × 0.246 ≈ 206).</summary>
|
|
|
|
|
|
const float LobbyUnitPx = 206f;
|
2026-11-05 12:11:10 +00:00
|
|
|
|
|
2026-11-09 08:10:25 +00:00
|
|
|
|
static RectTransform At(Transform p, string name, float x, float y, float w, float h)
|
|
|
|
|
|
{
|
|
|
|
|
|
return UiKit.Rect(name, p, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, -y), new Vector2(w, h));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-05 12:11:10 +00:00
|
|
|
|
public override void Build()
|
|
|
|
|
|
{
|
2026-11-09 08:10:25 +00:00
|
|
|
|
Header("본영", "", false);
|
|
|
|
|
|
var outer = Body();
|
2026-11-05 12:11:10 +00:00
|
|
|
|
|
2026-11-11 14:51:31 +00:00
|
|
|
|
// 배경 = 테마 bg_lobby(관청 마당) · 위치·배율 = 표 lobby_scene.csv view 줄 · 없으면 땅 색 판
|
|
|
|
|
|
// PD 2026-11-11 「로비 화면을 조금 더 상단으로 · 캐릭터가 배경 이미지와 겹치지 않게 · 너무 어둡지 않게 · 배경에 움직이는 연출」
|
2026-11-06 14:20:57 +00:00
|
|
|
|
var art = UISkin.Icon("bg_lobby");
|
2026-11-11 14:51:31 +00:00
|
|
|
|
scene = null;
|
2026-11-06 14:20:57 +00:00
|
|
|
|
if (art != null)
|
|
|
|
|
|
{
|
2026-11-09 08:10:25 +00:00
|
|
|
|
var clip = UiKit.Fill("LobbyBg", outer);
|
2026-11-06 14:20:57 +00:00
|
|
|
|
clip.gameObject.AddComponent<RectMask2D>();
|
2026-11-11 14:51:31 +00:00
|
|
|
|
var view = LobbyScene.First("view");
|
|
|
|
|
|
// 표 좌표의 기준 = 원본 그림 px(view f_w × f_h) — 임포트 크기(최대 크기 제한으로 줄 수 있음)와 무관하게 같은 자리
|
|
|
|
|
|
artW = view != null && view.w > 0f ? view.w : art.rect.width;
|
|
|
|
|
|
artH = view != null && view.h > 0f ? view.h : art.rect.height;
|
|
|
|
|
|
float top = view != null ? view.y : 0f; // 그림의 이 줄(px)이 본문 맨 위
|
|
|
|
|
|
float zoom = view != null && view.scale > 0.01f ? view.scale : 1f; // 화면 폭 대비 배율(가운데 기준)
|
|
|
|
|
|
var art2 = UiKit.Rect("Art", clip, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f - top / Mathf.Max(1f, artH)), Vector2.zero, Vector2.zero);
|
|
|
|
|
|
float pad = (zoom - 1f) * 0.5f;
|
|
|
|
|
|
art2.anchorMin = new Vector2(-pad, 1f);
|
|
|
|
|
|
art2.anchorMax = new Vector2(1f + pad, 1f);
|
|
|
|
|
|
art2.sizeDelta = Vector2.zero;
|
|
|
|
|
|
art2.anchoredPosition = Vector2.zero;
|
2026-11-06 14:20:57 +00:00
|
|
|
|
var bimg = art2.gameObject.AddComponent<Image>();
|
|
|
|
|
|
bimg.sprite = art;
|
|
|
|
|
|
bimg.color = UISkin.Col("lobby_tint", Color.white);
|
|
|
|
|
|
bimg.raycastTarget = false;
|
|
|
|
|
|
var fit = bimg.gameObject.AddComponent<AspectRatioFitter>();
|
|
|
|
|
|
fit.aspectMode = AspectRatioFitter.AspectMode.WidthControlsHeight;
|
2026-11-11 14:51:31 +00:00
|
|
|
|
fit.aspectRatio = artW / Mathf.Max(1f, artH);
|
|
|
|
|
|
// 장면 층(Frame 캔버스 1장) = 뒤 연출(깃발·불빛) → 영웅(대기 프레임 재생) → 앞 연출(새·꽃잎)
|
|
|
|
|
|
scene = LobbyAmbient.Build(art2, artW, artH).Lineup;
|
2026-11-06 14:20:57 +00:00
|
|
|
|
}
|
2026-11-09 08:10:25 +00:00
|
|
|
|
// 위 비네트(Glow_Panel01 = 위 불투명 → 아래 투명 · 실측) · 아래 버튼 뒤 어두운 띠
|
|
|
|
|
|
var vg = UiKit.Rect("Vignette", outer, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(0f, 180f));
|
|
|
|
|
|
vg.anchorMin = new Vector2(0f, 1f);
|
|
|
|
|
|
vg.anchorMax = new Vector2(1f, 1f);
|
|
|
|
|
|
vg.sizeDelta = new Vector2(0f, 180f);
|
|
|
|
|
|
UiKit.Skin(vg, "scroll_fade", UiKit.Col("lobby_vignette", Color.clear)).enabled = UISkin.Has("scroll_fade");
|
|
|
|
|
|
var body = UiKit.Rect("Mock", outer, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(OutGameRoot.Reference.x, OutGameRoot.BodyH));
|
2026-11-11 14:51:31 +00:00
|
|
|
|
// 아래 버튼 뒤 띠 = 아래 진함 → 위 투명(위 비네트 그림을 뒤집어 씀) — 밝아진 배경에서 띠 위 경계가 직선으로 끊겨 보임(QA r1 M7)
|
|
|
|
|
|
var band = At(body, "BottomBand", -40f, 1180f, 1160f, 386f);
|
|
|
|
|
|
if (UISkin.Has("scroll_fade"))
|
|
|
|
|
|
{
|
|
|
|
|
|
UiKit.Skin(band, "scroll_fade", UiKit.Col("lobby_band", Color.clear));
|
|
|
|
|
|
// 위아래 뒤집기는 가운데 기준(At 의 피벗 = 왼쪽 위 → 그대로 뒤집으면 위로 올라감)
|
|
|
|
|
|
band.pivot = new Vector2(0f, 0.5f);
|
|
|
|
|
|
band.anchoredPosition = new Vector2(-40f, -(1180f + 386f * 0.5f));
|
|
|
|
|
|
band.localScale = new Vector3(1f, -1f, 1f);
|
|
|
|
|
|
}
|
|
|
|
|
|
else UiKit.Skin(band, "card", UiKit.Col("lobby_band", Color.clear));
|
2026-11-06 14:20:57 +00:00
|
|
|
|
|
2026-11-09 08:10:25 +00:00
|
|
|
|
// 진행 띠(절대 236~312) = 지도 핀 + 장 이름 + 진행 바 n/m
|
|
|
|
|
|
var pb = At(body, "Progress", 24f, 12f, 1032f, 76f);
|
|
|
|
|
|
UiKit.Skin(pb, "card", UiKit.Col("chip_dark", UiKit.Dark));
|
|
|
|
|
|
UiKit.DropShadow(UiKit.Icon(pb, "map_point", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(18f, 2f), 50f), 3f);
|
|
|
|
|
|
progress = UiKit.Title(UiKit.Label(pb, "", 34, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(78f, 2f), new Vector2(560f, 60f)), true);
|
|
|
|
|
|
progress.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
|
|
|
|
var bar = UiKit.Rect("Bar", pb, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-104f, 0f), new Vector2(240f, 26f));
|
|
|
|
|
|
UiKit.Skin(bar, "lv_bar_bg", UiKit.White);
|
|
|
|
|
|
progressFill = UiKit.Rect("Fill", bar, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(4f, 0f), new Vector2(0f, 16f));
|
|
|
|
|
|
UiKit.Skin(progressFill, "lv_fill", UiKit.Col("lv_fill", UiKit.Coin));
|
|
|
|
|
|
progressNum = UiKit.Title(UiKit.Label(pb, "", 26, UiKit.White, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-18f, 2f), new Vector2(80f, 50f)), true);
|
|
|
|
|
|
|
|
|
|
|
|
// 좌우 세로 버튼 열(166×150 · 미구현 = 잠금 자리)
|
|
|
|
|
|
SideButton(body, 24f, 106f, "lobby_mission", "임무", true, () => Root.ComingSoon("임무"));
|
|
|
|
|
|
SideButton(body, 24f, 272f, "lobby_mail", "우편", true, () => Root.ComingSoon("우편"));
|
|
|
|
|
|
SideButton(body, 24f, 438f, "lobby_alliance", "동맹", true, () => Root.ComingSoon("동맹", true));
|
|
|
|
|
|
SideButton(body, 890f, 106f, "lobby_shop", "상점", false, () => Root.Trade.OpenShop());
|
|
|
|
|
|
var chest = SideButton(body, 890f, 272f, "lobby_chest", "상자", false, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Root.Show(Root.Treasures);
|
|
|
|
|
|
Root.Treasures.SetPage(2);
|
|
|
|
|
|
});
|
|
|
|
|
|
var cb = UiKit.Skin(UiKit.Rect("Alert", chest, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-12f, -12f), new Vector2(44f, 46f)), "badge_circle", UiKit.Col("alert_red", UiKit.Red));
|
|
|
|
|
|
cb.preserveAspect = true;
|
|
|
|
|
|
chestBadge = UiKit.Title(UiKit.FillLabel(cb.transform, "", 24, UiKit.White), true);
|
|
|
|
|
|
SideButton(body, 890f, 438f, "lobby_event", "이벤트", true, () => Root.ComingSoon("이벤트"));
|
2026-11-05 12:11:10 +00:00
|
|
|
|
|
2026-11-11 14:51:31 +00:00
|
|
|
|
// 출전 편성 = 배경 마당 위 자리(표 slot 줄 · 그림 좌표) · 배경이 없으면 예전 한 줄 판
|
|
|
|
|
|
if (scene != null) field = scene;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var ground = UiKit.Fill("Field", body, 24f, 24f, 140f, 520f);
|
|
|
|
|
|
UiKit.Paint(ground, new Color(0.47f, 0.52f, 0.36f));
|
|
|
|
|
|
var shadow = UiKit.Box("Shadow", ground, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 70f), new Vector2(940f, 150f), new Color(0.2f, 0.18f, 0.14f, 0.55f), false);
|
|
|
|
|
|
shadow.sprite = UiKit.Circle;
|
|
|
|
|
|
shadow.type = Image.Type.Simple;
|
|
|
|
|
|
field = UiKit.Fill("Lineup", ground, 20f, 20f, 40f, 40f);
|
|
|
|
|
|
}
|
2026-11-05 12:11:10 +00:00
|
|
|
|
|
2026-11-09 08:10:25 +00:00
|
|
|
|
// 아래 버튼 행(절대 1500~1740) = 편성 원 · 출정 3층 · 연무장 원
|
|
|
|
|
|
RoundButton(body, 160f, "btn_formation", "편성", () => Root.Show(Root.Formation));
|
|
|
|
|
|
RoundButton(body, 920f, "btn_arena", "연무장", () =>
|
2026-11-05 15:22:54 +00:00
|
|
|
|
{
|
|
|
|
|
|
Root.Arena.SetPage(0);
|
|
|
|
|
|
Root.Show(Root.Arena); // 연무장(PD #1093 C)
|
|
|
|
|
|
});
|
2026-11-09 08:10:25 +00:00
|
|
|
|
var so = At(body, "Sortie", 310f, 1290f, 460f, 160f);
|
|
|
|
|
|
var soImg = UiKit.Skin(so, "tapered1", UiKit.Red, true);
|
|
|
|
|
|
so.gameObject.AddComponent<Button>().onClick.AddListener(() => Root.OpenWorldMap());
|
|
|
|
|
|
var gl = UiKit.Rect("Gloss", so, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -10f), new Vector2(400f, 70f));
|
|
|
|
|
|
UiKit.Skin(gl, "tapered2", new Color(1f, 1f, 1f, 0.3f)).enabled = UISkin.Has("tapered2");
|
|
|
|
|
|
UiKit.Icon(so, "tapered3", new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(60f, -26f), 24f);
|
|
|
|
|
|
UiKit.Title(UiKit.Label(so, "출정", 64, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 18f), new Vector2(440f, 90f)), true);
|
|
|
|
|
|
var chip = UiKit.Skin(UiKit.Rect("Chip", so, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 14f), new Vector2(300f, 44f)), "chip", UiKit.Col("chip_dark", UiKit.Dark));
|
|
|
|
|
|
sortieSub = UiKit.Title(UiKit.FillLabel(chip.transform, "", 26, UiKit.White), true);
|
2026-11-05 12:11:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-09 08:10:25 +00:00
|
|
|
|
/// <summary>측면 버튼(팩 Button_Round03_Dark 166×150 · 아이콘 96 · 라벨 28 · 잠금 = 흐림 + 자물쇠).</summary>
|
|
|
|
|
|
Transform SideButton(RectTransform body, float x, float y, string icon, string label, bool locked, System.Action onClick)
|
2026-11-05 12:11:10 +00:00
|
|
|
|
{
|
2026-11-09 08:10:25 +00:00
|
|
|
|
var b = At(body, "Side_" + label, x, y, 166f, 150f);
|
|
|
|
|
|
UiKit.Skin(b, "btn_round03", UiKit.White, true);
|
|
|
|
|
|
b.gameObject.AddComponent<Button>().onClick.AddListener(() => onClick());
|
|
|
|
|
|
var ic = UiKit.Icon(b, icon, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -10f), 92f);
|
|
|
|
|
|
if (ic != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
UiKit.DropShadow(ic, 3f);
|
|
|
|
|
|
if (locked) ic.color = UiKit.Col("tab_locked", UiKit.InkSoft);
|
|
|
|
|
|
}
|
|
|
|
|
|
UiKit.Title(UiKit.Label(b, label, 28, locked ? UiKit.InkSoft : UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 4f), new Vector2(160f, 40f)), true);
|
|
|
|
|
|
if (locked) UiKit.Icon(b, "lock_s", new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-24f, -28f), 40f);
|
|
|
|
|
|
return b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>원형 버튼(팩 Button_Circle122 124 · 진남 틴트 · 아이콘 72 · 라벨 30 아래).</summary>
|
|
|
|
|
|
void RoundButton(RectTransform body, float cx, string icon, string label, System.Action onClick)
|
|
|
|
|
|
{
|
|
|
|
|
|
var b = At(body, "Round_" + label, cx - 62f, 1290f, 124f, 124f);
|
|
|
|
|
|
var img = UiKit.Skin(b, "circle122", UiKit.Col("menu_btn", UiKit.Dark), true);
|
|
|
|
|
|
img.preserveAspect = true;
|
|
|
|
|
|
b.gameObject.AddComponent<Button>().onClick.AddListener(() => onClick());
|
|
|
|
|
|
UiKit.DropShadow(UiKit.Icon(b, icon, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 4f), 76f), 3f);
|
|
|
|
|
|
UiKit.Title(UiKit.Label(body, label, 30, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0f, 1f), new Vector2(0.5f, 1f), new Vector2(cx, -1420f), new Vector2(200f, 44f)), true);
|
2026-11-05 12:11:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void Refresh()
|
|
|
|
|
|
{
|
|
|
|
|
|
var map = Root.Map;
|
|
|
|
|
|
var cfg = Root.Battle.Config;
|
2026-11-09 08:10:25 +00:00
|
|
|
|
float pct = 0f;
|
2026-11-05 12:11:10 +00:00
|
|
|
|
if (map != null && map.HasData)
|
|
|
|
|
|
{
|
|
|
|
|
|
int ch = Root.CurrentChapter();
|
2026-11-09 08:10:25 +00:00
|
|
|
|
int nodes = map.NodesIn(ch);
|
|
|
|
|
|
int done = WorldProgress.Cleared(ch);
|
|
|
|
|
|
int node = Mathf.Min(done, Mathf.Max(0, nodes - 1));
|
2026-11-05 12:11:10 +00:00
|
|
|
|
int si = map.StageIndexOf(ch, node);
|
|
|
|
|
|
var def = cfg.chapters[ch];
|
2026-11-09 08:10:25 +00:00
|
|
|
|
progress.text = "제" + def.no + "장 " + def.title;
|
|
|
|
|
|
progressNum.text = Mathf.Min(done, nodes) + "/" + nodes;
|
|
|
|
|
|
pct = nodes > 0 ? Mathf.Clamp01(done / (float)nodes) : 0f;
|
|
|
|
|
|
sortieSub.text = si >= 0 ? "챕터 " + def.no + " · " + def.no + "-" + cfg.stages[si].stage : "챕터 " + def.no;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
progress.text = "—";
|
|
|
|
|
|
progressNum.text = "";
|
|
|
|
|
|
sortieSub.text = "챕터 월드맵";
|
2026-11-05 12:11:10 +00:00
|
|
|
|
}
|
2026-11-09 08:10:25 +00:00
|
|
|
|
progressFill.sizeDelta = new Vector2(232f * pct, progressFill.sizeDelta.y);
|
|
|
|
|
|
progressFill.gameObject.SetActive(pct > 0.01f);
|
|
|
|
|
|
int chests = 0;
|
|
|
|
|
|
foreach (var c in S.Db.Chests.chests) chests += TreasureRules.ChestCount(S.Save, c.id);
|
|
|
|
|
|
chestBadge.text = chests.ToString();
|
|
|
|
|
|
chestBadge.transform.parent.gameObject.SetActive(chests > 0);
|
2026-11-05 12:11:10 +00:00
|
|
|
|
|
|
|
|
|
|
UiKit.Clear(field);
|
|
|
|
|
|
var lineup = S.BuildLineup();
|
2026-11-11 14:51:31 +00:00
|
|
|
|
if (scene != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
PlaceLineup(lineup, cfg);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-11-05 12:11:10 +00:00
|
|
|
|
int n = Mathf.Max(1, lineup.Count);
|
|
|
|
|
|
float w = Mathf.Min(200f, 980f / n);
|
|
|
|
|
|
for (int i = 0; i < lineup.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var h = S.Def(lineup[i].id);
|
|
|
|
|
|
float x = (i - (n - 1) / 2f) * w;
|
2026-11-10 04:50:57 +00:00
|
|
|
|
// 크기 표 기준으로 발 맞춰 세움(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);
|
2026-11-05 12:11:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-11-11 14:51:31 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 배경 마당 위 진형(표 slot 줄 · 발 = 그림 px) — 뒷줄(위)부터 그려 앞줄이 앞에 오게 · 자리마다 발밑 그림자(표 shadow 줄) · 뒷줄은 조금 작게(f_scale).
|
|
|
|
|
|
/// PD 2026-11-11 「나란히 일렬로 서 있으면 배경 이미지와 겹칠 수밖에 없음」 → 깃발·무기 거치대·북 사이 빈 돌바닥에 두 줄.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
void PlaceLineup(System.Collections.Generic.List<LineupEntry> lineup, BalanceConfig cfg)
|
|
|
|
|
|
{
|
|
|
|
|
|
var slots = LobbyScene.Slots(lineup.Count);
|
|
|
|
|
|
var order = new System.Collections.Generic.List<int>();
|
|
|
|
|
|
for (int i = 0; i < lineup.Count && i < slots.Count; i++) order.Add(i);
|
|
|
|
|
|
order.Sort((a, b) => slots[a].y.CompareTo(slots[b].y));
|
|
|
|
|
|
// 그림자(표 shadow 줄 여러 개 = 비스듬한 그늘 + 발 닿는 곳 접지 그림자 · QA r2)를 먼저 모두(한 아틀라스 = 한 묶음) → 영웅(뒷줄부터)
|
|
|
|
|
|
// 그림자·영웅을 번갈아 두면 묶음이 끊김(실측 로비 batches 18 → 21)
|
|
|
|
|
|
foreach (var sh in LobbyScene.All("shadow"))
|
|
|
|
|
|
{
|
|
|
|
|
|
var glow = UISkin.Icon(sh.sprite);
|
|
|
|
|
|
foreach (int i in order)
|
|
|
|
|
|
{
|
|
|
|
|
|
var r = slots[i];
|
|
|
|
|
|
float k = r.scale > 0.01f ? r.scale : 1f;
|
|
|
|
|
|
var at = new Vector2(r.x / Mathf.Max(1f, artW), 1f - r.y / Mathf.Max(1f, artH));
|
|
|
|
|
|
var s = UiKit.Rect("Shadow" + i, field, at, new Vector2(0.5f, 0.5f), new Vector2(sh.x * k, -sh.y * k), new Vector2(sh.w * k, sh.h * k));
|
|
|
|
|
|
var simg = s.gameObject.AddComponent<Image>();
|
|
|
|
|
|
simg.sprite = glow != null ? glow : UiKit.Circle;
|
|
|
|
|
|
simg.color = sh.color;
|
|
|
|
|
|
simg.raycastTarget = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (int i in order)
|
|
|
|
|
|
{
|
|
|
|
|
|
var r = slots[i];
|
|
|
|
|
|
var h = S.Def(lineup[i].id);
|
|
|
|
|
|
var at = new Vector2(r.x / Mathf.Max(1f, artW), 1f - r.y / Mathf.Max(1f, artH));
|
|
|
|
|
|
float k = r.scale > 0.01f ? r.scale : 1f;
|
|
|
|
|
|
var stand = UiKit.UnitStand("Hero" + i, field, h, at, Vector2.zero, LobbyUnitPx * k, cfg.mountedArtMaxTop);
|
|
|
|
|
|
if (stand == null)
|
|
|
|
|
|
UiKit.Portrait("Hero" + i, field, h, at, new Vector2(0.5f, 0f), Vector2.zero, new Vector2(196f * k, 340f * k), false, 1f, Color.clear);
|
|
|
|
|
|
else if (h != null)
|
|
|
|
|
|
UiFrameAnim.Attach(stand.GetComponent<Image>(), UnitArt.Load(h.art), "front_idle"); // 대기 숨 고르기(2~3 프레임 · 있으면)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-11-05 12:11:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|