영웅 목록 = 미보유 영웅도 그림 카드(빈 카드 대신 · 일러 조금 어둡게 · 「미보유」 칩 · 탭 = 획득 안내)(#1108 · PD 2026-11-10 「왜 유닛 목록에 장수들의 모습이 보이지 않는거지? 이번에 추가된 리소스가 반영되도록」)
- HeroScreens: CollectionCards.Card owned 인자 · 미보유 = 레벨 배지·성장 점 없음 · ★ 높은 순 → 표 순서 · 상세 화면은 보유 전제라 미보유 탭 = 토스트(도감과 같은 안내 InfoText) - 테마 표 ui_theme.csv: card_locked_art · tail_locked(SOT) → TKS/Import UI Theme - 테스트(고친 부분만): Lobby_Tabs_Back_ComingSoon 에 미보유 카드 수·일러 확인 추가 1/1 · UILayerTests 1/1 · error 0 - 캡처 hero_list 1장 · 배치 134(미보유 64장 그림이 각자 다른 텍스처 → 축소 초상 아틀라스 대상) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cd6e84ec1e
commit
9718b99a00
|
|
@ -304,6 +304,8 @@ color,card_empty,,,,,,#303146,빈·미보유 카드(팩 CardFrame01_Empty Bg 원
|
||||||
color,card_empty_pattern,,,,,,#2D2F42,빈·미보유 카드 문양
|
color,card_empty_pattern,,,,,,#2D2F42,빈·미보유 카드 문양
|
||||||
color,card_empty_border,,,,,,#1A1B27,빈·미보유 카드 테두리
|
color,card_empty_border,,,,,,#1A1B27,빈·미보유 카드 테두리
|
||||||
color,card_dim,,,,,,#000000A0,미보유 초상 어둡게
|
color,card_dim,,,,,,#000000A0,미보유 초상 어둡게
|
||||||
|
color,card_locked_art,,,,,,#A9AEC2,영웅 목록 미보유 카드 일러 틴트(모습은 보이게 · 보유보다 조금 어둡게 · PD 2026-11-10)
|
||||||
|
color,tail_locked,,,,,,#4A4C63,영웅 목록 미보유 꼬리 칩
|
||||||
color,tail_red,,,,,,#D8412F,출전 꼬리 칩
|
color,tail_red,,,,,,#D8412F,출전 꼬리 칩
|
||||||
color,grade_white_card_bg,,,,,,#626E8B,영웅 카드 white card_bg
|
color,grade_white_card_bg,,,,,,#626E8B,영웅 카드 white card_bg
|
||||||
color,grade_white_card_grad,,,,,,#90A3BA,영웅 카드 white card_grad
|
color,grade_white_card_grad,,,,,,#90A3BA,영웅 카드 white card_grad
|
||||||
|
|
|
||||||
|
|
|
@ -335,6 +335,10 @@ MonoBehaviour:
|
||||||
color: {r: 0.101960786, g: 0.105882354, b: 0.15294118, a: 1}
|
color: {r: 0.101960786, g: 0.105882354, b: 0.15294118, a: 1}
|
||||||
- id: card_dim
|
- id: card_dim
|
||||||
color: {r: 0, g: 0, b: 0, a: 0.627451}
|
color: {r: 0, g: 0, b: 0, a: 0.627451}
|
||||||
|
- id: card_locked_art
|
||||||
|
color: {r: 0.6627451, g: 0.68235296, b: 0.7607843, a: 1}
|
||||||
|
- id: tail_locked
|
||||||
|
color: {r: 0.2901961, g: 0.29803923, b: 0.3882353, a: 1}
|
||||||
- id: tail_red
|
- id: tail_red
|
||||||
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
|
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
|
||||||
- id: grade_white_card_bg
|
- id: grade_white_card_bg
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,8 @@ namespace TKS.OutGame
|
||||||
/// <summary>말 탄 유닛 전신(발 ~ 머리 끝) 높이 상한(카드 px) — 크기 표 실측이 튀는 그림(예 여포 size_px 449)도 다른 기병과 비슷하게.</summary>
|
/// <summary>말 탄 유닛 전신(발 ~ 머리 끝) 높이 상한(카드 px) — 크기 표 실측이 튀는 그림(예 여포 size_px 449)도 다른 기병과 비슷하게.</summary>
|
||||||
const float MountedMaxBody = 460f;
|
const float MountedMaxBody = 460f;
|
||||||
|
|
||||||
public static Image Card(Transform content, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap)
|
/// <param name="owned">false = 미보유(PD 2026-11-10 「유닛 목록에 장수들의 모습이 보이도록」) — 일러를 테마 card_locked_art 로 조금 어둡게 · 「미보유」 꼬리 칩 · 레벨 배지·성장 점 없음</param>
|
||||||
|
public static Image Card(Transform content, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap, bool owned = true)
|
||||||
{
|
{
|
||||||
var cell = UiKit.Cell(content, "Card_" + h.id, Color.clear, onTap);
|
var cell = UiKit.Cell(content, "Card_" + h.id, Color.clear, onTap);
|
||||||
cell.sprite = null; // 바닥 = 클릭 판(투명) · 그림은 층으로
|
cell.sprite = null; // 바닥 = 클릭 판(투명) · 그림은 층으로
|
||||||
|
|
@ -138,6 +139,7 @@ namespace TKS.OutGame
|
||||||
}
|
}
|
||||||
art.sprite = sp;
|
art.sprite = sp;
|
||||||
art.raycastTarget = false;
|
art.raycastTarget = false;
|
||||||
|
if (!owned) art.color = UiKit.Col("card_locked_art", UiKit.InkSoft);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -155,10 +157,10 @@ namespace TKS.OutGame
|
||||||
int star = hs != null ? hs.star : h.rarity;
|
int star = hs != null ? hs.star : h.rarity;
|
||||||
for (int i = star - 1; i >= 0; i--)
|
for (int i = star - 1; i >= 0; i--)
|
||||||
UiKit.Icon(t, "star_on", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-15.5f - i * 28f, 64.8f), 44f);
|
UiKit.Icon(t, "star_on", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-15.5f - i * 28f, 64.8f), 44f);
|
||||||
// 레벨 배지(왼쪽 아래 · 최고 레벨 = 노랑)
|
// 레벨 배지(왼쪽 아래 · 최고 레벨 = 노랑 · 미보유 = 없음)
|
||||||
int lvn = lv != null ? lv.lv : 1;
|
int lvn = lv != null ? lv.lv : 1;
|
||||||
bool maxLv = s.Db.Levels != null && lvn >= s.Db.Levels.MaxLevel;
|
bool maxLv = s.Db.Levels != null && lvn >= s.Db.Levels.MaxLevel;
|
||||||
var badge = UiKit.Icon(t, maxLv ? "lv_badge_max" : "lv_badge_s", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(33.9f, 36.7f), 79f);
|
var badge = owned ? UiKit.Icon(t, maxLv ? "lv_badge_max" : "lv_badge_s", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(33.9f, 36.7f), 79f) : null;
|
||||||
if (badge != null)
|
if (badge != null)
|
||||||
{
|
{
|
||||||
var bt = UiKit.Title(UiKit.Label(badge.transform, lvn.ToString(), 30, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(1f, -3f), new Vector2(74f, 50f)), true);
|
var bt = UiKit.Title(UiKit.Label(badge.transform, lvn.ToString(), 30, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(1f, -3f), new Vector2(74f, 50f)), true);
|
||||||
|
|
@ -171,23 +173,31 @@ namespace TKS.OutGame
|
||||||
name.resizeTextForBestFit = true;
|
name.resizeTextForBestFit = true;
|
||||||
name.resizeTextMinSize = 26;
|
name.resizeTextMinSize = 26;
|
||||||
name.resizeTextMaxSize = 45;
|
name.resizeTextMaxSize = 45;
|
||||||
if (deployed)
|
if (deployed || !owned)
|
||||||
{
|
{
|
||||||
var tail = UiKit.Skin(UiKit.Rect("Deployed", t, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(10f, -96f), new Vector2(84f, 38f)), "tail", UiKit.Col("tail_red", UiKit.Red));
|
var tail = UiKit.Skin(UiKit.Rect(owned ? "Deployed" : "Locked", t, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(10f, -96f), new Vector2(owned ? 84f : 104f, 38f)), "tail",
|
||||||
UiKit.Title(UiKit.FillLabel(tail.transform, "출전", 24, UiKit.White), true);
|
owned ? UiKit.Col("tail_red", UiKit.Red) : UiKit.Col("tail_locked", UiKit.Dark));
|
||||||
|
UiKit.Title(UiKit.FillLabel(tail.transform, owned ? "출전" : "미보유", 24, UiKit.White), true);
|
||||||
}
|
}
|
||||||
// 병과 = 단색 심볼만(배경 틀 없음 · PD 2026-11-09 「심볼은 배경 프레임 없이 단색」)
|
// 병과 = 단색 심볼만(배경 틀 없음 · PD 2026-11-09 「심볼은 배경 프레임 없이 단색」)
|
||||||
var role = UiKit.Rect("Role", t, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-42f, -42f), new Vector2(60f, 60f));
|
var role = UiKit.Rect("Role", t, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-42f, -42f), new Vector2(60f, 60f));
|
||||||
var sym = UiKit.Icon(role, UiKit.TypeSymbolId(h.type), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 60f);
|
var sym = UiKit.Icon(role, UiKit.TypeSymbolId(h.type), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 60f);
|
||||||
if (sym == null) UiKit.Title(UiKit.FillLabel(role, UnitTypeInfo.Mark(h.type), 28, UiKit.White), true);
|
if (sym == null) UiKit.Title(UiKit.FillLabel(role, UnitTypeInfo.Mark(h.type), 28, UiKit.White), true);
|
||||||
if (s.CanGrowAny(h.id)) UiKit.Icon(t, "new_dot", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(64f, 70f), 30f); // 성장 가능 = 레벨 배지 오른쪽 위
|
if (owned && s.CanGrowAny(h.id)) UiKit.Icon(t, "new_dot", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(64f, 70f), 30f); // 성장 가능 = 레벨 배지 오른쪽 위
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>원래 크기(320×431) 카드를 holder 가운데에 배율 k 로 넣는다(편성 자리 · 편성 보유 목록).</summary>
|
/// <summary>원래 크기(320×431) 카드를 holder 가운데에 배율 k 로 넣는다(편성 자리 · 편성 보유 목록 · 영웅 목록 미보유 = owned false).</summary>
|
||||||
public static Image CardIn(Transform holder, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap, float k)
|
public static Image CardIn(Transform holder, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap, float k, bool owned = true)
|
||||||
{
|
{
|
||||||
return Fit(Card(holder, s, h, deployed, onTap), k);
|
return Fit(Card(holder, s, h, deployed, onTap, owned), k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>미보유·도감 안내 한 줄(이름(한자) · 병종 · ★ · 보유/미보유).</summary>
|
||||||
|
public static string InfoText(HeroDb.Hero h, bool own)
|
||||||
|
{
|
||||||
|
return h.nameKo + (string.IsNullOrEmpty(h.hanja) ? "" : "(" + h.hanja + ")") + " · " + UnitTypeInfo.Korean(h.type) + " · ★" + h.rarity +
|
||||||
|
(h.isSoldier ? " · 병사 유닛" : "") + (own ? " · 보유" : " · 미보유 — 거래 > 뽑기");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Image EmptyIn(Transform holder, float k)
|
public static Image EmptyIn(Transform holder, float k)
|
||||||
|
|
@ -227,7 +237,7 @@ namespace TKS.OutGame
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 영웅 탭 = 영웅 목록(PD 2026-11-09 레퍼런스 = 팩 Collection_List) — 헤더(제목 · 장수/병사 고르기 ▼ · 사선 칸 「보유 / 전체」) ·
|
/// 영웅 탭 = 영웅 목록(PD 2026-11-09 레퍼런스 = 팩 Collection_List) — 헤더(제목 · 장수/병사 고르기 ▼ · 사선 칸 「보유 / 전체」) ·
|
||||||
/// 3열 세로 카드(보유 = 레벨 높은 순) + 미보유 자리 = 빈 카드 → 탭 = 영웅 상세.
|
/// 세로 카드(보유 = 레벨 높은 순 → 탭 = 영웅 상세) + 미보유 = 그 영웅 카드(조금 어둡게 · 「미보유」 · 탭 = 획득 안내 · PD 2026-11-10).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HeroListScreen : OutGameScreen
|
public class HeroListScreen : OutGameScreen
|
||||||
{
|
{
|
||||||
|
|
@ -237,6 +247,7 @@ namespace TKS.OutGame
|
||||||
Text filterText;
|
Text filterText;
|
||||||
Text countText;
|
Text countText;
|
||||||
int ownedCards;
|
int ownedCards;
|
||||||
|
int lockedCards;
|
||||||
UiAutoGrid autoGrid;
|
UiAutoGrid autoGrid;
|
||||||
|
|
||||||
public bool ShowingSoldiers
|
public bool ShowingSoldiers
|
||||||
|
|
@ -315,19 +326,36 @@ namespace TKS.OutGame
|
||||||
CollectionCards.CardIn(Slot(), S, h, S.InActivePreset(id), () => Root.ShowHero(id), autoGrid.Scale);
|
CollectionCards.CardIn(Slot(), S, h, S.InActivePreset(id), () => Root.ShowHero(id), autoGrid.Scale);
|
||||||
ownedCards++;
|
ownedCards++;
|
||||||
}
|
}
|
||||||
int total = 0;
|
// 미보유 = 빈 카드 대신 그 영웅 카드(일러 조금 어둡게 · 「미보유」 · 탭 = 획득 안내) · ★ 높은 순 → 표 순서
|
||||||
foreach (var h in S.Db.Heroes.heroes)
|
// PD 2026-11-10 「왜 유닛 목록에 장수들의 모습이 보이지 않는거지? 이번에 추가된 리소스가 반영되도록」
|
||||||
if (h.isSoldier == soldiers) total++;
|
// (상세 화면은 레벨·세이브가 있는 보유 영웅 전제라 미보유는 열지 않는다)
|
||||||
for (int i = owned.Count; i < total; i++) CollectionCards.EmptyIn(Slot(), autoGrid.Scale);
|
var locked = new List<HeroDb.Hero>();
|
||||||
countText.text = owned.Count + "/" + total;
|
var all = S.Db.Heroes.heroes;
|
||||||
|
foreach (var h in all)
|
||||||
|
if (h.isSoldier == soldiers && !S.Save.Owns(h.id)) locked.Add(h);
|
||||||
|
locked.Sort((a, b) => a.rarity != b.rarity ? b.rarity.CompareTo(a.rarity) : all.IndexOf(a).CompareTo(all.IndexOf(b)));
|
||||||
|
lockedCards = 0;
|
||||||
|
foreach (var h in locked)
|
||||||
|
{
|
||||||
|
var hero = h;
|
||||||
|
CollectionCards.CardIn(Slot(), S, h, false, () => Root.Toast(CollectionCards.InfoText(hero, false)), autoGrid.Scale, false);
|
||||||
|
lockedCards++;
|
||||||
|
}
|
||||||
|
countText.text = owned.Count + "/" + (owned.Count + locked.Count);
|
||||||
autoGrid.Apply();
|
autoGrid.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>보유 카드 수(빈 카드 제외).</summary>
|
/// <summary>보유 카드 수(미보유 제외).</summary>
|
||||||
public int CardCount
|
public int CardCount
|
||||||
{
|
{
|
||||||
get { return ownedCards; }
|
get { return ownedCards; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>미보유 영웅 카드 수(그림 카드 · PD 2026-11-10).</summary>
|
||||||
|
public int LockedCount
|
||||||
|
{
|
||||||
|
get { return lockedCards; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -968,8 +996,7 @@ namespace TKS.OutGame
|
||||||
{
|
{
|
||||||
bool own = s.Save.Owns(h.id);
|
bool own = s.Save.Owns(h.id);
|
||||||
var hero = h;
|
var hero = h;
|
||||||
HeroCards.Card(grid, s, h, false, own, () => root.Toast(hero.nameKo + (string.IsNullOrEmpty(hero.hanja) ? "" : "(" + hero.hanja + ")") + " · " +
|
HeroCards.Card(grid, s, h, false, own, () => root.Toast(CollectionCards.InfoText(hero, own)));
|
||||||
UnitTypeInfo.Korean(hero.type) + " · ★" + hero.rarity + (hero.isSoldier ? " · 병사 유닛" : "") + (own ? " · 보유" : " · 미보유 — 거래 > 뽑기")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,17 @@ namespace TKS.Tests
|
||||||
Assert.AreEqual(og.HeroList, og.Current);
|
Assert.AreEqual(og.HeroList, og.Current);
|
||||||
yield return null;
|
yield return null;
|
||||||
Assert.AreEqual(og.Session.Owned(false).Count, og.HeroList.CardCount, "보유 장수 카드");
|
Assert.AreEqual(og.Session.Owned(false).Count, og.HeroList.CardCount, "보유 장수 카드");
|
||||||
|
// 미보유 장수 = 빈 카드가 아니라 그 영웅 그림 카드(PD 2026-11-10 「유닛 목록에 장수들의 모습이 보이도록」)
|
||||||
|
int heroTotal = 0;
|
||||||
|
foreach (var h in og.Session.Db.Heroes.heroes)
|
||||||
|
if (!h.isSoldier) heroTotal++;
|
||||||
|
Assert.AreEqual(heroTotal - og.Session.Owned(false).Count, og.HeroList.LockedCount, "미보유 장수 카드 = 그림 카드");
|
||||||
|
Assert.Greater(og.HeroList.LockedCount, 0, "신규 유저 = 미보유 있음");
|
||||||
|
var lockedTail = og.HeroList.Rt.GetComponentsInChildren<Transform>(true);
|
||||||
|
int lockedWithArt = 0;
|
||||||
|
foreach (var tr in lockedTail)
|
||||||
|
if (tr.name == "Locked" && tr.parent.Find("Bg/Character") != null) lockedWithArt++;
|
||||||
|
Assert.AreEqual(og.HeroList.LockedCount, lockedWithArt, "미보유 카드마다 일러(Character)");
|
||||||
og.HeroList.SetFilter(true);
|
og.HeroList.SetFilter(true);
|
||||||
yield return null;
|
yield return null;
|
||||||
Assert.AreEqual(og.Session.Owned(true).Count, og.HeroList.CardCount, "병사 필터");
|
Assert.AreEqual(og.Session.Owned(true).Count, og.HeroList.CardCount, "병사 필터");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue