UI 리뉴얼 A-2~A-4 화면 적용(PD #1100) — 팩 부품 + UI 테마(id 치환) · 화면 10종 · 인박스 ui kind

- 테마: SOT Assets/Data/UI/ui_theme.csv(색 57 · 스킨 32 · 아이콘 63 · 제목 폰트) → 메뉴 TKS/Import UI Theme → Resources/UI/UITheme.asset
  (Runtime/UI/UITheme · UISkin · Editor/UIThemeImporter · ProjectSetup 에서도 임포트) · 화면 코드는 id 로만 묻는다(스프라이트 경로 0)
- 신규 제작(B) 치환: Assets/UI/Samguk/**/<id>.png → 임포트 시 같은 id 스킨·아이콘 자동 치환(팩 보존 · 새 id = 아이콘 추가) ·
  Tools/apply_delivery.py ui kind(md5 → Samguk/<group> · gim_* → Resources/Gimmicks/Art · ui_delivered.csv)
- 공통: UiKit(카드 = BorderFrame_Round20 틴트 · 버튼 = Button01_s + 광택 · 원 · 진행 바 = Slider_Basic01 · 초상 틀 · 등급 칸 ItemFrame02 · 별 줄 · 병종 배지)
  · 재화 바(ResourceBar + cur_* 아이콘) · 하단 탭(선택 판 + PictoIcon) · 제목 띠 · 팝업(Popup Topber + 닫기 X) · 토스트
- 화면: 로비(키아트 04 배경 위쪽 ×1.22 · 출정·편성·연무장 아이콘) · 영웅(등급 칸 + 장비 아이콘 · 체크/알림 점 · 별 아이콘) · 편성 · 거래(배너 · 상자 아이콘) ·
  보물(상자 아이콘 · 포인트 배지) · 내정(보상 줄 · 도면·골드 아이콘) · 연무장 · 결과(리본 · 별) · 월드맵 스테이지 팝업만 팩(거점 kit 유지)
- 전투: HUD 칩 · 턴 띠 · 원형 스킬 버튼 + 쿨 라디얼 · 유닛 체력 바 · 기믹 내구 핍 = 팩 슬라이더(월드 9-slice 사본 · 가운데 2 px) · 조준선 색 유지
- 3:4(1536×2048): 본문 비율 축소(UiFitHeight · 1080×1920 = 그대로)
- 검증: 플레이모드 116/116(기존 115 + UIThemeTests 1) · 컴파일 0 · error 0 · 캡처 = DataImporter.CaptureUI(UICaptureDriver · 15장)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-11-06 23:20:57 +09:00
parent 43856e70cb
commit d73c606e46
40 changed files with 2489 additions and 136 deletions

8
Assets/Data/UI.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bf00b201586bf2a48beb4f0b0ffe0d9c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

154
Assets/Data/UI/ui_theme.csv Normal file
View File

@ -0,0 +1,154 @@
kind,id,path,overlay,ppu,inset,alpha,color,note
color,bg,,,,,,#EFE5CF,화면 바탕(양피지)
color,paper,,,,,,#FFF9EC,패널·카드 바탕
color,header,,,,,,#2E3349,제목 띠(팩 진남)
color,header_text,,,,,,#FFFFFF,제목 띠 글자
color,header_sub,,,,,,#C9C3D6,제목 띠 보조 글자
color,ink,,,,,,#2A2230,글자(먹)
color,ink_soft,,,,,,#6F6470,보조 글자
color,dark,,,,,,#3A4060,어두운 버튼(팩 진남)
color,red,,,,,,#D8412F,붉은 옻칠(주 버튼·강조)
color,line,,,,,,#1E2535,외곽선
color,slot,,,,,,#DCD0B9,빈 칸
color,coin,,,,,,#F3B72E,금
color,jade,,,,,,#23B394,보옥(옥)
color,grain,,,,,,#D9963F,군량
color,ok,,,,,,#47B53F,완료
color,star,,,,,,#FFC21A,별
color,disabled,,,,,,#9E98A3,비활성
color,purple,,,,,,#9A57D8,보라
color,white,,,,,,#FFFFFF,흰색
color,bar,,,,,,#1E2535,상단 재화 바·하단 탭 바
color,pill,,,,,,#353B57,상단 재화 칩 바탕(흰 칩 틴트)
color,bar_text,,,,,,#FFFFFF,바 글자
color,bar_text_off,,,,,,#8D93A8,바 글자(비활성)
color,tab_off,,,,,,#3A4060,하단 탭 아이콘 바탕(꺼짐)
color,stroke,,,,,,#1E2535,흰 글자 외곽선
color,dim,,,,,,#000000A0,팝업 뒤 어둡게
color,lobby_tint,,,,,,#9C95A8,로비 배경 어둡게(곱)
color,bar_track,,,,,,#FFFFFF,진행 바 바탕(스킨 원색 · 곱)
color,grade_white,,,,,,#D9D6CF,등급 흰
color,grade_green,,,,,,#47B53F,등급 녹
color,grade_blue,,,,,,#3C7BE0,등급 청
color,grade_purple,,,,,,#9A57D8,등급 자
color,grade_orange,,,,,,#F08A24,등급 주
color,grade_gold,,,,,,#F3C12E,등급 금
color,type_infantry,,,,,,#C8553A,병종 보병
color,type_spear,,,,,,#3E9A4A,병종 창병
color,type_cavalry,,,,,,#3C67C8,병종 기병
color,type_archer,,,,,,#D99A2B,병종 궁병
color,type_strategist,,,,,,#9A57D8,병종 책사
color,faction_shu,,,,,,#D8412F,세력 촉(적)
color,faction_wei,,,,,,#3C7BE0,세력 위(청)
color,faction_wu,,,,,,#2FA84F,세력 오(녹)
color,faction_other,,,,,,#8C8C96,세력 군웅(회)
color,hp_bg,,,,,,#FFFFFF,유닛 체력 바 바탕(스킨 원색)
color,hp_hi,,,,,,#52D148,체력 50 % 초과
color,hp_mid,,,,,,#FFC52B,체력 25~50 %
color,hp_low,,,,,,#F2453A,체력 25 % 이하
color,pip_on,,,,,,#FFC21A,기믹 내구 핍(남음)
color,pip_off,,,,,,#2B2F40CC,기믹 내구 핍(깎임)
color,hud_panel,,,,,,#1E2535D9,전투 HUD 판
color,hud_turn_player,,,,,,#3C7BE0,턴 띠(아군)
color,hud_turn_enemy,,,,,,#D8412F,턴 띠(적군)
color,skill_ready,,,,,,#D8412F,스킬 사용 가능
color,skill_reserved,,,,,,#F3B72E,스킬 예약
color,skill_off,,,,,,#5A5E70,스킬 불가
color,skill_passive,,,,,,#3A4060,패시브만
color,skill_cool,,,,,,#000000A8,스킬 쿨 라디얼 덮개
skin,card,Frame/BorderFrame_Round20_White_Bg.png,,1,,,,패널·카드(흰 바탕 + 굵은 외곽선 · 호출 색 틴트)
skin,soft,Frame/BasicFrame_Round20.png,,1,,,,외곽선 없는 둥근 칸(반투명 판)
skin,button,Button/Button01_s_White_Bg.png,Button/Button01_s_White_Light1.png,1,5;5;5;18,0.35,,버튼(흰 바탕 틴트 + 윗면 광택)
skin,dot,Frame/BorderFrame_Circle60.png,,1,,,,원(외곽선)
skin,bar_bg,Slider/Slider_Basic01_Bg_Single.png,,1,,,,진행 바 바탕
skin,bar_fill,Slider/Slider_Basic01_FillMask.png,,1,,,,진행 바 채움(틴트)
skin,pill,UI_Etc/ResourceBar_Single_Bg.png,,1,,,,상단 재화 칩
skin,plus,UI_Etc/ResourceBar_Single_Btn_Yellow1.png,UI_Etc/ResourceBar_Single_Btn_Yellow2.png,1,14;14;14;16,1,,재화 + 버튼
skin,tabbar,Button/Menu_BottomBtn_Bg.png,,1,,,,하단 탭 바
skin,tab_focus,Frame/BorderFrame_Round20_White_Bg.png,Frame/BorderFrame_Round20_White_Light.png,1,6;4;6;30,0.45,,하단 탭 선택(틴트)
skin,popup,Popup/Popup02~09_Topber_White_Bg.png,,1,,,,팝업 틀
skin,popup_head,Popup/Popup02~09_Topber_White_BgTop.png,Popup/Popup02~09_Topber_White_BgTopLight.png,1,0;0;0;60,0.25,,팝업 제목 띠(틴트)
skin,close,Button/Button_Round06_Red.png,IconMisc/Icon_Close01.png,1,22;22;22;26,1,,팝업 닫기 X
skin,ribbon,Label/Title_Ribbon01_Red.png,,1,,,,결과 제목 리본
skin,ribbon_gray,Label/Title_Ribbon01_Gray.png,,1,,,,결과 제목 리본(패배)
skin,toast,Frame/BorderFrame_Round20_White_Bg.png,,1,,,,알림(틴트)
skin,portrait_mask,Frame/BasicFrame_Round20.png,,1,,,,초상 마스크
skin,circle_frame,Frame/BorderFrame_Circle78.png,,1,,,,원형 자리(편성)
skin,skill_btn,Button/Button_Circle122.png,,1,,,,전투 스킬 버튼(원형)
skin,skill_cool,Frame/BasicFrame_Circle160.png,,1,,,,스킬 쿨 라디얼
skin,hp_bg,Slider/Slider_Basic02_Bg.png,,1,,,,유닛 체력 바 바탕(월드)
skin,hp_fill,Slider/Slider_Basic01_FillMask.png,,1,,,,유닛 체력 바 채움(월드)
skin,pip,Frame/BorderFrame_Round20_White_Bg.png,,1,,,,기믹 내구 핍(월드)
skin,hud_pill,UI_Etc/ResourceBar_Single_Bg.png,,1,,,,전투 HUD 칩
skin,banner,Frame/BannerFrame04_Single.png,,1,,,,뽑기 배너 틀
skin,flag,Frame/BorderFrame_Flag_White_Bg.png,,1,,,,세력 깃발(틴트)
skin,frame_white,Frame/ItemFrame02_Single_OffWhite.png,,1,,,,등급 칸 흰(B frame_* 오면 치환)
skin,frame_green,Frame/ItemFrame02_Single_Green.png,,1,,,,등급 칸 녹
skin,frame_blue,Frame/ItemFrame02_Single_Blue.png,,1,,,,등급 칸 청
skin,frame_purple,Frame/ItemFrame02_Single_Purple.png,,1,,,,등급 칸 자
skin,frame_orange,Frame/ItemFrame02_Single_Red.png,,1,,,,등급 칸 주(팩에 주황 없음 → 적색)
skin,frame_gold,Frame/ItemFrame02_Single_Yellow.png,,1,,,,등급 칸 금
icon,cur_gold,Icon_ItemIcons/128/ItemIcon_Money_Coin.Png,,,,,,골드(임시 · B cur_gold)
icon,cur_gem,Icon_ItemIcons/128/ItemIcon_Gem_Circle.Png,,,,,,보옥(임시)
icon,cur_stamina,Icon_ItemIcons/128/ItemIcon_Bag_Brown.Png,,,,,,군량(임시)
icon,cur_shard,Icon_ItemIcons/128/ItemIcon_Star_Purple.Png,,,,,,영웅 조각(임시)
icon,cur_iron,Icon_ItemIcons/128/ItemIcon_Gear.Png,,,,,,운철(임시)
icon,cur_blueprint,Icon_ItemIcons/128/ItemIcon_MemoPad.Png,,,,,,도면(임시)
icon,cur_ticket,Icon_ItemIcons/128/ItemIcon_Ticket_Gold.Png,,,,,,입장권(임시)
icon,cur_skillbook,Icon_ItemIcons/128/ItemIcon_Book_1_Wine.Png,,,,,,스킬서(임시)
icon,cur_exp,Icon_ItemIcons/128/ItemIcon_Star_Blue.Png,,,,,,경험(임시)
icon,cur_gear,Icon_ItemIcons/128/ItemIcon_Gear_Sword.Png,,,,,,장비(임시)
icon,tab_hero,Icon_PictoIcons/128/PictoIcon_Helmet_1.Png,,,,,,하단 탭 영웅(임시)
icon,tab_treasure,Icon_PictoIcons/128/PictoIcon_Chest.Png,,,,,,하단 탭 보물(임시)
icon,tab_domestic,Icon_PictoIcons/128/PictoIcon_Castle.Png,,,,,,하단 탭 내정(임시)
icon,tab_alliance,Icon_PictoIcons/128/PictoIcon_Lock_1.Png,,,,,,하단 탭 동맹(잠금 · 임시)
icon,tab_trade,Icon_PictoIcons/128/PictoIcon_Scale.Png,,,,,,하단 탭 거래(임시)
icon,btn_sortie,Icon_PictoIcons/128/PictoIcon_Battle.Png,,,,,,로비 출정(임시)
icon,btn_formation,Icon_PictoIcons/128/PictoIcon_Flag.Png,,,,,,로비 편성(임시)
icon,btn_arena,Icon_PictoIcons/128/PictoIcon_Target.Png,,,,,,로비 연무장(임시)
icon,cls_infantry,Icon_PictoIcons/128/PictoIcon_Sword_1.Png,,,,,,병종 보병(임시)
icon,cls_spear,Icon_PictoIcons/128/PictoIcon_Pick.Png,,,,,,병종 창병(임시)
icon,cls_cavalry,Icon_PictoIcons/128/PictoIcon_Paw.Png,,,,,,병종 기병(임시)
icon,cls_archer,Icon_PictoIcons/128/PictoIcon_Bow.Png,,,,,,병종 궁병(임시)
icon,cls_strategist,Icon_PictoIcons/128/PictoIcon_Pen_Feather.Png,,,,,,병종 책사(임시)
icon,chest_bronze,Icon_ItemIcons/128/ItemIcon_Chest_Wood.Png,,,,,,청동 상자(임시)
icon,chest_silver,Icon_ItemIcons/128/ItemIcon_Chest_Skull.png,,,,,,은 상자(임시)
icon,chest_gold,Icon_ItemIcons/128/ItemIcon_Chest_Gold.Png,,,,,,금 상자(임시)
icon,chest_general,Icon_ItemIcons/128/ItemIcon_Chest_Premium.Png,,,,,,명장 상자(임시)
icon,chest_gem,Icon_ItemIcons/128/ItemIcon_Chest_Gem.Png,,,,,,보옥 상자(임시)
icon,chest_point,Icon_ItemIcons/128/ItemIcon_Badge_Gold.png,,,,,,상자 포인트(임시)
icon,gear_sword,Icon_ItemIcons/128/ItemIcon_Gear_Sword.Png,,,,,,청동 검(임시)
icon,gear_spear,Icon_ItemIcons/128/ItemIcon_Battle.Png,,,,,,철 창(임시)
icon,gear_bow,Icon_PictoIcons/128/PictoIcon_Bow.Png,,,,,#8A5A2B,각궁(임시 · 실루엣 틴트)
icon,gear_armor,Icon_ItemIcons/128/ItemIcon_Gear_Armor_Top.Png,,,,,,가죽 갑주(임시)
icon,gear_helmet,IconMisc/Icon_Helmet.png,,,,,,철 투구(임시)
icon,gear_horse,Icon_ItemIcons/128/ItemIcon_Gear_Boots.Png,,,,,,마구(임시)
icon,gear_banner,Icon_PictoIcons/128/PictoIcon_Flag.Png,,,,,#D8412F,군령기(임시 · 실루엣 틴트)
icon,gear_book,Icon_ItemIcons/128/ItemIcon_Book_1_Purple.Png,,,,,,병서(임시)
icon,gear_potion,Icon_ItemIcons/128/ItemIcon_Pot.Png,,,,,,약낭(임시)
icon,gear_shield,IconMisc/Icon_Shield.png,,,,,,방패(임시)
icon,gear_charm,Icon_ItemIcons/128/ItemIcon_Clover_Gold.Png,,,,,,부적(임시)
icon,gear_flag,Icon_ItemIcons/128/ItemIcon_Medal_Gold_1.png,,,,,,전고(임시)
icon,flag_shu,Frame/BorderFrame_Flag_Red.png,,,,,,세력 촉(임시)
icon,flag_wei,Frame/BorderFrame_Flag_Blue.png,,,,,,세력 위(임시)
icon,flag_wu,Frame/BorderFrame_Flag_Green.png,,,,,,세력 오(임시)
icon,flag_other,Frame/BorderFrame_Flag_White_Bg.png,,,,,#8C8C96,세력 군웅(임시)
icon,rank_1,Icon_GradeBadge/128/Icon_GradeBadge_Beginner.Png,,,,,,훈련 랭크 1(임시)
icon,rank_2,Icon_GradeBadge/128/Icon_GradeBadge_Iron.Png,,,,,,훈련 랭크 2(임시)
icon,rank_3,Icon_GradeBadge/128/Icon_GradeBadge_Bronze.Png,,,,,,훈련 랭크 3(임시)
icon,rank_4,Icon_GradeBadge/128/Icon_GradeBadge_Silver.Png,,,,,,훈련 랭크 4(임시)
icon,rank_5,Icon_GradeBadge/128/Icon_GradeBadge_Gold.Png,,,,,,훈련 랭크 5(임시)
icon,rank_6,Icon_GradeBadge/128/Icon_GradeBadge_Platinum.Png,,,,,,훈련 랭크 6(임시)
icon,rank_7,Icon_GradeBadge/128/Icon_GradeBadge_Diamond.Png,,,,,,훈련 랭크 7(임시)
icon,star_on,IconMisc/GradeIcon_Star_s_Yellow.png,,,,,,별(켜짐)
icon,star_off,IconMisc/Icon_Star_Off.png,,,,,,별(꺼짐)
icon,lock,IconMisc/Icon_Lock03.png,,,,,,잠금
icon,check,IconMisc/Icon_Check03_s.png,,,,,,체크
icon,arrow_prev,IconMisc/Icon_Arrow_Prev1.png,,,,,,이전
icon,arrow_next,IconMisc/Icon_Arrow_Next1.png,,,,,,다음
icon,arrow_back,IconMisc/Icon_Arrow_Back.png,,,,,,뒤로
icon,new_dot,UI_Etc/Alert_Dot_Bg.png,,,,,#F2453A,새 알림 점
icon,tut_hand,Icon_TutorialHand/128/tutorial_hand_1.png,,,,,,튜토리얼 손가락
icon,shop_pack,Icon_ShopItem/ShopItem_l_GemPack_3.png,,,,,,상점 패키지(준비 중 칸)
icon,bg_lobby,Assets/UI/Lobby/keyart_04.png,,,,,,로비 배경(키아트 04 · B bg_lobby 오면 치환)
font,title,Assets/UI/GUIPro/ResourcesData/Fonts/Sen-ExtraBold.ttf,,,,,,제목·숫자(라틴 Sen · 한글 = 폴백 Malgun Gothic · PD 결정 전)
1 kind id path overlay ppu inset alpha color note
2 color bg #EFE5CF 화면 바탕(양피지)
3 color paper #FFF9EC 패널·카드 바탕
4 color header #2E3349 제목 띠(팩 진남)
5 color header_text #FFFFFF 제목 띠 글자
6 color header_sub #C9C3D6 제목 띠 보조 글자
7 color ink #2A2230 글자(먹)
8 color ink_soft #6F6470 보조 글자
9 color dark #3A4060 어두운 버튼(팩 진남)
10 color red #D8412F 붉은 옻칠(주 버튼·강조)
11 color line #1E2535 외곽선
12 color slot #DCD0B9 빈 칸
13 color coin #F3B72E 금
14 color jade #23B394 보옥(옥)
15 color grain #D9963F 군량
16 color ok #47B53F 완료
17 color star #FFC21A 별
18 color disabled #9E98A3 비활성
19 color purple #9A57D8 보라
20 color white #FFFFFF 흰색
21 color bar #1E2535 상단 재화 바·하단 탭 바
22 color pill #353B57 상단 재화 칩 바탕(흰 칩 틴트)
23 color bar_text #FFFFFF 바 글자
24 color bar_text_off #8D93A8 바 글자(비활성)
25 color tab_off #3A4060 하단 탭 아이콘 바탕(꺼짐)
26 color stroke #1E2535 흰 글자 외곽선
27 color dim #000000A0 팝업 뒤 어둡게
28 color lobby_tint #9C95A8 로비 배경 어둡게(곱)
29 color bar_track #FFFFFF 진행 바 바탕(스킨 원색 · 곱)
30 color grade_white #D9D6CF 등급 흰
31 color grade_green #47B53F 등급 녹
32 color grade_blue #3C7BE0 등급 청
33 color grade_purple #9A57D8 등급 자
34 color grade_orange #F08A24 등급 주
35 color grade_gold #F3C12E 등급 금
36 color type_infantry #C8553A 병종 보병
37 color type_spear #3E9A4A 병종 창병
38 color type_cavalry #3C67C8 병종 기병
39 color type_archer #D99A2B 병종 궁병
40 color type_strategist #9A57D8 병종 책사
41 color faction_shu #D8412F 세력 촉(적)
42 color faction_wei #3C7BE0 세력 위(청)
43 color faction_wu #2FA84F 세력 오(녹)
44 color faction_other #8C8C96 세력 군웅(회)
45 color hp_bg #FFFFFF 유닛 체력 바 바탕(스킨 원색)
46 color hp_hi #52D148 체력 50 % 초과
47 color hp_mid #FFC52B 체력 25~50 %
48 color hp_low #F2453A 체력 25 % 이하
49 color pip_on #FFC21A 기믹 내구 핍(남음)
50 color pip_off #2B2F40CC 기믹 내구 핍(깎임)
51 color hud_panel #1E2535D9 전투 HUD 판
52 color hud_turn_player #3C7BE0 턴 띠(아군)
53 color hud_turn_enemy #D8412F 턴 띠(적군)
54 color skill_ready #D8412F 스킬 사용 가능
55 color skill_reserved #F3B72E 스킬 예약
56 color skill_off #5A5E70 스킬 불가
57 color skill_passive #3A4060 패시브만
58 color skill_cool #000000A8 스킬 쿨 라디얼 덮개
59 skin card Frame/BorderFrame_Round20_White_Bg.png 1 패널·카드(흰 바탕 + 굵은 외곽선 · 호출 색 틴트)
60 skin soft Frame/BasicFrame_Round20.png 1 외곽선 없는 둥근 칸(반투명 판)
61 skin button Button/Button01_s_White_Bg.png Button/Button01_s_White_Light1.png 1 5;5;5;18 0.35 버튼(흰 바탕 틴트 + 윗면 광택)
62 skin dot Frame/BorderFrame_Circle60.png 1 원(외곽선)
63 skin bar_bg Slider/Slider_Basic01_Bg_Single.png 1 진행 바 바탕
64 skin bar_fill Slider/Slider_Basic01_FillMask.png 1 진행 바 채움(틴트)
65 skin pill UI_Etc/ResourceBar_Single_Bg.png 1 상단 재화 칩
66 skin plus UI_Etc/ResourceBar_Single_Btn_Yellow1.png UI_Etc/ResourceBar_Single_Btn_Yellow2.png 1 14;14;14;16 1 재화 + 버튼
67 skin tabbar Button/Menu_BottomBtn_Bg.png 1 하단 탭 바
68 skin tab_focus Frame/BorderFrame_Round20_White_Bg.png Frame/BorderFrame_Round20_White_Light.png 1 6;4;6;30 0.45 하단 탭 선택(틴트)
69 skin popup Popup/Popup02~09_Topber_White_Bg.png 1 팝업 틀
70 skin popup_head Popup/Popup02~09_Topber_White_BgTop.png Popup/Popup02~09_Topber_White_BgTopLight.png 1 0;0;0;60 0.25 팝업 제목 띠(틴트)
71 skin close Button/Button_Round06_Red.png IconMisc/Icon_Close01.png 1 22;22;22;26 1 팝업 닫기 X
72 skin ribbon Label/Title_Ribbon01_Red.png 1 결과 제목 리본
73 skin ribbon_gray Label/Title_Ribbon01_Gray.png 1 결과 제목 리본(패배)
74 skin toast Frame/BorderFrame_Round20_White_Bg.png 1 알림(틴트)
75 skin portrait_mask Frame/BasicFrame_Round20.png 1 초상 마스크
76 skin circle_frame Frame/BorderFrame_Circle78.png 1 원형 자리(편성)
77 skin skill_btn Button/Button_Circle122.png 1 전투 스킬 버튼(원형)
78 skin skill_cool Frame/BasicFrame_Circle160.png 1 스킬 쿨 라디얼
79 skin hp_bg Slider/Slider_Basic02_Bg.png 1 유닛 체력 바 바탕(월드)
80 skin hp_fill Slider/Slider_Basic01_FillMask.png 1 유닛 체력 바 채움(월드)
81 skin pip Frame/BorderFrame_Round20_White_Bg.png 1 기믹 내구 핍(월드)
82 skin hud_pill UI_Etc/ResourceBar_Single_Bg.png 1 전투 HUD 칩
83 skin banner Frame/BannerFrame04_Single.png 1 뽑기 배너 틀
84 skin flag Frame/BorderFrame_Flag_White_Bg.png 1 세력 깃발(틴트)
85 skin frame_white Frame/ItemFrame02_Single_OffWhite.png 1 등급 칸 흰(B frame_* 오면 치환)
86 skin frame_green Frame/ItemFrame02_Single_Green.png 1 등급 칸 녹
87 skin frame_blue Frame/ItemFrame02_Single_Blue.png 1 등급 칸 청
88 skin frame_purple Frame/ItemFrame02_Single_Purple.png 1 등급 칸 자
89 skin frame_orange Frame/ItemFrame02_Single_Red.png 1 등급 칸 주(팩에 주황 없음 → 적색)
90 skin frame_gold Frame/ItemFrame02_Single_Yellow.png 1 등급 칸 금
91 icon cur_gold Icon_ItemIcons/128/ItemIcon_Money_Coin.Png 골드(임시 · B cur_gold)
92 icon cur_gem Icon_ItemIcons/128/ItemIcon_Gem_Circle.Png 보옥(임시)
93 icon cur_stamina Icon_ItemIcons/128/ItemIcon_Bag_Brown.Png 군량(임시)
94 icon cur_shard Icon_ItemIcons/128/ItemIcon_Star_Purple.Png 영웅 조각(임시)
95 icon cur_iron Icon_ItemIcons/128/ItemIcon_Gear.Png 운철(임시)
96 icon cur_blueprint Icon_ItemIcons/128/ItemIcon_MemoPad.Png 도면(임시)
97 icon cur_ticket Icon_ItemIcons/128/ItemIcon_Ticket_Gold.Png 입장권(임시)
98 icon cur_skillbook Icon_ItemIcons/128/ItemIcon_Book_1_Wine.Png 스킬서(임시)
99 icon cur_exp Icon_ItemIcons/128/ItemIcon_Star_Blue.Png 경험(임시)
100 icon cur_gear Icon_ItemIcons/128/ItemIcon_Gear_Sword.Png 장비(임시)
101 icon tab_hero Icon_PictoIcons/128/PictoIcon_Helmet_1.Png 하단 탭 영웅(임시)
102 icon tab_treasure Icon_PictoIcons/128/PictoIcon_Chest.Png 하단 탭 보물(임시)
103 icon tab_domestic Icon_PictoIcons/128/PictoIcon_Castle.Png 하단 탭 내정(임시)
104 icon tab_alliance Icon_PictoIcons/128/PictoIcon_Lock_1.Png 하단 탭 동맹(잠금 · 임시)
105 icon tab_trade Icon_PictoIcons/128/PictoIcon_Scale.Png 하단 탭 거래(임시)
106 icon btn_sortie Icon_PictoIcons/128/PictoIcon_Battle.Png 로비 출정(임시)
107 icon btn_formation Icon_PictoIcons/128/PictoIcon_Flag.Png 로비 편성(임시)
108 icon btn_arena Icon_PictoIcons/128/PictoIcon_Target.Png 로비 연무장(임시)
109 icon cls_infantry Icon_PictoIcons/128/PictoIcon_Sword_1.Png 병종 보병(임시)
110 icon cls_spear Icon_PictoIcons/128/PictoIcon_Pick.Png 병종 창병(임시)
111 icon cls_cavalry Icon_PictoIcons/128/PictoIcon_Paw.Png 병종 기병(임시)
112 icon cls_archer Icon_PictoIcons/128/PictoIcon_Bow.Png 병종 궁병(임시)
113 icon cls_strategist Icon_PictoIcons/128/PictoIcon_Pen_Feather.Png 병종 책사(임시)
114 icon chest_bronze Icon_ItemIcons/128/ItemIcon_Chest_Wood.Png 청동 상자(임시)
115 icon chest_silver Icon_ItemIcons/128/ItemIcon_Chest_Skull.png 은 상자(임시)
116 icon chest_gold Icon_ItemIcons/128/ItemIcon_Chest_Gold.Png 금 상자(임시)
117 icon chest_general Icon_ItemIcons/128/ItemIcon_Chest_Premium.Png 명장 상자(임시)
118 icon chest_gem Icon_ItemIcons/128/ItemIcon_Chest_Gem.Png 보옥 상자(임시)
119 icon chest_point Icon_ItemIcons/128/ItemIcon_Badge_Gold.png 상자 포인트(임시)
120 icon gear_sword Icon_ItemIcons/128/ItemIcon_Gear_Sword.Png 청동 검(임시)
121 icon gear_spear Icon_ItemIcons/128/ItemIcon_Battle.Png 철 창(임시)
122 icon gear_bow Icon_PictoIcons/128/PictoIcon_Bow.Png #8A5A2B 각궁(임시 · 실루엣 틴트)
123 icon gear_armor Icon_ItemIcons/128/ItemIcon_Gear_Armor_Top.Png 가죽 갑주(임시)
124 icon gear_helmet IconMisc/Icon_Helmet.png 철 투구(임시)
125 icon gear_horse Icon_ItemIcons/128/ItemIcon_Gear_Boots.Png 마구(임시)
126 icon gear_banner Icon_PictoIcons/128/PictoIcon_Flag.Png #D8412F 군령기(임시 · 실루엣 틴트)
127 icon gear_book Icon_ItemIcons/128/ItemIcon_Book_1_Purple.Png 병서(임시)
128 icon gear_potion Icon_ItemIcons/128/ItemIcon_Pot.Png 약낭(임시)
129 icon gear_shield IconMisc/Icon_Shield.png 방패(임시)
130 icon gear_charm Icon_ItemIcons/128/ItemIcon_Clover_Gold.Png 부적(임시)
131 icon gear_flag Icon_ItemIcons/128/ItemIcon_Medal_Gold_1.png 전고(임시)
132 icon flag_shu Frame/BorderFrame_Flag_Red.png 세력 촉(임시)
133 icon flag_wei Frame/BorderFrame_Flag_Blue.png 세력 위(임시)
134 icon flag_wu Frame/BorderFrame_Flag_Green.png 세력 오(임시)
135 icon flag_other Frame/BorderFrame_Flag_White_Bg.png #8C8C96 세력 군웅(임시)
136 icon rank_1 Icon_GradeBadge/128/Icon_GradeBadge_Beginner.Png 훈련 랭크 1(임시)
137 icon rank_2 Icon_GradeBadge/128/Icon_GradeBadge_Iron.Png 훈련 랭크 2(임시)
138 icon rank_3 Icon_GradeBadge/128/Icon_GradeBadge_Bronze.Png 훈련 랭크 3(임시)
139 icon rank_4 Icon_GradeBadge/128/Icon_GradeBadge_Silver.Png 훈련 랭크 4(임시)
140 icon rank_5 Icon_GradeBadge/128/Icon_GradeBadge_Gold.Png 훈련 랭크 5(임시)
141 icon rank_6 Icon_GradeBadge/128/Icon_GradeBadge_Platinum.Png 훈련 랭크 6(임시)
142 icon rank_7 Icon_GradeBadge/128/Icon_GradeBadge_Diamond.Png 훈련 랭크 7(임시)
143 icon star_on IconMisc/GradeIcon_Star_s_Yellow.png 별(켜짐)
144 icon star_off IconMisc/Icon_Star_Off.png 별(꺼짐)
145 icon lock IconMisc/Icon_Lock03.png 잠금
146 icon check IconMisc/Icon_Check03_s.png 체크
147 icon arrow_prev IconMisc/Icon_Arrow_Prev1.png 이전
148 icon arrow_next IconMisc/Icon_Arrow_Next1.png 다음
149 icon arrow_back IconMisc/Icon_Arrow_Back.png 뒤로
150 icon new_dot UI_Etc/Alert_Dot_Bg.png #F2453A 새 알림 점
151 icon tut_hand Icon_TutorialHand/128/tutorial_hand_1.png 튜토리얼 손가락
152 icon shop_pack Icon_ShopItem/ShopItem_l_GemPack_3.png 상점 패키지(준비 중 칸)
153 icon bg_lobby Assets/UI/Lobby/keyart_04.png 로비 배경(키아트 04 · B bg_lobby 오면 치환)
154 font title Assets/UI/GUIPro/ResourcesData/Fonts/Sen-ExtraBold.ttf 제목·숫자(라틴 Sen · 한글 = 폴백 Malgun Gothic · PD 결정 전)

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b79aa3573c2bd824eb781e9385791aad
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Resources/UI.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b327fe546ab27674db7aebf2c7c51f6c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,608 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 63b0a8e358e193740883a84a0bfe1325, type: 3}
m_Name:
m_EditorClassIdentifier: TKS.Runtime::TKS.UITheme
colors:
- id: bg
color: {r: 0.9372549, g: 0.8980392, b: 0.8117647, a: 1}
- id: paper
color: {r: 1, g: 0.9764706, b: 0.9254902, a: 1}
- id: header
color: {r: 0.18039216, g: 0.2, b: 0.28627452, a: 1}
- id: header_text
color: {r: 1, g: 1, b: 1, a: 1}
- id: header_sub
color: {r: 0.7882353, g: 0.7647059, b: 0.8392157, a: 1}
- id: ink
color: {r: 0.16470589, g: 0.13333334, b: 0.1882353, a: 1}
- id: ink_soft
color: {r: 0.43529412, g: 0.39215687, b: 0.4392157, a: 1}
- id: dark
color: {r: 0.22745098, g: 0.2509804, b: 0.3764706, a: 1}
- id: red
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
- id: line
color: {r: 0.11764706, g: 0.14509805, b: 0.20784314, a: 1}
- id: slot
color: {r: 0.8627451, g: 0.8156863, b: 0.7254902, a: 1}
- id: coin
color: {r: 0.9529412, g: 0.7176471, b: 0.18039216, a: 1}
- id: jade
color: {r: 0.13725491, g: 0.7019608, b: 0.5803922, a: 1}
- id: grain
color: {r: 0.8509804, g: 0.5882353, b: 0.24705882, a: 1}
- id: ok
color: {r: 0.2784314, g: 0.70980394, b: 0.24705882, a: 1}
- id: star
color: {r: 1, g: 0.7607843, b: 0.101960786, a: 1}
- id: disabled
color: {r: 0.61960787, g: 0.59607846, b: 0.6392157, a: 1}
- id: purple
color: {r: 0.6039216, g: 0.34117648, b: 0.84705883, a: 1}
- id: white
color: {r: 1, g: 1, b: 1, a: 1}
- id: bar
color: {r: 0.11764706, g: 0.14509805, b: 0.20784314, a: 1}
- id: pill
color: {r: 0.20784314, g: 0.23137255, b: 0.34117648, a: 1}
- id: bar_text
color: {r: 1, g: 1, b: 1, a: 1}
- id: bar_text_off
color: {r: 0.5529412, g: 0.5764706, b: 0.65882355, a: 1}
- id: tab_off
color: {r: 0.22745098, g: 0.2509804, b: 0.3764706, a: 1}
- id: stroke
color: {r: 0.11764706, g: 0.14509805, b: 0.20784314, a: 1}
- id: dim
color: {r: 0, g: 0, b: 0, a: 0.627451}
- id: lobby_tint
color: {r: 0.6117647, g: 0.58431375, b: 0.65882355, a: 1}
- id: bar_track
color: {r: 1, g: 1, b: 1, a: 1}
- id: grade_white
color: {r: 0.8509804, g: 0.8392157, b: 0.8117647, a: 1}
- id: grade_green
color: {r: 0.2784314, g: 0.70980394, b: 0.24705882, a: 1}
- id: grade_blue
color: {r: 0.23529412, g: 0.48235294, b: 0.8784314, a: 1}
- id: grade_purple
color: {r: 0.6039216, g: 0.34117648, b: 0.84705883, a: 1}
- id: grade_orange
color: {r: 0.9411765, g: 0.5411765, b: 0.14117648, a: 1}
- id: grade_gold
color: {r: 0.9529412, g: 0.75686276, b: 0.18039216, a: 1}
- id: type_infantry
color: {r: 0.78431374, g: 0.33333334, b: 0.22745098, a: 1}
- id: type_spear
color: {r: 0.24313726, g: 0.6039216, b: 0.2901961, a: 1}
- id: type_cavalry
color: {r: 0.23529412, g: 0.40392157, b: 0.78431374, a: 1}
- id: type_archer
color: {r: 0.8509804, g: 0.6039216, b: 0.16862746, a: 1}
- id: type_strategist
color: {r: 0.6039216, g: 0.34117648, b: 0.84705883, a: 1}
- id: faction_shu
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
- id: faction_wei
color: {r: 0.23529412, g: 0.48235294, b: 0.8784314, a: 1}
- id: faction_wu
color: {r: 0.18431373, g: 0.65882355, b: 0.30980393, a: 1}
- id: faction_other
color: {r: 0.54901963, g: 0.54901963, b: 0.5882353, a: 1}
- id: hp_bg
color: {r: 1, g: 1, b: 1, a: 1}
- id: hp_hi
color: {r: 0.32156864, g: 0.81960785, b: 0.28235295, a: 1}
- id: hp_mid
color: {r: 1, g: 0.77254903, b: 0.16862746, a: 1}
- id: hp_low
color: {r: 0.9490196, g: 0.27058825, b: 0.22745098, a: 1}
- id: pip_on
color: {r: 1, g: 0.7607843, b: 0.101960786, a: 1}
- id: pip_off
color: {r: 0.16862746, g: 0.18431373, b: 0.2509804, a: 0.8}
- id: hud_panel
color: {r: 0.11764706, g: 0.14509805, b: 0.20784314, a: 0.8509804}
- id: hud_turn_player
color: {r: 0.23529412, g: 0.48235294, b: 0.8784314, a: 1}
- id: hud_turn_enemy
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
- id: skill_ready
color: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
- id: skill_reserved
color: {r: 0.9529412, g: 0.7176471, b: 0.18039216, a: 1}
- id: skill_off
color: {r: 0.3529412, g: 0.36862746, b: 0.4392157, a: 1}
- id: skill_passive
color: {r: 0.22745098, g: 0.2509804, b: 0.3764706, a: 1}
- id: skill_cool
color: {r: 0, g: 0, b: 0, a: 0.65882355}
skins:
- id: card
pack: {fileID: 21300000, guid: 3070a6f23e6ca4273897acf1b7e8c5ce, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: soft
pack: {fileID: 21300000, guid: 6c9211bc1d90b4e35a3a080121e93d05, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: button
pack: {fileID: 21300000, guid: 615459ca724384003a91b93533033ab1, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 21300000, guid: 1047c0bcbda25440faf8332b872b3529, type: 3}
ppu: 1
inset: {x: 5, y: 5, z: 5, w: 18}
overlayAlpha: 0.35
- id: dot
pack: {fileID: 21300000, guid: b31cc26b044a54db88302afadc39ed11, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: bar_bg
pack: {fileID: 21300000, guid: 6332433fb34294d58bfc835736ee8ac3, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: bar_fill
pack: {fileID: 21300000, guid: 3fceb536486e040c99fac1bb7f353cf6, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: pill
pack: {fileID: 21300000, guid: 83aedc4dd182a4b859ab60b44189cda6, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: plus
pack: {fileID: 21300000, guid: 77db17572c3694c38812470f80f4009b, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 21300000, guid: a707b1e1252f042c58519d2882ee2651, type: 3}
ppu: 1
inset: {x: 14, y: 14, z: 14, w: 16}
overlayAlpha: 1
- id: tabbar
pack: {fileID: 21300000, guid: 03cc62a06f5fc44639b0a7ec241ee65f, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: tab_focus
pack: {fileID: 21300000, guid: 3070a6f23e6ca4273897acf1b7e8c5ce, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 21300000, guid: d332162b7f97a44c8968e3570e42e325, type: 3}
ppu: 1
inset: {x: 6, y: 4, z: 6, w: 30}
overlayAlpha: 0.45
- id: popup
pack: {fileID: 21300000, guid: 2bd06dd44f28c41ebac87b908313e284, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: popup_head
pack: {fileID: 21300000, guid: 620879c9ab62c447b82f0c54e1e88fee, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 21300000, guid: ab01a2531c35a4aafb31413b44dd1bcb, type: 3}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 60}
overlayAlpha: 0.25
- id: close
pack: {fileID: 21300000, guid: ac729c6a63c5c46578bef27ddfc3c0fe, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 21300000, guid: c7c98a2b50d944d2d933ad3c1f60ad39, type: 3}
ppu: 1
inset: {x: 22, y: 22, z: 22, w: 26}
overlayAlpha: 1
- id: ribbon
pack: {fileID: 21300000, guid: 2495840c8944f4f05b1d8aa2a409ad7c, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: ribbon_gray
pack: {fileID: 21300000, guid: 46d72fa068f1c473cacf5009c97a9b9c, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: toast
pack: {fileID: 21300000, guid: 3070a6f23e6ca4273897acf1b7e8c5ce, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: portrait_mask
pack: {fileID: 21300000, guid: 6c9211bc1d90b4e35a3a080121e93d05, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: circle_frame
pack: {fileID: 21300000, guid: 555b142732e774962897f672b89e14a9, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: skill_btn
pack: {fileID: 21300000, guid: c8638447d0c4a40fd9a7e32c25783d96, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: skill_cool
pack: {fileID: 21300000, guid: d8a3e7288e03247ec8f56796382ec1a1, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: hp_bg
pack: {fileID: 21300000, guid: 1dfa4588cbf5d4c8eb9ecaf11c55c88b, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: hp_fill
pack: {fileID: 21300000, guid: 3fceb536486e040c99fac1bb7f353cf6, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: pip
pack: {fileID: 21300000, guid: 3070a6f23e6ca4273897acf1b7e8c5ce, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: hud_pill
pack: {fileID: 21300000, guid: 83aedc4dd182a4b859ab60b44189cda6, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: banner
pack: {fileID: 21300000, guid: 0386c61d06bee46feb8e6bca9882cb40, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: flag
pack: {fileID: 21300000, guid: 6f23dee49868b4ee4bb927e2eebe0f5a, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_white
pack: {fileID: 21300000, guid: f0bdcf4700e3143dd941fff695e8c391, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_green
pack: {fileID: 21300000, guid: 9f5976efc00a04bbf9d3a01ac0219c63, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_blue
pack: {fileID: 21300000, guid: 63128e5af0cdc46bfbd4a90aac012629, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_purple
pack: {fileID: 21300000, guid: 7878a4afed4e6465c9785efcd828aaec, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_orange
pack: {fileID: 21300000, guid: e628dc11848744b1c8b2cf768e50aaeb, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
- id: frame_gold
pack: {fileID: 21300000, guid: bf3e36f58b7084484bf9b2cf2fb6f142, type: 3}
samguk: {fileID: 0}
overlay: {fileID: 0}
ppu: 1
inset: {x: 0, y: 0, z: 0, w: 0}
overlayAlpha: 1
icons:
- id: cur_gold
pack: {fileID: 21300000, guid: 6d5687fb0dd3a428d9cd26a6f36c6d78, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_gem
pack: {fileID: 21300000, guid: 42854c0d5a9654da395ed40f0e7443ed, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_stamina
pack: {fileID: 21300000, guid: a6832029756c14e05a79241b3ab8bfd5, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_shard
pack: {fileID: 21300000, guid: a480ffcca13b444cc828ffb178e72a88, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_iron
pack: {fileID: 21300000, guid: d3aad013ac9a74286a3cf57df76f510f, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_blueprint
pack: {fileID: 21300000, guid: 3cadefdd2c8ef4523bd35390bab0359e, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_ticket
pack: {fileID: 21300000, guid: 1ca4b50341717411f9ad901950edfc2b, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_skillbook
pack: {fileID: 21300000, guid: c16591869b2474a649dac6761e3e4b7e, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_exp
pack: {fileID: 21300000, guid: 99225861569984f349a535e76bfe9902, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cur_gear
pack: {fileID: 21300000, guid: 41bc376691e7a4aef8398c3f84ee249f, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: tab_hero
pack: {fileID: 21300000, guid: 6fcf7bcb2b29e4b589e82ab74f31c79e, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: tab_treasure
pack: {fileID: 21300000, guid: 3654b5c84fc0440f786a4918b576d3bb, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: tab_domestic
pack: {fileID: 21300000, guid: ad90f1e81df3442d3b8fc40036d24cd3, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: tab_alliance
pack: {fileID: 21300000, guid: 1eb0217bf1f784956bbb96cf1663c557, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: tab_trade
pack: {fileID: 21300000, guid: 6add04477e44d48508df9a7dabad0134, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: btn_sortie
pack: {fileID: 21300000, guid: 7f272d61d71ea41479651113f8ec2456, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: btn_formation
pack: {fileID: 21300000, guid: 816d14bb0d767435585a0bb55886aaef, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: btn_arena
pack: {fileID: 21300000, guid: d3112ce909bbc4504bdadf79eb35726d, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cls_infantry
pack: {fileID: 21300000, guid: 3d059bda0f1cb4181a080c8258995348, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cls_spear
pack: {fileID: 21300000, guid: 16edef63d3f7646d9bcf5f86b0e476ec, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cls_cavalry
pack: {fileID: 21300000, guid: 6200d1a117bb34c21ae7b0265c0027c3, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cls_archer
pack: {fileID: 21300000, guid: 21efa6791000e47f2b2b3d493c5d34f0, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: cls_strategist
pack: {fileID: 21300000, guid: 244985ea9313545a38c1aec6679972a9, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_bronze
pack: {fileID: 21300000, guid: fd7a2020060844292b286576606000ab, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_silver
pack: {fileID: 21300000, guid: 52bd6c820cae24fbbbb577594c4cb085, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_gold
pack: {fileID: 21300000, guid: 145874fd002594d928ac4f1cc9046b3d, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_general
pack: {fileID: 21300000, guid: 9de9bb779c7694b08bf5aabc148b1dd3, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_gem
pack: {fileID: 21300000, guid: 05b4c15e805fa4156812361f1797f526, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: chest_point
pack: {fileID: 21300000, guid: b23fb0df2c4474124bcec7b152ff27d9, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_sword
pack: {fileID: 21300000, guid: 41bc376691e7a4aef8398c3f84ee249f, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_spear
pack: {fileID: 21300000, guid: d031f4b21493c4841914d4257119a4e3, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_bow
pack: {fileID: 21300000, guid: 21efa6791000e47f2b2b3d493c5d34f0, type: 3}
samguk: {fileID: 0}
tint: {r: 0.5411765, g: 0.3529412, b: 0.16862746, a: 1}
- id: gear_armor
pack: {fileID: 21300000, guid: 3a3a5d970e2d94998818421e4e0e384b, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_helmet
pack: {fileID: 21300000, guid: a52e1372e66954bcf8dfac5f52044251, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_horse
pack: {fileID: 21300000, guid: e07c16cced13542f59c7400aff52d8a7, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_banner
pack: {fileID: 21300000, guid: 816d14bb0d767435585a0bb55886aaef, type: 3}
samguk: {fileID: 0}
tint: {r: 0.84705883, g: 0.25490198, b: 0.18431373, a: 1}
- id: gear_book
pack: {fileID: 21300000, guid: 4ea16497cec35499e8fcb6e48452aa29, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_potion
pack: {fileID: 21300000, guid: 67f5e0fdd0f6b4952a22b99269db6096, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_shield
pack: {fileID: 21300000, guid: 09135c198f8744fcd89493b169d11bd8, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_charm
pack: {fileID: 21300000, guid: 5d49c0aac30764cde93957570569f245, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: gear_flag
pack: {fileID: 21300000, guid: 36c378f3ccf8c40fe8c6a3ae69624bae, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: flag_shu
pack: {fileID: 21300000, guid: 0f8c944da644b42e09c7342aa5735ff9, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: flag_wei
pack: {fileID: 21300000, guid: ca6ab62179a66442588aacd94b454ffe, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: flag_wu
pack: {fileID: 21300000, guid: b625cd3838cc0477ca073199439588b5, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: flag_other
pack: {fileID: 21300000, guid: 6f23dee49868b4ee4bb927e2eebe0f5a, type: 3}
samguk: {fileID: 0}
tint: {r: 0.54901963, g: 0.54901963, b: 0.5882353, a: 1}
- id: rank_1
pack: {fileID: 21300000, guid: 8ae43bb338f6c4486b21b6220a1cac9d, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_2
pack: {fileID: 21300000, guid: cbe57c0243cf5440e9934cffa4681019, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_3
pack: {fileID: 21300000, guid: 4b3a9b4d9f2ed4a92996c71af3ba0c42, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_4
pack: {fileID: 21300000, guid: 0e3e88c80644941acab7b5bfc3d02d78, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_5
pack: {fileID: 21300000, guid: 7f4622e1fadad4b7680182e7309efca3, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_6
pack: {fileID: 21300000, guid: 1054306ffb8884ea8a2e142d780c85cb, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: rank_7
pack: {fileID: 21300000, guid: 65172c8f38641400dbb8dea2f06a5fcd, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: star_on
pack: {fileID: 21300000, guid: 39afaf3c0b490422ea5f07aac1dcfbe1, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: star_off
pack: {fileID: 21300000, guid: d228604ab6373411fac2dbf1e0e215d1, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: lock
pack: {fileID: 21300000, guid: 899da40fa9ae1462c8e7e40a89da5ca1, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: check
pack: {fileID: 21300000, guid: 598c081bd6eaf40bfba9db8c9e2862a9, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: arrow_prev
pack: {fileID: 21300000, guid: a9d09d9a3d0d442079a130341db6be90, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: arrow_next
pack: {fileID: 21300000, guid: 84c3adc90c0da4f009a1cfa76fa9363c, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: arrow_back
pack: {fileID: 21300000, guid: 26459d86a13194dc1ab0997e73d7c594, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: new_dot
pack: {fileID: 21300000, guid: 9209a152c6b45453e9b98b6a1f1f2084, type: 3}
samguk: {fileID: 0}
tint: {r: 0.9490196, g: 0.27058825, b: 0.22745098, a: 1}
- id: tut_hand
pack: {fileID: 21300000, guid: acfcced52c2a44ec78ff53f2b51971b2, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: shop_pack
pack: {fileID: 21300000, guid: 33f163c49b488460ea0182825ae1600e, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
- id: bg_lobby
pack: {fileID: 21300000, guid: 604887b577dfd2e4aacb8cf8509a5878, type: 3}
samguk: {fileID: 0}
tint: {r: 1, g: 1, b: 1, a: 1}
titleFont: {fileID: 12800000, guid: c8e065a266a274eeebccb4ae0f64b3d9, type: 3}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0944e51b3c366294b95f0b4b93cfe4c5
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -121,6 +121,24 @@ namespace TKS.EditorTools
EditorApplication.EnterPlaymode();
}
/// <summary>
/// UI 리뉴얼 검증 캡처(GUI 에디터 · 사본 프로젝트 권장 · 세이브 = 메모리 전용 · PD #1100):
/// Unity.exe -projectPath &lt;copy&gt; -executeMethod TKS.EditorTools.DataImporter.CaptureUI -uiDir &lt;dir&gt;
/// 화면 10종 + 팝업(1080×1920) + 1536×2048 2장 PNG + log · 끝나면 스스로 종료.
/// </summary>
public static void CaptureUI()
{
string dir = GetArg("-uiDir");
if (string.IsNullOrEmpty(dir)) dir = Path.Combine(Directory.GetCurrentDirectory(), "Logs", "ui_capture");
ImportAll();
UIThemeImporter.Import();
EditorSceneManager.OpenScene(ProjectSetup.ScenePath, OpenSceneMode.Single);
SessionState.SetBool(SaveStore.MemoryFlagKey, true);
SessionState.SetString(UICaptureDriver.DirKey, dir);
Debug.Log("[TKS] UI 캡처 시작 → " + dir);
EditorApplication.EnterPlaymode();
}
static string GetArg(string name)
{
var args = System.Environment.GetCommandLineArgs();

View File

@ -21,6 +21,7 @@ namespace TKS.EditorTools
BuildScene(cfg);
ApplySettings();
UnitArtImporter.ReimportAll();
UIThemeImporter.Import();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[TKS] Setup 완료 · " + ConfigPath + " · " + ScenePath);
@ -41,6 +42,7 @@ namespace TKS.EditorTools
BuildScene(cfg);
ApplySettings();
UnitArtImporter.ReimportAll();
UIThemeImporter.Import();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("[TKS] BuildFresh 완료 · 기본값 재시드 · " + ScenePath);

View File

@ -0,0 +1,234 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace TKS.EditorTools
{
/// <summary>
/// UI 테마 임포트(PD #1100) — Assets/Data/UI/ui_theme.csv(SOT) → Assets/Resources/UI/UITheme.asset.
/// 경로 = 팩 Components 기준 상대 경로(Assets/ 로 시작하면 그대로) · 오류가 있으면 자산을 바꾸지 않는다.
/// 신규 제작 납품(Assets/UI/Samguk/**/&lt;id&gt;.png)은 같은 id 의 스킨·아이콘을 치환하고 표에 없는 id 는 아이콘으로 추가한다(코드 변경 없이 교체).
/// </summary>
public static class UIThemeImporter
{
public const string CsvPath = "Assets/Data/UI/ui_theme.csv";
public const string AssetPath = "Assets/Resources/UI/UITheme.asset";
public const string PackRoot = "Assets/UI/GUIPro/ResourcesData/Sprites/Components/";
public const string SamgukRoot = "Assets/UI/Samguk";
[MenuItem("TKS/Import UI Theme (UI 테마 임포트)")]
public static void Menu()
{
Import();
}
public static bool Import()
{
var errors = new List<string>();
var theme = ScriptableObject.CreateInstance<UITheme>();
if (!File.Exists(CsvPath))
{
Debug.LogError("[TKS] UI 테마 표 없음: " + CsvPath);
return false;
}
var lines = File.ReadAllText(CsvPath, Encoding.UTF8).TrimStart('').Replace("\r", "").Split('\n');
for (int n = 1; n < lines.Length; n++)
{
var line = lines[n];
if (string.IsNullOrWhiteSpace(line)) continue;
var f = line.Split(',');
if (f.Length < 8)
{
errors.Add((n + 1) + "행: 열 부족");
continue;
}
string kind = f[0].Trim(), id = f[1].Trim(), path = f[2].Trim(), overlay = f[3].Trim(), color = f[7].Trim();
switch (kind)
{
case "color":
{
Color c;
if (!ColorUtility.TryParseHtmlString(color, out c)) errors.Add((n + 1) + "행 " + id + ": 색 형식 " + color);
else theme.colors.Add(new UITheme.ColorEntry { id = id, color = c });
break;
}
case "skin":
{
var s = new UITheme.SkinEntry { id = id, pack = LoadSprite(path, n, errors) };
if (overlay.Length > 0) s.overlay = LoadSprite(overlay, n, errors);
s.ppu = ParseF(f[4], 1f);
s.inset = ParseInset(f[5]);
s.overlayAlpha = ParseF(f[6], 1f);
theme.skins.Add(s);
break;
}
case "icon":
{
var e = new UITheme.IconEntry { id = id, pack = LoadSprite(path, n, errors) };
Color c;
if (color.Length > 0 && ColorUtility.TryParseHtmlString(color, out c)) e.tint = c;
theme.icons.Add(e);
break;
}
case "font":
if (id == "title")
{
theme.titleFont = AssetDatabase.LoadAssetAtPath<Font>(path);
if (theme.titleFont == null) errors.Add((n + 1) + "행: 폰트 없음 " + path);
}
break;
default:
errors.Add((n + 1) + "행: 알 수 없는 kind " + kind);
break;
}
}
if (errors.Count > 0)
{
foreach (var e in errors) Debug.LogError("[TKS] UI 테마 표 오류 · " + e);
Debug.LogError("[TKS] UI 테마 임포트 중단(오류 " + errors.Count + ") · 자산 변경 없음");
Object.DestroyImmediate(theme);
return false;
}
// 신규 제작 치환(파일 이름 = id)
var samguk = SamgukSprites();
int replaced = 0, added = 0;
var used = new HashSet<string>();
foreach (var s in theme.skins)
{
Sprite sp;
if (samguk.TryGetValue(s.id, out sp))
{
s.samguk = sp;
used.Add(s.id);
replaced++;
}
}
foreach (var e in theme.icons)
{
Sprite sp;
if (samguk.TryGetValue(e.id, out sp))
{
e.samguk = sp;
used.Add(e.id);
replaced++;
}
}
foreach (var kv in samguk)
{
if (used.Contains(kv.Key)) continue;
theme.icons.Add(new UITheme.IconEntry { id = kv.Key, samguk = kv.Value });
added++;
}
if (!AssetDatabase.IsValidFolder("Assets/Resources")) AssetDatabase.CreateFolder("Assets", "Resources");
if (!AssetDatabase.IsValidFolder("Assets/Resources/UI")) AssetDatabase.CreateFolder("Assets/Resources", "UI");
var asset = AssetDatabase.LoadAssetAtPath<UITheme>(AssetPath);
if (asset == null)
{
AssetDatabase.CreateAsset(theme, AssetPath);
asset = theme;
}
else
{
EditorUtility.CopySerialized(theme, asset);
Object.DestroyImmediate(theme);
}
asset.Rebuild();
EditorUtility.SetDirty(asset);
AssetDatabase.SaveAssets();
UITheme.Use(asset);
Debug.Log("[TKS] UI 테마 임포트 · 색 " + asset.colors.Count + " · 스킨 " + asset.skins.Count + " · 아이콘 " + asset.icons.Count +
" · 신규 제작 치환 " + replaced + " · 추가 " + added + " → " + AssetPath);
return true;
}
/// <summary>Assets/UI/Samguk/**/&lt;id&gt;.png → 스프라이트(같은 id 가 둘이면 경로 순 마지막).</summary>
public static Dictionary<string, Sprite> SamgukSprites()
{
var map = new Dictionary<string, Sprite>();
if (!AssetDatabase.IsValidFolder(SamgukRoot)) return map;
var guids = AssetDatabase.FindAssets("t:Sprite", new[] { SamgukRoot });
var paths = new List<string>();
foreach (var g in guids) paths.Add(AssetDatabase.GUIDToAssetPath(g));
paths.Sort(System.StringComparer.Ordinal);
foreach (var p in paths)
{
var sp = AssetDatabase.LoadAssetAtPath<Sprite>(p);
if (sp != null) map[Path.GetFileNameWithoutExtension(p)] = sp;
}
return map;
}
static Sprite LoadSprite(string path, int n, List<string> errors)
{
if (string.IsNullOrEmpty(path))
{
errors.Add((n + 1) + "행: 경로 없음");
return null;
}
string full = path.StartsWith("Assets/") ? path : PackRoot + path;
var sp = AssetDatabase.LoadAssetAtPath<Sprite>(full);
if (sp == null) errors.Add((n + 1) + "행: 스프라이트 없음 " + full);
return sp;
}
static float ParseF(string s, float def)
{
float v;
return float.TryParse(s.Trim(), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out v) ? v : def;
}
static Vector4 ParseInset(string s)
{
s = s.Trim();
if (s.Length == 0) return Vector4.zero;
var p = s.Split(';');
if (p.Length == 1)
{
float v = ParseF(p[0], 0f);
return new Vector4(v, v, v, v);
}
return new Vector4(ParseF(p[0], 0f), p.Length > 1 ? ParseF(p[1], 0f) : 0f, p.Length > 2 ? ParseF(p[2], 0f) : 0f, p.Length > 3 ? ParseF(p[3], 0f) : 0f);
}
}
/// <summary>신규 제작 UI(Assets/UI/Samguk) · 로비 배경(Assets/UI/Lobby) 임포트 설정 = Sprite(2D) · 납품 파일이 들어오면 테마 치환 테이블 자동 갱신.</summary>
public class UIThemeAssetPostprocessor : AssetPostprocessor
{
static bool pending;
static bool Ours(string path)
{
return path.StartsWith(UIThemeImporter.SamgukRoot + "/") || path.StartsWith("Assets/UI/Lobby/");
}
void OnPreprocessTexture()
{
if (!Ours(assetPath)) return;
var ti = (TextureImporter)assetImporter;
ti.textureType = TextureImporterType.Sprite;
ti.spriteImportMode = SpriteImportMode.Single;
ti.alphaIsTransparency = true;
ti.mipmapEnabled = false;
ti.spritePixelsPerUnit = 100f;
ti.maxTextureSize = assetPath.StartsWith("Assets/UI/Lobby/") ? 2048 : 1024;
}
static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFrom)
{
bool hit = false;
foreach (var p in imported) hit |= p.StartsWith(UIThemeImporter.SamgukRoot + "/") && p.EndsWith(".png");
foreach (var p in deleted) hit |= p.StartsWith(UIThemeImporter.SamgukRoot + "/") && p.EndsWith(".png");
if (!hit || pending) return;
pending = true;
EditorApplication.delayCall += () =>
{
pending = false;
UIThemeImporter.Import();
};
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 30f8ad8437cad284d831742184f31c39

View File

@ -225,10 +225,12 @@ namespace TKS.OutGame
PatrolRules.ChestPerHour(db, d).ToString("0.##") + " / 시간",
};
Color[] rc = { UiKit.Coin, new Color(0.8f, 0.48f, 0.38f), UiKit.Ok, new Color(0.7f, 0.48f, 0.3f) };
string[] rIcon = { "cur_gold", "cur_exp", "cur_gear", "chest_bronze" };
for (int i = 0; i < 4; i++)
{
var line = UiKit.Box("Row" + i, rate, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -76f - i * 78f), new Vector2(980f, 68f), new Color(0.95f, 0.92f, 0.86f));
UiKit.Dot("Dot", line.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(16f, 0f), 50f, rc[i]);
if (UiKit.Icon(line.transform, rIcon[i], new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(12f, 0f), 58f) == null)
UiKit.Dot("Dot", line.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(16f, 0f), 50f, rc[i]);
UiKit.Label(line.transform, rn[i], 34, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(86f, 0f), new Vector2(400f, 56f));
UiKit.Label(line.transform, rv[i], 30, UiKit.Ink, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-20f, 0f), new Vector2(520f, 56f), false);
}
@ -370,11 +372,9 @@ namespace TKS.OutGame
var foot = UiKit.Box("Foot", p, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 16f), new Vector2(1032f, 150f), UiKit.Paper);
UiKit.Border(foot, UiKit.Line, 3f);
var bp = UiKit.Dot("Bp", foot.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), 76f, new Color(0.33f, 0.45f, 0.62f));
UiKit.FillLabel(bp.transform, "도", 36, UiKit.White);
UiKit.IconOrDot(foot.transform, "cur_blueprint", "도", new Color(0.33f, 0.45f, 0.62f), new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), 76f);
UiKit.Label(foot.transform, "도면 ×" + d.inv.blueprint, 38, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(110f, 0f), new Vector2(260f, 60f));
var gd = UiKit.Dot("Gold", foot.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(380f, 0f), 76f, UiKit.Coin);
UiKit.FillLabel(gd.transform, "골", 36, UiKit.White);
UiKit.IconOrDot(foot.transform, "cur_gold", "골", UiKit.Coin, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(380f, 0f), 76f);
UiKit.Label(foot.transform, d.gold.ToString("N0"), 38, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(470f, 0f), new Vector2(220f, 60f));
UiKit.Btn("Reset", foot.transform, "트리 초기화\n<size=24>보옥 " + db.Economy.DevResetGem + " · 도면 환급</size>", 34, UiKit.Dark, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f, 0f), new Vector2(320f, 120f), () =>
{

View File

@ -98,7 +98,8 @@ namespace TKS.OutGame
var cell = UiKit.Cell(parent, "T_" + it.id, owned ? UiKit.Paper : new Color(0.85f, 0.82f, 0.77f), owned ? (System.Action)(() => Detail(it.id)) : null);
UiKit.Border(cell, owned ? GradeColor(it.grade) : new Color(0.7f, 0.66f, 0.62f), 3f);
var icon = UiKit.Dot("Icon", cell.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -14f), 120f, owned ? new Color(0.94f, 0.9f, 0.83f) : new Color(0.78f, 0.74f, 0.69f));
UiKit.FillLabel(icon.transform, owned ? it.nameKo.Substring(0, 1) : "?", 60, owned ? GradeColor(it.grade) : new Color(0.6f, 0.56f, 0.52f));
if (owned && UiKit.HasFinalIcon("tre_" + it.id)) UiKit.Icon(icon.transform, "tre_" + it.id, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 100f);
else UiKit.FillLabel(icon.transform, owned ? it.nameKo.Substring(0, 1) : "?", 60, owned ? GradeColor(it.grade) : new Color(0.6f, 0.56f, 0.52f));
if (owned)
{
if (showShards && st.star < it.starMax)
@ -255,8 +256,8 @@ namespace TKS.OutGame
var goalBox = UiKit.TopBand("Points", p, 6f, 150f, 24f, 24f);
var gimg = UiKit.Paint(goalBox, UiKit.Paper);
UiKit.Border(gimg, UiKit.Line, 3f);
var pd = UiKit.Dot("P", goalBox, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), 90f, UiKit.Coin);
UiKit.FillLabel(pd.transform, "P", 44, UiKit.Ink);
if (UiKit.Icon(goalBox, "chest_point", new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(16f, 0f), 100f) == null)
UiKit.FillLabel(UiKit.Dot("P", goalBox, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), 90f, UiKit.Coin).transform, "P", 44, UiKit.Ink);
var reward = db.Chests.Get(e.ChestPointReward);
UiKit.Label(goalBox, (e.ChestPointGoal - d.inv.chestPoints) + "포인트 더 모으면 " + (reward != null ? reward.nameKo : ""), 34, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(130f, -12f), new Vector2(800f, 54f));
var fill = UiKit.Bar("Bar", goalBox, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(130f, 52f), new Vector2(700f, 30f), new Color(0.88f, 0.84f, 0.77f), UiKit.Coin);
@ -268,7 +269,8 @@ namespace TKS.OutGame
int count = TreasureRules.ChestCount(d, c.id);
var banner = UiKit.Box("Banner", p, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -180f), new Vector2(560f, 470f), ChestColor(c.id));
UiKit.Border(banner, new Color(0.25f, 0.2f, 0.18f), 4f);
UiKit.Label(banner.transform, c.nameKo, 60, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), new Vector2(520f, 90f));
UiKit.Title(UiKit.Label(banner.transform, c.nameKo, 60, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), new Vector2(520f, 90f)), true);
UiKit.Icon(banner.transform, "chest_" + c.id, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(10f, -10f), 150f);
int line = 0;
for (int i = 0; i < ChestDb.ProbGrades.Length; i++)
{
@ -305,6 +307,7 @@ namespace TKS.OutGame
UiKit.Border(b.GetComponent<Image>(), i == chestIdx ? UiKit.Coin : UiKit.Line, i == chestIdx ? 5f : 2f);
var box = UiKit.Box("Icon", b.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(w * 0.7f, 90f), ChestColor(ch.id));
box.raycastTarget = false;
UiKit.Icon(box.transform, "chest_" + ch.id, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 4f), 96f);
UiKit.Label(b.transform, ch.nameKo + "\n<size=34>×" + TreasureRules.ChestCount(d, ch.id) + "</size>", 26, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 14f), new Vector2(w, 100f));
}
}

View File

@ -17,6 +17,9 @@ namespace TKS.OutGame
readonly Text conds;
readonly Text rewards;
readonly Text hint;
readonly Image ribbon;
readonly RectTransform starRow;
readonly Image[] starImg = new Image[3];
public bool Visible
{
@ -32,13 +35,32 @@ namespace TKS.OutGame
{
root = r;
var dim = UiKit.Fill("Result", canvas);
UiKit.Paint(dim, new Color(0f, 0f, 0f, 0.55f), false);
UiKit.Paint(dim, UISkin.Col("dim", new Color(0f, 0f, 0f, 0.55f)), false);
go = dim.gameObject;
var panel = UiKit.Box("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 40f), new Vector2(920f, 1180f), UiKit.Paper);
UiKit.Border(panel, UiKit.Line, 4f);
var head = UiKit.TopBand("Head", panel.transform, 0f, 170f);
UiKit.Paint(head, UiKit.Dark);
title = UiKit.FillLabel(head, "", 96, UiKit.White);
Image panel;
if (UISkin.Has("popup"))
{
// 팩 팝업 틀 + 제목 리본(승리 = 붉은 · 패배 = 회색) + 별 아이콘 3
panel = UiKit.Skin(UiKit.Rect("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f), new Vector2(920f, 1180f)), "popup", UiKit.Paper);
var rib = UiKit.Rect("Ribbon", panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, 10f), new Vector2(1000f, 200f));
ribbon = UiKit.Skin(rib, "ribbon", Color.white);
title = UiKit.Title(UiKit.FillLabel(rib, "", 88, UiKit.White, TextAnchor.MiddleCenter, true, 150f), true);
title.rectTransform.offsetMin = new Vector2(150f, 34f);
starRow = UiKit.Rect("StarRow", panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -150f), new Vector2(600f, 190f));
for (int i = 0; i < 3; i++)
{
float s = i == 1 ? 190f : 150f;
starImg[i] = UiKit.Icon(starRow, "star_on", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2((i - 1) * 180f, i == 1 ? 14f : -10f), s);
}
}
else
{
panel = UiKit.Box("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 40f), new Vector2(920f, 1180f), UiKit.Paper);
UiKit.Border(panel, UiKit.Line, 4f);
var head = UiKit.TopBand("Head", panel.transform, 0f, 170f);
UiKit.Paint(head, UiKit.Dark);
title = UiKit.FillLabel(head, "", 96, UiKit.White);
}
stars = UiKit.Label(panel.transform, "", 120, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -190f), new Vector2(800f, 150f));
conds = UiKit.Label(panel.transform, "", 34, UiKit.Ink, TextAnchor.UpperLeft, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -350f), new Vector2(760f, 180f), false);
conds.lineSpacing = 1.15f;
@ -61,6 +83,7 @@ namespace TKS.OutGame
hint.text = "화면을 탭하면 월드맵으로";
title.text = (r.win ? "승리" : "패배") + " <size=44>" + r.chapter + "-" + r.stage + (r.boss ? " 보스" : "") + "</size>";
stars.text = r.win ? UiKit.Stars(r.stars, 3) : UiKit.Stars(0, 3);
ShowStars(r.win ? r.stars : 0, r.win);
if (r.win)
{
conds.text = Mark(r.conds[0]) + " 클리어\n" +
@ -87,10 +110,22 @@ namespace TKS.OutGame
}
title.text = r.kind == ArenaRules.Raid ? "토벌 종료" : r.win ? "승리" : "패배";
stars.text = "";
ShowStars(-1, r.win || r.kind == ArenaRules.Raid);
conds.text = "<size=44>" + r.title + "</size>";
rewards.text = string.Join("\n", r.lines.ToArray());
}
/// <summary>별 아이콘(n &lt; 0 = 숨김) · 리본 색(승리 붉은 · 패배 회색) · 테마가 없으면 글자 별만.</summary>
void ShowStars(int n, bool win)
{
if (starRow == null) return;
stars.gameObject.SetActive(false);
starRow.gameObject.SetActive(n >= 0);
for (int i = 0; i < 3; i++)
if (starImg[i] != null) starImg[i].sprite = UISkin.Icon(i < n ? "star_on" : "star_off") ?? starImg[i].sprite;
if (ribbon != null && !UISkin.Apply(ribbon, win ? "ribbon" : "ribbon_gray")) ribbon.color = win ? Color.white : UiKit.Disabled;
}
static string Mark(bool ok)
{
return ok ? "<color=#3a6a30>○</color>" : "<color=#a03a22>×</color>";

View File

@ -15,22 +15,25 @@ namespace TKS.OutGame
var hs = s.Save.Hero(h.id);
UiKit.Portrait("Face", cell.transform, h, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(12f, 8f), new Vector2(116f, 108f), false, 2f, new Color(0.9f, 0.86f, 0.8f));
int star = hs != null ? hs.star : h.rarity;
UiKit.Label(cell.transform, UiKit.Stars(star, star), 22, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(12f, 2f), new Vector2(116f, 30f));
if (UISkin.Icon("star_on") != null) UiKit.StarRow(cell.transform, star, star, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(14f, 2f), 24f);
else UiKit.Label(cell.transform, UiKit.Stars(star, star), 22, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(12f, 2f), new Vector2(116f, 30f));
UiKit.TypeBadge(cell.transform, h.type, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(142f, -16f), 40f);
UiKit.Label(cell.transform, owned ? "Lv " + (lv != null ? lv.lv : 1) : "미보유", 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(204f, -12f), new Vector2(150f, 48f));
UiKit.Title(UiKit.Label(cell.transform, owned ? "Lv " + (lv != null ? lv.lv : 1) : "미보유", 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(204f, -12f), new Vector2(150f, 48f)));
if (deployed)
{
var b = UiKit.Box("Deployed", cell.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-12f, -14f), new Vector2(124f, 44f), UiKit.Red);
UiKit.FillLabel(b.transform, "출전 중", 24, UiKit.White);
UiKit.Title(UiKit.FillLabel(b.transform, "출전 중", 24, UiKit.White), true);
}
if (hs != null)
{
var g = UiKit.Box("Grade", cell.transform, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-12f, 14f), new Vector2(56f, 40f), UiKit.GradeColor(hs.grade));
UiKit.FillLabel(g.transform, UiKit.GradeKo(hs.grade), 24, UiKit.White);
var g = UiKit.Box("Grade", cell.transform, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-12f, 14f), new Vector2(56f, 48f), UiKit.GradeColor(hs.grade));
UiKit.GradeFrame(g, hs.grade);
UiKit.Title(UiKit.FillLabel(g.transform, UiKit.GradeKo(hs.grade), 24, UiKit.White), true);
}
var name = UiKit.Label(cell.transform, h.nameKo, 44, owned ? UiKit.Ink : UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(142f, 12f), new Vector2(290f, 66f));
var name = UiKit.Title(UiKit.Label(cell.transform, h.nameKo, 44, owned ? UiKit.Ink : UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(142f, 12f), new Vector2(290f, 66f)));
name.horizontalOverflow = HorizontalWrapMode.Overflow;
if (owned && s.CanGrowAny(h.id)) UiKit.Dot("New", cell.transform, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 26f, UiKit.Red);
if (owned && s.CanGrowAny(h.id) && UiKit.Icon(cell.transform, "new_dot", new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 34f) == null)
UiKit.Dot("New", cell.transform, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 26f, UiKit.Red);
return cell;
}
}
@ -102,6 +105,9 @@ namespace TKS.OutGame
readonly Image[] slotImg = new Image[6];
readonly Text[] slotName = new Text[6];
readonly Text[] slotState = new Text[6];
readonly Image[] slotIcon = new Image[6];
readonly Image[] slotMark = new Image[6];
RectTransform starRow;
Image typeDot;
Text typeMark;
Text nameText;
@ -158,8 +164,24 @@ namespace TKS.OutGame
var b = UiKit.Btn("Slot" + i, card, "", 24, UiKit.Slot, UiKit.Ink, a, a, new Vector2(left ? 22f : -22f, -104f - row * 172f), new Vector2(196f, 156f), () => OnSlot(k));
slotImg[i] = b.GetComponent<Image>();
UiKit.LabelOf(b).text = "";
slotName[i] = UiKit.Label(b.transform, "", 30, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -12f), new Vector2(186f, 84f));
slotState[i] = UiKit.Label(b.transform, "", 24, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(186f, 44f), false);
if (UISkin.On)
{
// 팩 등급 칸(ItemFrame) + 장비 아이콘(테마 gear_<kind> · 임시) + 이름 한 줄 + 상태
slotIcon[i] = UiKit.Rect("GearIcon", b.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 8f), new Vector2(72f, 72f)).gameObject.AddComponent<Image>();
slotIcon[i].preserveAspect = true;
slotIcon[i].raycastTarget = false;
slotMark[i] = UiKit.Rect("Mark", b.transform, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-14f, -14f), new Vector2(44f, 44f)).gameObject.AddComponent<Image>();
slotMark[i].preserveAspect = true;
slotMark[i].raycastTarget = false;
slotName[i] = UiKit.Label(b.transform, "", 28, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -6f), new Vector2(186f, 40f));
slotState[i] = UiKit.Label(b.transform, "", 22, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(186f, 34f));
UISkin.Stroke(slotState[i], UiKit.White, 1.5f);
}
else
{
slotName[i] = UiKit.Label(b.transform, "", 30, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -12f), new Vector2(186f, 84f));
slotState[i] = UiKit.Label(b.transform, "", 24, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(186f, 44f), false);
}
}
var band = UiKit.Rect("NameBand", card, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), Vector2.zero, new Vector2(0f, 116f));
band.anchorMin = new Vector2(0f, 0f);
@ -173,6 +195,8 @@ namespace TKS.OutGame
nameText.horizontalOverflow = HorizontalWrapMode.Overflow;
subText = UiKit.Label(band, "", 28, new Color(0.85f, 0.8f, 0.75f), TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(420f, 0f), new Vector2(330f, 80f), false);
starText = UiKit.Label(band, "", 52, UiKit.Star, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(300f, 80f));
if (UISkin.Icon("star_on") != null) starRow = UiKit.Rect("StarRow", band, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-20f, 0f), new Vector2(300f, 60f));
UiKit.Title(nameText, true);
// ② 스킬 정보
var skill = UiKit.TopBand("Skill", body, 752f, 400f, 24f, 24f);
@ -205,8 +229,7 @@ namespace TKS.OutGame
// ④ 조각 · 진화
var shRow = UiKit.TopBand("Shard", body, 1290f, 96f, 24f, 24f);
var sd = UiKit.Dot("ShardDot", shRow, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0f, -14f), 54f, UiKit.Purple);
UiKit.FillLabel(sd.transform, "조", 28, UiKit.White);
UiKit.IconOrDot(shRow, "cur_shard", "조", UiKit.Purple, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(0f, -14f), 58f);
UiKit.Label(shRow, "영웅 조각", 32, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(64f, 0f), new Vector2(300f, 46f));
shardBar = UiKit.Bar("ShardBar", shRow, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(64f, 4f), new Vector2(690f, 42f), new Color(0.86f, 0.82f, 0.75f), UiKit.Purple);
shardText = UiKit.Label(shardBar.parent, "", 28, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(680f, 42f));
@ -250,6 +273,24 @@ namespace TKS.OutGame
slotImg[i].gameObject.SetActive(need != null);
if (need == null) continue;
slotName[i].text = g != null ? g.nameKo : need;
if (slotIcon[i] != null)
{
// 등급 칸 + 아이콘 · 장착 = 체크 · 가방 보유 = 알림 점 · 없음 = 아이콘 흐리게(글자 = 흰 + 외곽선)
UiKit.GradeFrame(slotImg[i], g != null ? g.grade : "white");
var gs = UISkin.Icon(g != null ? "gear_" + g.kind : null);
var ge = gs != null ? UISkin.Theme.IconEntryOf("gear_" + g.kind) : null;
slotIcon[i].sprite = gs;
slotIcon[i].enabled = gs != null;
var tint = ge != null && ge.samguk == null ? ge.tint : Color.white;
slotIcon[i].color = new Color(tint.r, tint.g, tint.b, on || have > 0 ? 1f : 0.4f);
slotMark[i].sprite = UISkin.Icon(on ? "check" : "new_dot");
slotMark[i].enabled = (on || have > 0) && slotMark[i].sprite != null;
slotMark[i].color = on ? Color.white : UISkin.Theme.IconEntryOf("new_dot") != null ? UISkin.Theme.IconEntryOf("new_dot").tint : UiKit.Red;
string gk = UiKit.GradeKo(g != null ? g.grade : "");
slotState[i].color = on ? UiKit.Ok : have > 0 ? UiKit.Red : UiKit.InkSoft;
slotState[i].text = on ? "장착 · " + gk : have > 0 ? "탭하여 장착 ×" + have : "없음 · " + gk;
continue;
}
if (on)
{
slotImg[i].color = new Color(0.9f, 0.95f, 0.86f);
@ -272,6 +313,13 @@ namespace TKS.OutGame
nameText.text = h.nameKo;
subText.text = UnitTypeInfo.Korean(h.type) + " · " + (h.isSoldier ? "병사 유닛" : UiKit.GradeKo(hs.grade) + " 등급");
starText.text = h.isSoldier ? UiKit.Stars(h.rarity, 3) : UiKit.Stars(hs.star, db.Evolve.MaxStar);
if (starRow != null)
{
starText.text = "";
UiKit.Clear(starRow);
int max = h.isSoldier ? 3 : db.Evolve.MaxStar;
UiKit.StarRow(starRow, h.isSoldier ? h.rarity : hs.star, max, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, 56f);
}
var skills = new[] { db.Skills.Get(h.skillActive), db.Skills.Get(h.skillPassive) };
for (int i = 0; i < 2; i++)

View File

@ -16,14 +16,34 @@ namespace TKS.OutGame
Header("본영", "로비", false);
var body = Body();
// 배경 = 테마 bg_lobby(키아트 04 · 어둡게 · 신규 제작 로비 배경이 오면 치환) · 없으면 땅 색 판
var art = UISkin.Icon("bg_lobby");
if (art != null)
{
var clip = UiKit.Fill("LobbyBg", body);
clip.gameObject.AddComponent<RectMask2D>();
// 위쪽 맞춤 × 1.22 — 키아트 아래쪽 아군 5인은 잘라 내고(편성 5인과 겹치지 않게) 전장·적진만 보이게
var art2 = UiKit.Rect("Art", clip, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, Vector2.zero);
art2.anchorMin = new Vector2(-0.11f, 1f);
art2.anchorMax = new Vector2(1.11f, 1f);
art2.offsetMin = art2.offsetMax = Vector2.zero;
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;
fit.aspectRatio = art.rect.width / Mathf.Max(1f, art.rect.height);
}
var chip = UiKit.Box("Progress", body, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -28f), new Vector2(760f, 84f), UiKit.Paper);
UiKit.Border(chip, UiKit.Line, 2f);
progress = UiKit.FillLabel(chip.transform, "", 34, UiKit.Ink);
// 전장 자리(배경 아트 없음 → 땅 색 판) · 출전 편성 5인
// 전장 자리 · 출전 편성 5인
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));
if (art == null) 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);
@ -32,28 +52,30 @@ namespace TKS.OutGame
var sortie = UiKit.Btn("Sortie", body, "", 40, UiKit.Red, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, y), new Vector2(440f, 400f), () => Root.OpenWorldMap());
UiKit.Border(sortie.GetComponent<Image>(), new Color(0.98f, 0.86f, 0.7f), 5f);
UiKit.LabelOf(sortie).text = "";
UiKit.Label(sortie.transform, "로비 중앙", 28, new Color(1f, 0.85f, 0.8f), TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -22f), new Vector2(400f, 50f), false);
UiKit.Label(sortie.transform, "출정", 120, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 30f), new Vector2(420f, 170f));
var si = UiKit.Icon(sortie.transform, "btn_sortie", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -26f), 92f);
if (si != null) UISkin.Stroke(si, UiKit.Stroke, 3f);
else UiKit.Label(sortie.transform, "로비 중앙", 28, new Color(1f, 0.85f, 0.8f), TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -22f), new Vector2(400f, 50f), false);
UiKit.Title(UiKit.Label(sortie.transform, "출정", 120, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 30f), new Vector2(420f, 170f)), true);
int stages = Root.Battle != null ? Root.Battle.Config.stages.Length : 0;
UiKit.Label(sortie.transform, "챕터 월드맵\n" + stages + " 스테이지", 34, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 30f), new Vector2(420f, 100f), false);
UiKit.Title(UiKit.Label(sortie.transform, "챕터 월드맵\n" + stages + " 스테이지", 34, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 34f), new Vector2(420f, 100f), false), true);
SideButton(body, "Formation", "편", "편성", "출정 좌측", -1, () => Root.Show(Root.Formation));
SideButton(body, "Arena", "연", "연무장", "출정 우측", 1, () =>
SideButton(body, "Formation", "btn_formation", "편", "편성", "출정 좌측", -1, () => Root.Show(Root.Formation));
SideButton(body, "Arena", "btn_arena", "연", "연무장", "출정 우측", 1, () =>
{
Root.Arena.SetPage(0);
Root.Show(Root.Arena); // 연무장(PD #1093 C)
});
}
void SideButton(RectTransform body, string name, string mark, string title, string sub, int side, System.Action onClick)
void SideButton(RectTransform body, string name, string icon, string mark, string title, string sub, int side, System.Action onClick)
{
float x = side * (220f + 160f);
var b = UiKit.Btn(name, body, "", 30, UiKit.Paper, UiKit.Ink, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(x, 70f), new Vector2(290f, 340f), onClick);
UiKit.Border(b.GetComponent<Image>(), UiKit.Line, 3f);
var dot = UiKit.Dot("Mark", b.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -36f), 110f, UiKit.Dark);
UiKit.FillLabel(dot.transform, mark, 50, UiKit.White);
UiKit.Label(b.transform, title, 52, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -40f), new Vector2(280f, 80f));
UiKit.Label(b.transform, sub, 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 30f), new Vector2(280f, 50f), false);
if (UiKit.Icon(dot.transform, icon, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 66f) == null) UiKit.FillLabel(dot.transform, mark, 50, UiKit.White);
UiKit.Title(UiKit.Label(b.transform, title, 52, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -40f), new Vector2(280f, 80f)));
UiKit.Label(b.transform, sub, 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 34f), new Vector2(280f, 50f), false);
}
public override void Refresh()

View File

@ -32,18 +32,24 @@ namespace TKS.OutGame
float x = 36f;
if (back)
{
UiKit.Btn("Back", band, "◀", 44, UiKit.Dark, UiKit.White, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(100f, 76f), () => Root.Back());
var b = UiKit.Btn("Back", band, "◀", 44, UISkin.On ? UiKit.Red : UiKit.Dark, UiKit.White, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(100f, 80f), () => Root.Back());
if (UiKit.Icon(b.transform, "arrow_back", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 6f), 50f) != null) UiKit.LabelOf(b).text = "";
x = 146f;
}
var t = UiKit.Label(band, title, 52, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(x, 0f), new Vector2(360f, 90f));
var t = UiKit.Title(UiKit.Label(band, title, 52, UiKit.HeaderText, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(x, 0f), new Vector2(360f, 90f)), true);
t.horizontalOverflow = HorizontalWrapMode.Overflow;
UiKit.Label(band, sub, 30, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(x + t.preferredWidth + 18f, -4f), new Vector2(700f, 60f), false);
UiKit.Label(band, sub, 30, UiKit.HeaderSub, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(x + t.preferredWidth + 18f, -4f), new Vector2(700f, 60f), false);
}
/// <summary>제목 띠 아래 ~ 하단 탭 위 본문.</summary>
/// <summary>
/// 제목 띠 아래 ~ 하단 탭 위 본문 — 기준(1080×1920)의 본문 높이보다 화면이 낮으면(예 1536×2048 = 3:4) 본문 전체를 같은 비율로 줄여 탭 바 밑으로 숨지 않게 한다(PD #1100 · 배치는 그대로).
/// </summary>
protected RectTransform Body(string name = "Body")
{
return UiKit.Fill(name, Rt, 0f, 0f, OutGameRoot.TopBarH + OutGameRoot.HeaderH, OutGameRoot.TabBarH);
var outer = UiKit.Fill(name, Rt, 0f, 0f, OutGameRoot.TopBarH + OutGameRoot.HeaderH, OutGameRoot.TabBarH);
var inner = UiKit.Rect("Fit", outer, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(OutGameRoot.Reference.x, OutGameRoot.BodyH));
inner.gameObject.AddComponent<UiFitHeight>().designHeight = OutGameRoot.BodyH;
return inner;
}
}
@ -60,6 +66,11 @@ namespace TKS.OutGame
public const float HeaderH = 104f;
public const float TabBarH = 150f;
public static readonly Vector2 Reference = new Vector2(1080f, 1920f);
/// <summary>기준 화면의 본문 높이(재화 바 · 제목 띠 · 하단 탭 제외).</summary>
public static float BodyH
{
get { return Reference.y - TopBarH - HeaderH - TabBarH; }
}
public OutGameSession Session { get; private set; }
public BattleManager Battle { get; private set; }
@ -246,7 +257,8 @@ namespace TKS.OutGame
for (int i = 0; i < tabImages.Count; i++)
{
bool on = scr.Tab == i;
tabImages[i].color = on ? UiKit.Red : UiKit.Dark;
if (UISkin.On) tabImages[i].gameObject.SetActive(on);
else tabImages[i].color = on ? UiKit.Red : UiKit.Dark;
}
scr.OnShow();
scr.Refresh();
@ -334,24 +346,32 @@ namespace TKS.OutGame
void BuildCurrencyBar()
{
barRoot = UiKit.TopBand("CurrencyBar", TopRoot, 0f, TopBarH);
UiKit.Paint(barRoot, new Color(0.15f, 0.11f, 0.09f), false, true);
goldText = Pill(0, "골", "골드", UiKit.Coin, () => Toast("골드 = 스테이지 보상 · 거래 > 상점 · (구매 상점 준비 중)"));
gemText = Pill(1, "보", "보옥", UiKit.Jade, () => Toast("보옥 구매(과금) = 준비 중 · 첫 클리어 · 보스 보상"));
staminaText = Pill(2, "군", "군량", UiKit.Grain, StaminaModal);
UiKit.Paint(barRoot, UISkin.On ? UiKit.BarColor : new Color(0.15f, 0.11f, 0.09f), false, true);
goldText = Pill(0, "cur_gold", "골", "골드", UiKit.Coin, () => Toast("골드 = 스테이지 보상 · 거래 > 상점 · (구매 상점 준비 중)"));
gemText = Pill(1, "cur_gem", "보", "보옥", UiKit.Jade, () => Toast("보옥 구매(과금) = 준비 중 · 첫 클리어 · 보스 보상"));
staminaText = Pill(2, "cur_stamina", "군", "군량", UiKit.Grain, StaminaModal);
staminaTimer = staminaText.transform.parent.Find("Name").GetComponent<Text>();
}
Text Pill(int i, string mark, string name, Color c, Action plus)
/// <summary>재화 칩(팩 ResourceBar · 아이콘 = 테마 cur_* · 「+」 = 팩 노란 단추).</summary>
Text Pill(int i, string icon, string mark, string name, Color c, Action plus)
{
float w = 336f;
var p = UiKit.Box("Pill" + i, barRoot, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(18f + i * (w + 18f), 0f), new Vector2(w, 84f), new Color(0.27f, 0.21f, 0.18f));
var dot = UiKit.Dot("Icon", p.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(8f, 0f), 64f, c);
UiKit.FillLabel(dot.transform, mark, 32, UiKit.White);
var n = UiKit.Label(p.transform, name, 26, new Color(0.85f, 0.8f, 0.75f), TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(80f, 0f), new Vector2(110f, 60f), false);
var p = UiKit.Rect("Pill" + i, barRoot, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(18f + i * (w + 18f), 0f), new Vector2(w, 84f));
UiKit.Skin(p, "pill", UISkin.Col("pill", new Color(0.27f, 0.21f, 0.18f)));
UiKit.IconOrDot(p, icon, mark, c, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(-4f, 0f), 76f);
var n = UiKit.Label(p, name, 26, UISkin.Col("bar_text_off", new Color(0.85f, 0.8f, 0.75f)), TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(80f, 0f), new Vector2(110f, 60f), false);
n.gameObject.name = "Name";
var v = UiKit.Label(p.transform, "", 34, UiKit.White, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-70f, 0f), new Vector2(170f, 60f));
if (i != 2 && UISkin.Icon(icon) != null) n.text = ""; // 아이콘이 이름을 대신(군량 칸은 회복 타이머 자리)
var v = UiKit.Title(UiKit.Label(p, "", 36, UISkin.Col("bar_text", UiKit.White), TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-70f, 0f), new Vector2(170f, 60f)), true);
v.horizontalOverflow = HorizontalWrapMode.Overflow;
UiKit.Btn("Plus", p.transform, "+", 40, new Color(0.42f, 0.35f, 0.31f), UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-8f, 0f), new Vector2(56f, 56f), plus);
if (UISkin.Has("plus"))
{
var pb = UiKit.Rect("Plus", p, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-6f, 0f), new Vector2(60f, 62f));
UiKit.Skin(pb, "plus", Color.white, true);
pb.gameObject.AddComponent<Button>().onClick.AddListener(() => plus());
}
else UiKit.Btn("Plus", p, "+", 40, new Color(0.42f, 0.35f, 0.31f), UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-8f, 0f), new Vector2(56f, 56f), plus);
return v;
}
@ -375,9 +395,12 @@ namespace TKS.OutGame
bar.anchorMax = new Vector2(1f, 0f);
bar.offsetMin = new Vector2(0f, 0f);
bar.offsetMax = new Vector2(0f, TabBarH);
UiKit.Paint(bar, UiKit.Dark, false, true);
UiKit.Paint(bar, UISkin.On ? UiKit.BarColor : UiKit.Dark, false, true);
if (UISkin.On) UiKit.Box("TopLine", bar, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(Reference.x * 2f, 6f), UiKit.Line, false);
string[] names = { "영웅", "보물", "내정", "동맹", "거래" };
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));
for (int i = 0; i < names.Length; i++)
{
int k = i;
@ -385,12 +408,33 @@ namespace TKS.OutGame
cell.anchorMin = new Vector2(i / 5f, 0f);
cell.anchorMax = new Vector2((i + 1) / 5f, 1f);
cell.offsetMin = cell.offsetMax = Vector2.zero;
var img = UiKit.Paint(cell, UiKit.Dark, false, true);
var hit = cell.gameObject.AddComponent<Image>();
hit.color = new Color(0f, 0f, 0f, 0f);
var btn = cell.gameObject.AddComponent<Button>();
btn.onClick.AddListener(() => OnTab(k));
var dot = UiKit.Dot("Icon", cell, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -18f), 66f, i == 3 ? new Color(0.35f, 0.3f, 0.28f) : new Color(0.38f, 0.31f, 0.28f));
UiKit.FillLabel(dot.transform, marks[i], 32, i == 3 ? new Color(0.7f, 0.66f, 0.62f) : UiKit.White);
var t = UiKit.Label(cell, names[i], 32, i == 3 ? new Color(0.62f, 0.58f, 0.55f) : UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 14f), new Vector2(200f, 48f));
Image img;
if (UISkin.On)
{
// 선택 탭 = 팩 둥근 판(붉은 옻칠 틴트 · 위로 솟음) · 아이콘 = 테마 tab_*(흰 실루엣)
var focus = UiKit.Fill("Focus", cell, 8f, 8f, -18f, 6f);
img = UiKit.Skin(focus, "tab_focus", UiKit.Red);
var ic = UiKit.Icon(cell, icons[i], new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -14f), 74f);
if (ic != null)
{
if (i == 3) ic.color = off;
UISkin.Stroke(ic, UiKit.Line, 3f);
}
else UiKit.FillLabel(UiKit.Dot("Icon", cell, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -18f), 66f, UISkin.Col("tab_off", UiKit.Dark)).transform, marks[i], 32, UiKit.White);
}
else
{
img = hit;
img.color = UiKit.Dark;
var dot = UiKit.Dot("Icon", cell, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -18f), 66f, i == 3 ? new Color(0.35f, 0.3f, 0.28f) : new Color(0.38f, 0.31f, 0.28f));
UiKit.FillLabel(dot.transform, marks[i], 32, i == 3 ? new Color(0.7f, 0.66f, 0.62f) : UiKit.White);
}
var t = 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, i != 3);
tabImages.Add(img);
tabTexts.Add(t);
}
@ -441,16 +485,35 @@ namespace TKS.OutGame
{
CloseModal();
var dim = UiKit.Fill("Modal", TopRoot);
var dimImg = UiKit.Paint(dim, new Color(0f, 0f, 0f, 0.6f), false, true);
var dimImg = UiKit.Paint(dim, UISkin.Col("dim", new Color(0f, 0f, 0f, 0.6f)), false, true);
dimImg.raycastTarget = true;
modal = dim.gameObject;
float h = height > 0f ? height : 600f;
var panel = UiKit.Box("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f), new Vector2(940f, h), UiKit.Paper);
panel.raycastTarget = true;
UiKit.Border(panel, UiKit.Line, 4f);
var head = UiKit.TopBand("Head", panel.transform, 0f, 110f);
UiKit.Paint(head, UiKit.Dark);
UiKit.FillLabel(head, title, 44, UiKit.White);
Image panel;
RectTransform head;
if (UISkin.Has("popup"))
{
// 팩 팝업: 흰 틀 + 위 제목 띠(진남 틴트 · 광택) + 닫기 X
panel = UiKit.Skin(UiKit.Rect("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f), new Vector2(940f, h)), "popup", UiKit.Paper, true);
head = UiKit.TopBand("Head", panel.transform, 6f, 110f, 7f, 7f);
UiKit.Skin(head, "popup_head", UiKit.Header);
var tt = UiKit.Title(UiKit.FillLabel(head, title, 44, UiKit.HeaderText, TextAnchor.MiddleCenter, true, 110f), true);
tt.resizeTextForBestFit = true;
tt.resizeTextMinSize = 28;
tt.resizeTextMaxSize = 44;
var x = UiKit.Rect("Close", panel.transform, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-58f, -60f), new Vector2(86f, 88f));
UiKit.Skin(x, "close", Color.white, true);
x.gameObject.AddComponent<Button>().onClick.AddListener(CloseModal);
}
else
{
panel = UiKit.Box("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f), new Vector2(940f, h), UiKit.Paper);
panel.raycastTarget = true;
UiKit.Border(panel, UiKit.Line, 4f);
head = UiKit.TopBand("Head", panel.transform, 0f, 110f);
UiKit.Paint(head, UiKit.Dark);
UiKit.FillLabel(head, title, 44, UiKit.White);
}
var content = UiKit.Fill("Content", panel.transform, 40f, 40f, 130f, 160f);
int n = Mathf.Max(1, buttons.Length);
float bw = Mathf.Min(400f, (860f - (n - 1) * 20f) / n);
@ -479,7 +542,9 @@ namespace TKS.OutGame
{
if (toast == null)
{
var img = UiKit.Box("Toast", TopRoot, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, TabBarH + 40f), new Vector2(940f, 110f), new Color(0.1f, 0.08f, 0.07f, 0.92f));
var img = UISkin.Has("toast")
? UiKit.Skin(UiKit.Rect("Toast", TopRoot, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, TabBarH + 40f), new Vector2(940f, 110f)), "toast", UiKit.BarColor)
: UiKit.Box("Toast", TopRoot, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, TabBarH + 40f), new Vector2(940f, 110f), new Color(0.1f, 0.08f, 0.07f, 0.92f));
toast = img.gameObject;
toastText = UiKit.FillLabel(img.transform, "", 32, UiKit.White, TextAnchor.MiddleCenter, false, 16f);
}

View File

@ -104,10 +104,11 @@ namespace TKS.OutGame
{
var g = S.Db.Gacha;
var banner = UiKit.TopBand("Banner", p, 12f, 440f, 24f, 24f);
UiKit.Paint(banner, new Color(0.27f, 0.14f, 0.12f));
if (UISkin.Has("banner")) UiKit.Skin(banner, "banner", Color.white); // 팩 배너 틀(PD #1100)
else UiKit.Paint(banner, new Color(0.27f, 0.14f, 0.12f));
var pick = UiKit.Box("Pickup", banner, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -24f), new Vector2(230f, 62f), UiKit.Red);
UiKit.FillLabel(pick.transform, "픽업 (2주)", 30, UiKit.White);
UiKit.Label(banner, "영웅·유닛 뽑기", 60, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -110f), new Vector2(600f, 90f));
UiKit.Title(UiKit.Label(banner, "영웅·유닛 뽑기", 60, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -110f), new Vector2(600f, 90f)), true);
UiKit.Label(banner, "모든 영웅·병사 유닛\n= 뽑기로 획득", 32, new Color(0.9f, 0.82f, 0.78f), TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -210f), new Vector2(520f, 110f), false);
var free = UiKit.Box("Free", banner, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(36f, 28f), new Vector2(300f, 78f), new Color(0.93f, 0.75f, 0.24f));
UiKit.FillLabel(free.transform, "첫 10연 무료", 34, UiKit.Ink);
@ -370,7 +371,9 @@ namespace TKS.OutGame
var img = UiKit.Paint(row, UiKit.Paper);
UiKit.Border(img, UiKit.Line, 3f);
var icon = UiKit.Box("Icon", row, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), new Vector2(170f, 140f), TreasureScreen.ChestColor(c.id));
UiKit.FillLabel(icon.transform, "×" + TreasureRules.ChestCount(d, c.id), 40, UiKit.White);
if (UiKit.Icon(icon.transform, "chest_" + c.id, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 8f), 120f) != null)
UiKit.Title(UiKit.Label(icon.transform, "×" + TreasureRules.ChestCount(d, c.id), 36, UiKit.White, TextAnchor.LowerRight, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-8f, 2f), new Vector2(150f, 50f)), true);
else UiKit.FillLabel(icon.transform, "×" + TreasureRules.ChestCount(d, c.id), 40, UiKit.White);
var probs = new List<string>();
for (int g = 0; g < ChestDb.ProbGrades.Length; g++)
if (c.p[g] > 0f) probs.Add(TreasureRules.GradeKo(ChestDb.ProbGrades[g]) + " " + (c.p[g] * 100f).ToString("0.#") + "%");

View File

@ -0,0 +1,45 @@
using UnityEngine;
namespace TKS.OutGame
{
/// <summary>
/// 본문 높이 맞춤(PD #1100) — 부모가 기준 높이보다 낮으면(3:4 등) 같은 비율로 줄이고 가로는 부모를 꽉 채운다 · 높거나 같으면 부모와 같은 크기(기존 늘림과 같음).
/// </summary>
public class UiFitHeight : MonoBehaviour
{
public float designHeight = 1546f;
Vector2 last = new Vector2(-1f, -1f);
void OnEnable()
{
last = new Vector2(-1f, -1f);
Apply();
}
void Update()
{
Apply();
}
public float Scale
{
get { return transform.localScale.x; }
}
void Apply()
{
var rt = (RectTransform)transform;
var parent = rt.parent as RectTransform;
if (parent == null || designHeight <= 0f) return;
var size = parent.rect.size;
if (size == last || size.y <= 0f) return;
last = size;
float s = Mathf.Min(1f, size.y / designHeight);
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 1f);
rt.pivot = new Vector2(0.5f, 1f);
rt.localScale = new Vector3(s, s, 1f);
rt.sizeDelta = s < 1f ? new Vector2(size.x / s, designHeight) : size;
rt.anchoredPosition = Vector2.zero;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fa2cf3b7e2917044da9dd68acfd5a663

View File

@ -7,29 +7,40 @@ using UnityEngine.UI;
namespace TKS.OutGame
{
/// <summary>
/// 아웃게임 와이어프레임 UI 도우미 — uGUI + Malgun Gothic(TextUtil) · 아트 없음 = 도형 + 글자 · 기준 1080×1920 · 색 = 목업 img03·06·08·11.
/// 아웃게임 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
{
public static readonly Color Bg = new Color(0.95f, 0.92f, 0.85f);
public static readonly Color Paper = new Color(0.99f, 0.97f, 0.92f);
public static readonly Color Header = new Color(0.92f, 0.87f, 0.78f);
public static readonly Color Ink = new Color(0.17f, 0.12f, 0.10f);
public static readonly Color InkSoft = new Color(0.45f, 0.39f, 0.34f);
public static readonly Color Dark = new Color(0.21f, 0.16f, 0.14f);
public static readonly Color Red = new Color(0.74f, 0.26f, 0.19f);
public static readonly Color Line = new Color(0.33f, 0.26f, 0.22f);
public static readonly Color Slot = new Color(0.80f, 0.76f, 0.70f);
public static readonly Color Coin = new Color(0.88f, 0.67f, 0.20f);
public static readonly Color Jade = new Color(0.27f, 0.60f, 0.52f);
public static readonly Color Grain = new Color(0.76f, 0.58f, 0.38f);
public static readonly Color Ok = new Color(0.36f, 0.62f, 0.31f);
public static readonly Color Star = new Color(0.93f, 0.68f, 0.13f);
public static readonly Color Disabled = new Color(0.66f, 0.62f, 0.58f);
public static readonly Color Purple = new Color(0.58f, 0.36f, 0.70f);
// 팔레트 = 테마 색 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>
@ -111,11 +122,14 @@ namespace TKS.OutGame
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)
if (rounded && !UISkin.Apply(img, c.a >= 0.95f ? "card" : "soft"))
{
img.sprite = Round;
img.type = Image.Type.Sliced;
@ -124,6 +138,44 @@ namespace TKS.OutGame
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);
@ -132,15 +184,18 @@ namespace TKS.OutGame
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>();
img.sprite = Circle;
if (d > 280f || !UISkin.Apply(img, "dot")) img.sprite = Circle; // 큰 원(후광 등)은 팩 외곽선이 흐려져서 평면 원
img.preserveAspect = true;
img.color = c;
img.raycastTarget = false;
return img;
}
/// <summary>테두리(외곽선 효과 · 둥근 상자에 굵은 선처럼 보임).</summary>
/// <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);
@ -181,14 +236,22 @@ namespace TKS.OutGame
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 = Box(name, parent, anchor, pivot, pos, size, bg);
img.raycastTarget = true;
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());
FillLabel(img.transform, label, fontSize, fg, TextAnchor.MiddleCenter, true, 6f);
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;
}
@ -200,8 +263,19 @@ namespace TKS.OutGame
/// <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)
{
var back = Box(name, parent, anchor, pivot, pos, size, bg);
var fill = Fill("Fill", back.transform);
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;
@ -278,15 +352,36 @@ namespace TKS.OutGame
{
switch (t)
{
case UnitType.Infantry: return new Color(0.62f, 0.33f, 0.22f);
case UnitType.Spear: return new Color(0.30f, 0.50f, 0.30f);
case UnitType.Cavalry: return new Color(0.28f, 0.32f, 0.45f);
case UnitType.Archer: return new Color(0.70f, 0.52f, 0.20f);
case UnitType.Strategist: return Purple;
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)
{
@ -306,16 +401,42 @@ namespace TKS.OutGame
{
switch (g)
{
case "white": return new Color(0.80f, 0.79f, 0.76f);
case "green": return new Color(0.38f, 0.64f, 0.33f);
case "blue": return new Color(0.29f, 0.48f, 0.78f);
case "purple": return Purple;
case "orange": return new Color(0.90f, 0.50f, 0.18f);
case "gold": return new Color(0.92f, 0.74f, 0.16f);
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();
@ -350,12 +471,30 @@ namespace TKS.OutGame
/// </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 rt = Rect(name, parent, anchor, pivot, pos, size);
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 = frame;
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
else if (!framed || !UISkin.Apply(bg, "portrait_mask"))
{
bg.sprite = Round;
bg.type = Image.Type.Sliced;
@ -380,16 +519,18 @@ namespace TKS.OutGame
{
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));
FillLabel(dot.transform, UnitTypeInfo.Mark(h.type), Mathf.RoundToInt(d * 0.5f), White);
Title(FillLabel(dot.transform, UnitTypeInfo.Mark(h.type), Mathf.RoundToInt(d * 0.5f), White), true);
}
return rt;
return outer;
}
/// <summary>병종 배지(어두운 둥근 칸 + 글자).</summary>
/// <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), Dark);
FillLabel(b.transform, UnitTypeInfo.Mark(t), Mathf.RoundToInt(h * 0.62f), White);
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;
}
}

View File

@ -0,0 +1,169 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace TKS.OutGame
{
/// <summary>
/// UI 리뉴얼 검증 캡처(PD #1100 · 에디터 DataImporter.CaptureUI 가 SessionState 로 출력 폴더를 넘기면 생성 · 세이브 = 메모리 전용 신규 유저).
/// 1080×1920 = 로비 · 영웅 목록 · 영웅 상세 · 편성 · 거래 · 보물 · 내정 · 연무장 · 월드맵 팝업 · 전투 HUD · 전투 결과 · 팝업 + 1536×2048 = 로비 · 영웅 상세(겹침 확인) · 끝나면 스스로 종료.
/// </summary>
public class UICaptureDriver : MonoBehaviour
{
public const string DirKey = "TKS_UI_CAPTURE_DIR";
public string OutputDir;
const int W = 1080, H = 1920, W2 = 1536, H2 = 2048;
readonly List<string> log = new List<string>();
int shot = 1;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void AutoSpawn()
{
string dir = UnityEditor.SessionState.GetString(DirKey, "");
if (string.IsNullOrEmpty(dir)) return;
UnityEditor.SessionState.EraseString(DirKey);
var d = new GameObject("UICaptureDriver").AddComponent<UICaptureDriver>();
d.OutputDir = dir;
}
#endif
IEnumerator Start()
{
Directory.CreateDirectory(OutputDir);
yield return null;
yield return null;
var root = OutGameRoot.Instance;
var cam = Camera.main;
if (root == null || cam == null)
{
Log("FAIL: OutGameRoot 또는 카메라 없음");
Finish(1);
yield break;
}
var s = root.Session;
var db = s.Db;
var d = s.Save;
Log("theme=" + (UITheme.Current != null) + " titleFont=" + (UISkin.TitleFont != null ? UISkin.TitleFont.name : "-") + " memoryOnly=" + SaveStore.MemoryOnly);
// 캡처용 진행(메모리 세이브만): 재화 · 장비 · 보물 · 상자 · 순찰
d.gold = 124500;
d.gem = 1280;
var id = db.Economy.StartHeroes.Length > 1 ? db.Economy.StartHeroes[1] : db.Economy.StartHeroes[0];
var h = s.Def(id);
var hs = s.Save.Hero(id);
for (int i = 0; i < 4; i++) d.inv.AddGear(HeroRules.RequiredGear(db, h, hs, i), 1);
for (int i = 0; i < 3; i++) s.Equip(id, i);
HeroRules.GrantExp(hs, db.Levels.Get(1).expToNext + 5);
var rng = new System.Random(1100);
Counts.Set(d.inv.chests, "gold", 6);
for (int i = 0; i < 6; i++) TreasureRules.Open(db, d, "gold", rng);
Counts.Set(d.inv.chests, "bronze", 4);
Counts.Set(d.inv.chests, "silver", 18);
Counts.Set(d.inv.chests, "general", 1);
d.inv.chestPoints = 35;
d.patrol.startAt = SaveStore.Iso(s.Now.AddHours(-7));
s.Commit();
root.ShowLobby();
yield return Shot(cam, "lobby", W, H);
root.Show(root.HeroList);
yield return Shot(cam, "hero_list", W, H);
root.ShowHero(id);
yield return Shot(cam, "hero_detail", W, H);
root.Show(root.Formation);
yield return Shot(cam, "formation", W, H);
root.Show(root.Trade);
root.Trade.SetPage(0);
yield return Shot(cam, "trade_gacha", W, H);
root.Show(root.Treasures);
root.Treasures.SetPage(2);
root.Treasures.SelectChest("gold");
yield return Shot(cam, "treasure_chests", W, H);
root.Treasures.SetPage(0);
yield return Shot(cam, "treasure_owned", W, H);
root.Show(root.Domestic);
root.Domestic.SetTrainType(UnitType.Infantry);
root.Domestic.SetPage(1);
yield return Shot(cam, "domestic_patrol", W, H);
root.Arena.SetPage(0);
root.Show(root.Arena);
yield return Shot(cam, "arena", W, H);
root.StaminaModal();
yield return Shot(cam, "popup_stamina", W, H);
root.CloseModal();
root.ShowLobby();
root.ShowHero(id);
yield return Shot(cam, "hero_detail_1536x2048", W2, H2);
root.ShowLobby();
yield return Shot(cam, "lobby_1536x2048", W2, H2);
root.OpenWorldMap();
yield return Wait();
var wm = root.Map;
wm.OpenPopup(0);
yield return Shot(cam, "worldmap_popup", W, H);
wm.ConfirmPopup();
var bm = root.Battle;
float t0 = Time.realtimeSinceStartup;
while (bm.Phase != BattlePhase.PlayerTurn && Time.realtimeSinceStartup - t0 < 10f) yield return null;
yield return new WaitForSecondsRealtime(0.6f);
Unit pick = null;
foreach (var u in bm.AliveUnits(Faction.Player))
if (bm.Skills != null && bm.Skills.Of(u) != null && bm.Skills.Of(u).Active != null)
{
pick = u;
break;
}
if (pick != null) bm.Sling.Select(pick);
Log("hud select=" + (pick != null ? pick.DisplayName : "-") + " skill=" + bm.Hud.SkillButtonText);
yield return Shot(cam, "battle_hud", W, H);
foreach (var e in bm.AliveUnits(Faction.Enemy)) e.TakeDamage(e.Hp);
bm.CheckBattleEnd();
yield return new WaitForSecondsRealtime(0.6f);
yield return Shot(cam, "battle_result", W, H);
Finish(0);
}
static IEnumerator Wait()
{
yield return null;
yield return new WaitForSecondsRealtime(0.3f);
}
IEnumerator Shot(Camera cam, string name, int w, int h)
{
yield return Wait();
string file = Path.Combine(OutputDir, shot.ToString("00") + "_" + name + ".png");
shot++;
yield return CaptureUtil.Capture(cam, w, h, file);
Log("shot " + file);
}
void Log(string s)
{
log.Add(s);
Debug.Log("[UICapture] " + s);
try
{
File.WriteAllLines(Path.Combine(OutputDir, "capture_log.txt"), log.ToArray());
}
catch
{
}
}
void Finish(int code)
{
Log("finish code=" + code);
#if UNITY_EDITOR
UnityEditor.EditorApplication.Exit(code);
#else
Application.Quit(code);
#endif
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b6ebc17a46c965c4790f198d64167444

View File

@ -8,10 +8,11 @@ namespace TKS
/// <summary>화면 HUD(uGUI · 코드 생성): 스테이지·턴·유닛 수·안내·선택 유닛 정보·미니맵·스킬 버튼(PD #1093 B)·스킬 발동 한 줄·결과 오버레이.</summary>
public class BattleHUD : MonoBehaviour
{
static readonly Color SkillReadyColor = new Color(0.72f, 0.2f, 0.14f, 0.94f);
static readonly Color SkillReservedColor = new Color(0.96f, 0.76f, 0.22f, 0.97f);
static readonly Color SkillOffColor = new Color(0.3f, 0.3f, 0.33f, 0.88f);
static readonly Color SkillPassiveOnlyColor = new Color(0.18f, 0.2f, 0.26f, 0.85f);
// 색 = UI 테마(PD #1100 · ui_theme.csv skill_*) · 괄호 값 = 테마 없을 때
static Color SkillReadyColor { get { return UISkin.Col("skill_ready", new Color(0.72f, 0.2f, 0.14f, 0.94f)); } }
static Color SkillReservedColor { get { return UISkin.Col("skill_reserved", new Color(0.96f, 0.76f, 0.22f, 0.97f)); } }
static Color SkillOffColor { get { return UISkin.Col("skill_off", new Color(0.3f, 0.3f, 0.33f, 0.88f)); } }
static Color SkillPassiveOnlyColor { get { return UISkin.Col("skill_passive", new Color(0.18f, 0.2f, 0.26f, 0.85f)); } }
static readonly Color SkillLogColor = new Color(0.6f, 1f, 0.85f);
const float SkillLogSeconds = 3f;
@ -30,6 +31,8 @@ namespace TKS
Unit selected;
Image skillPanel;
Image skillCool;
Image turnBadge;
Text skillName;
Text skillState;
Text skillPassive;
@ -62,6 +65,15 @@ namespace TKS
es.AddComponent<StandaloneInputModule>();
}
if (UISkin.On)
{
// 팩 칩(PD #1100): 스테이지 · 유닛 수 · 턴 띠 · 선택 유닛 정보 판 — 글자 뒤(먼저 만든다) · 반투명 진남
Color panelCol = UISkin.Col("hud_panel", new Color(0.12f, 0.15f, 0.21f, 0.85f));
Chip("StageChip", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(14f, -14f), new Vector2(730f, 86f), panelCol);
Chip("CountChip", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-14f, -14f), new Vector2(330f, 86f), panelCol);
turnBadge = Chip("TurnBadge", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -106f), new Vector2(440f, 84f), UISkin.Col("hud_turn_player", Color.blue));
Chip("UnitPanel", new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(14f, 96f), new Vector2(790f, 196f), panelCol);
}
stageText = MakeText("Stage", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -28f), new Vector2(700f, 70f), 40, TextAnchor.UpperLeft);
// 스테이지 표 제목(최대 ~20자)이 오른쪽 위 적 수와 겹치지 않게 칸 안에서 글자 크기를 줄인다
stageText.horizontalOverflow = HorizontalWrapMode.Wrap;
@ -72,8 +84,15 @@ namespace TKS
countText = MakeText("Count", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-30f, -28f), new Vector2(520f, 70f), 36, TextAnchor.UpperRight);
turnText = MakeText("Turn", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -92f), new Vector2(700f, 70f), 46, TextAnchor.UpperCenter);
turnText.color = new Color(1f, 0.95f, 0.6f);
if (UISkin.On)
{
turnText.color = Color.white;
turnText.font = UISkin.TitleFont;
turnText.rectTransform.anchoredPosition = new Vector2(0f, -114f);
}
messageText = MakeText("Message", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -160f), new Vector2(1040f, 60f), 30, TextAnchor.UpperCenter);
messageText.color = new Color(0.9f, 0.95f, 1f);
if (UISkin.On) messageText.rectTransform.anchoredPosition = new Vector2(0f, -206f); // 턴 띠 아래
hintText = MakeText("Hint", new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 36f), new Vector2(1040f, 60f), 28, TextAnchor.LowerCenter);
hintText.color = new Color(0.85f, 0.85f, 0.85f);
@ -94,6 +113,37 @@ namespace TKS
skillPassive.transform.SetParent(skillPanel.transform, false);
skillPassive.horizontalOverflow = HorizontalWrapMode.Wrap;
skillPassive.color = new Color(0.8f, 0.9f, 1f);
if (UISkin.Apply(skillPanel, "skill_btn"))
{
// 원형 스킬 버튼(팩 Button_Circle · PD #1100) + 쿨 라디얼 · 이름·상태 = 원 안 · 패시브 한 줄 = 원 위
var rt = skillPanel.rectTransform;
rt.sizeDelta = new Vector2(250f, 250f);
rt.anchoredPosition = new Vector2(-24f, 100f);
skillPanel.preserveAspect = true;
skillCool = MakeImage("Cool", skillPanel.transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(-26f, -26f), UISkin.Col("skill_cool", new Color(0f, 0f, 0f, 0.6f)));
if (UISkin.Apply(skillCool, "skill_cool"))
{
skillCool.type = Image.Type.Filled;
skillCool.fillMethod = Image.FillMethod.Radial360;
skillCool.fillOrigin = (int)Image.Origin360.Top;
skillCool.fillClockwise = false;
skillCool.fillAmount = 0f;
}
skillCool.transform.SetAsFirstSibling();
Place(skillName, new Vector2(0f, -54f), new Vector2(196f, 56f));
skillName.font = UISkin.TitleFont;
Place(skillState, new Vector2(0f, -10f), new Vector2(200f, 80f));
skillState.resizeTextForBestFit = true;
skillState.verticalOverflow = VerticalWrapMode.Truncate;
skillState.resizeTextMinSize = 18;
skillState.resizeTextMaxSize = 26;
var pr = skillPassive.rectTransform;
pr.anchorMin = pr.anchorMax = new Vector2(0.5f, 1f);
pr.pivot = new Vector2(0.5f, 0f);
pr.anchoredPosition = new Vector2(-20f, 6f);
pr.sizeDelta = new Vector2(320f, 40f);
skillPassive.alignment = TextAnchor.LowerCenter;
}
skillPanel.gameObject.SetActive(false);
// 스킬 발동 한 줄(액티브·패시브 · 최근 몇 초)
@ -113,6 +163,7 @@ namespace TKS
resultOverlay = MakeImage("ResultOverlay", transform, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero, new Color(0f, 0f, 0f, 0.65f)).gameObject;
resultTitle = MakeText("Title", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 60f), new Vector2(1000f, 160f), 110, TextAnchor.MiddleCenter);
resultTitle.transform.SetParent(resultOverlay.transform, false);
if (UISkin.On) resultTitle.font = UISkin.TitleFont;
resultSub = MakeText("Sub", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -80f), new Vector2(1000f, 120f), 40, TextAnchor.MiddleCenter);
resultSub.transform.SetParent(resultOverlay.transform, false);
resultOverlay.SetActive(false);
@ -158,6 +209,20 @@ namespace TKS
return img;
}
/// <summary>테마 칩(팩 카드 틀 · 색 틴트) — 글자보다 먼저 만들어 뒤에 깔린다.</summary>
Image Chip(string name, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size, Color color)
{
var img = MakeImage(name, transform, anchor, anchor, pivot, pos, size, color);
UISkin.Apply(img, "card");
return img;
}
static void Place(Text t, Vector2 pos, Vector2 size)
{
t.rectTransform.anchoredPosition = pos;
t.rectTransform.sizeDelta = size;
}
public void SetSelected(Unit u)
{
selected = u;
@ -207,6 +272,12 @@ namespace TKS
bool show = s != null && (s.Active != null || s.Passive != null);
if (skillPanel.gameObject.activeSelf != show) skillPanel.gameObject.SetActive(show);
if (!show) return;
if (skillCool != null)
{
// 쿨 라디얼 = 남은 턴 / 쿨(쿨 중일 때만)
bool cooling = s.Active != null && selected.Faction == Faction.Player && sys.StateOf(selected) == SkillSystem.ActiveState.Cooldown && s.Active.Cooldown > 0;
skillCool.fillAmount = cooling ? Mathf.Clamp01(s.CooldownLeft(battle.TurnNumber) / (float)s.Active.Cooldown) : 0f;
}
skillPassive.text = s.Passive != null ? "패 · " + s.Passive.NameKo + (s.PassiveMult > 1f ? " ★" : "") : "";
if (s.Active == null || selected.Faction != Faction.Player)
@ -296,6 +367,14 @@ namespace TKS
default: turnText.text = ""; break;
}
messageText.text = battle.LastMessage;
if (turnBadge != null)
{
bool on = turnText.text.Length > 0;
if (turnBadge.gameObject.activeSelf != on) turnBadge.gameObject.SetActive(on);
turnBadge.color = battle.Phase == BattlePhase.EnemyTurn || battle.Phase == BattlePhase.Defeat
? UISkin.Col("hud_turn_enemy", Color.red)
: UISkin.Col("hud_turn_player", Color.blue);
}
if (battle.Skills != null && battle.Skills.PendingTeleport != null)
{

View File

@ -127,6 +127,14 @@ namespace TKS
var p = ProceduralSprites.NewSprite(body, "Pip", ProceduralSprites.Square, Color.white, 1f, LabelOrder);
p.transform.localPosition = new Vector3(-total * 0.5f + w * 0.5f + i * (w + gap), top, 0f);
p.transform.localScale = new Vector3(w, 0.1f, 1f);
var ps = UISkin.WorldSprite("pip", 460f); // 팩 둥근 칸 핍(PD #1100 · 외곽선)
if (ps != null)
{
p.sprite = ps;
p.drawMode = SpriteDrawMode.Sliced;
p.transform.localScale = Vector3.one;
p.size = new Vector2(w, 0.12f);
}
pips.Add(p);
}
}
@ -236,7 +244,7 @@ namespace TKS
if (label != null) label.text = text;
for (int i = 0; i < pips.Count; i++)
{
pips[i].color = i < g.Hp ? new Color(1f, 0.85f, 0.3f) : new Color(0.15f, 0.15f, 0.15f, 0.8f);
pips[i].color = i < g.Hp ? UISkin.Col("pip_on", new Color(1f, 0.85f, 0.3f)) : UISkin.Col("pip_off", new Color(0.15f, 0.15f, 0.15f, 0.8f));
pips[i].gameObject.SetActive(!g.Broken);
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8d3e18def507e7d43a5def42775218f9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,148 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TKS
{
/// <summary>
/// UI 테마 적용 도우미(PD #1100) — 아웃게임 UiKit · 전투 HUD · 월드맵 팝업이 같이 쓴다.
/// 테마가 없으면 모든 함수가 false/null 을 돌려주고 호출부는 기존 도형으로 그린다.
/// </summary>
public static class UISkin
{
public const string OverlayName = "Gloss";
public static UITheme Theme
{
get { return UITheme.Current; }
}
public static bool On
{
get { return Theme != null; }
}
public static Color Col(string id, Color fallback)
{
Color c;
return Theme != null && Theme.TryColor(id, out c) ? c : fallback;
}
public static Font TitleFont
{
get { return Theme != null && Theme.titleFont != null ? Theme.titleFont : TextUtil.Font; }
}
public static bool Has(string skinId)
{
return Theme != null && Theme.Skin(skinId) != null;
}
/// <summary>이미지에 스킨(9-slice 바탕 + 덮개) 적용 · 색은 호출부(img.color)가 정한다.</summary>
public static bool Apply(Image img, string skinId)
{
var s = Theme != null ? Theme.Skin(skinId) : null;
if (s == null || img == null) return false;
var sp = s.Sprite;
img.sprite = sp;
img.type = sp.border != Vector4.zero ? Image.Type.Sliced : Image.Type.Simple;
img.pixelsPerUnitMultiplier = s.ppu > 0f ? s.ppu : 1f;
img.fillCenter = true;
var old = img.transform.Find(OverlayName);
if (old != null) Object.Destroy(old.gameObject);
if (s.overlay != null) Overlay(img.transform, s.overlay, s.inset, s.ppu).color = new Color(1f, 1f, 1f, s.overlayAlpha);
return true;
}
/// <summary>부모를 꽉 채우는 덮개(광택 등 · 첫 자식 = 내용 아래) · inset = 왼 · 위 · 오 · 아래.</summary>
public static Image Overlay(Transform parent, Sprite sp, Vector4 inset, float ppu = 1f)
{
var go = new GameObject(OverlayName, typeof(RectTransform));
go.transform.SetParent(parent, false);
go.transform.SetAsFirstSibling();
var rt = (RectTransform)go.transform;
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = new Vector2(inset.x, inset.w);
rt.offsetMax = new Vector2(-inset.z, -inset.y);
var img = go.AddComponent<Image>();
img.sprite = sp;
img.type = sp.border != Vector4.zero ? Image.Type.Sliced : Image.Type.Simple;
img.pixelsPerUnitMultiplier = ppu > 0f ? ppu : 1f;
img.preserveAspect = sp.border == Vector4.zero;
img.raycastTarget = false;
return img;
}
public static Sprite Icon(string id)
{
return Theme != null ? Theme.Icon(id) : null;
}
/// <summary>아이콘 이미지(테마에 id 가 없으면 null) · 비율 유지 · 표 tint 적용.</summary>
public static Image IconImage(Transform parent, string id, Vector2 anchor, Vector2 pivot, Vector2 pos, Vector2 size)
{
var e = Theme != null ? Theme.IconEntryOf(id) : null;
if (e == null) return null;
var go = new GameObject("Icon_" + id, 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;
var img = go.AddComponent<Image>();
img.sprite = e.Sprite;
img.color = e.samguk != null ? Color.white : e.tint;
img.preserveAspect = true;
img.raycastTarget = false;
return img;
}
/// <summary>글자 외곽선(팩 버튼 글자처럼 굵은 어두운 선).</summary>
public static void Stroke(Graphic g, Color c, float d = 2.5f)
{
if (g == null || g.GetComponent<Outline>() != null) return;
var t = g as Text;
if (t != null && t.fontSize < 40) d = Mathf.Min(d, t.fontSize < 30 ? 1.5f : 2f); // 작은 글자는 얇게(굵으면 한글이 뭉개짐)
var o = g.gameObject.AddComponent<Outline>();
o.effectColor = c;
o.effectDistance = new Vector2(d, -d);
}
static readonly Dictionary<string, Sprite> worldCache = new Dictionary<string, Sprite>();
/// <summary>
/// 월드 공간(SpriteRenderer · 9-slice)용 스킨 사본 — 같은 텍스처를 ppu 만 바꿔 다시 만든다(체력 바처럼 월드 크기가 작은 막대가 테두리보다 작아 깨지지 않게).
/// </summary>
public static Sprite WorldSprite(string skinId, float ppu)
{
var s = Theme != null ? Theme.Skin(skinId) : null;
if (s == null) return null;
string key = skinId + "@" + ppu;
Sprite sp;
if (worldCache.TryGetValue(key, out sp) && sp != null) return sp;
var src = s.Sprite;
// SpriteRenderer 9-slice 는 가운데 칸이 0 px 이면 가운데를 그리지 않는다(팩 슬라이더 = 테두리가 전부) → 가운데 2 px 를 남긴다
var b = src.border;
float w = src.rect.width, h = src.rect.height;
if (b.x + b.z > w - 2f)
{
float k = Mathf.Max(0f, w - 2f) / Mathf.Max(1f, b.x + b.z);
b.x = Mathf.Floor(b.x * k);
b.z = Mathf.Floor(b.z * k);
}
if (b.y + b.w > h - 2f)
{
float k = Mathf.Max(0f, h - 2f) / Mathf.Max(1f, b.y + b.w);
b.y = Mathf.Floor(b.y * k);
b.w = Mathf.Floor(b.w * k);
}
sp = Sprite.Create(src.texture, src.rect, new Vector2(0.5f, 0.5f), ppu, 0, SpriteMeshType.FullRect, b);
sp.name = key;
sp.hideFlags = HideFlags.DontSave;
worldCache[key] = sp;
return sp;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8c3088b2ae40c174280af74a99269105

View File

@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace TKS
{
/// <summary>
/// UI 테마(PD #1100 UI 리뉴얼 · 2026-11-06) — 색 팔레트 · 9-slice 부품(스킨) · 아이콘 id → 스프라이트 치환 테이블 · 제목 폰트.
/// SOT = Assets/Data/UI/ui_theme.csv → 메뉴 TKS/Import UI Theme → Resources/UI/UITheme.asset(에디터 UIThemeImporter).
/// 스킨·아이콘은 팩(Layer Lab GUI Pro-SuperCasual · Assets/UI/GUIPro) 기본 + 신규 제작 납품(Assets/UI/Samguk/**/&lt;id&gt;.png)이 있으면 그것으로 치환.
/// 화면 코드는 id 로만 묻는다(스프라이트 경로를 코드에 쓰지 않는다 · C45).
/// </summary>
[CreateAssetMenu(menuName = "TKS/UI Theme")]
public class UITheme : ScriptableObject
{
public const string ResourcePath = "UI/UITheme";
[Serializable]
public class ColorEntry
{
public string id;
public Color color = Color.white;
}
[Serializable]
public class SkinEntry
{
public string id;
/// <summary>팩 바탕(흰 계열 = 호출 색으로 틴트).</summary>
public Sprite pack;
/// <summary>신규 제작 치환(있으면 pack 대신).</summary>
public Sprite samguk;
/// <summary>위 덮개(광택·글리프 · 틴트 안 함).</summary>
public Sprite overlay;
/// <summary>9-slice 배율(Image.pixelsPerUnitMultiplier · 클수록 테두리가 얇다).</summary>
public float ppu = 1f;
/// <summary>덮개 안쪽 여백(px · 왼 · 위 · 오 · 아래).</summary>
public Vector4 inset;
/// <summary>덮개 불투명도.</summary>
public float overlayAlpha = 1f;
public Sprite Sprite
{
get { return samguk != null ? samguk : pack; }
}
}
[Serializable]
public class IconEntry
{
public string id;
public Sprite pack;
public Sprite samguk;
/// <summary>표시 색(흰 실루엣 아이콘용 · 기본 흰색 = 원색 그대로).</summary>
public Color tint = Color.white;
public Sprite Sprite
{
get { return samguk != null ? samguk : pack; }
}
}
public List<ColorEntry> colors = new List<ColorEntry>();
public List<SkinEntry> skins = new List<SkinEntry>();
public List<IconEntry> icons = new List<IconEntry>();
/// <summary>제목·숫자 강조(Sen-ExtraBold · 한글 = 임포터 폴백 Malgun Gothic · PD 결정 전).</summary>
public Font titleFont;
Dictionary<string, ColorEntry> colorMap;
Dictionary<string, SkinEntry> skinMap;
Dictionary<string, IconEntry> iconMap;
static UITheme current;
static bool tried;
/// <summary>Resources/UI/UITheme(없으면 null = 기존 도형 UI 로 동작).</summary>
public static UITheme Current
{
get
{
if (current == null && !tried)
{
tried = true;
current = Resources.Load<UITheme>(ResourcePath);
}
return current;
}
}
/// <summary>테마 교체(임포트 뒤 · 테스트) — null = 다시 Resources 에서 읽음.</summary>
public static void Use(UITheme t)
{
current = t;
tried = t != null;
if (t != null) t.Rebuild();
}
public void Rebuild()
{
colorMap = new Dictionary<string, ColorEntry>();
foreach (var c in colors)
if (c != null && !string.IsNullOrEmpty(c.id)) colorMap[c.id] = c;
skinMap = new Dictionary<string, SkinEntry>();
foreach (var s in skins)
if (s != null && !string.IsNullOrEmpty(s.id)) skinMap[s.id] = s;
iconMap = new Dictionary<string, IconEntry>();
foreach (var i in icons)
if (i != null && !string.IsNullOrEmpty(i.id)) iconMap[i.id] = i;
}
void OnEnable()
{
colorMap = null;
}
void Ensure()
{
if (colorMap == null || skinMap == null || iconMap == null) Rebuild();
}
public bool TryColor(string id, out Color c)
{
Ensure();
ColorEntry e;
if (id != null && colorMap.TryGetValue(id, out e))
{
c = e.color;
return true;
}
c = Color.white;
return false;
}
public SkinEntry Skin(string id)
{
Ensure();
SkinEntry e;
return id != null && skinMap.TryGetValue(id, out e) && e.Sprite != null ? e : null;
}
public IconEntry IconEntryOf(string id)
{
Ensure();
IconEntry e;
return id != null && iconMap.TryGetValue(id, out e) && e.Sprite != null ? e : null;
}
public Sprite Icon(string id)
{
var e = IconEntryOf(id);
return e != null ? e.Sprite : null;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 63b0a8e358e193740883a84a0bfe1325

View File

@ -29,6 +29,10 @@ namespace TKS
SpriteRenderer overwatchArea;
SpriteRenderer hpBg;
SpriteRenderer hpFill;
bool packHp;
// 체력 바 9-slice 월드 배율(테두리 px → 월드 = px / ppu · 바 높이 0.22 / 0.13 안에 테두리가 들어가게)
const float HpBgPpu = 240f;
const float HpFillPpu = 420f;
TextMesh mark;
TextMesh nameLabel;
TextMesh badge;
@ -154,6 +158,22 @@ namespace TKS
hpFill = ProceduralSprites.NewSprite(transform, "HpFill", ProceduralSprites.Square, new Color(0.35f, 0.9f, 0.35f), 1f, 3);
hpFill.transform.localPosition = hpBg.transform.localPosition;
hpFill.transform.localScale = new Vector3(HpBarWidth, 0.12f, 1f);
// 팩 슬라이더 체력 바(PD #1100 · 테마 hp_bg·hp_fill · 9-slice 월드 사본) — 테마가 없으면 위 도형 그대로
var bgSp = UISkin.WorldSprite("hp_bg", HpBgPpu);
var fillSp = UISkin.WorldSprite("hp_fill", HpFillPpu);
if (bgSp != null && fillSp != null)
{
packHp = true;
hpBg.sprite = bgSp;
hpBg.drawMode = SpriteDrawMode.Sliced;
hpBg.transform.localScale = Vector3.one;
hpBg.size = new Vector2(HpBarWidth + 0.1f, 0.22f);
hpBg.color = UISkin.Col("hp_bg", Color.white);
hpFill.sprite = fillSp;
hpFill.drawMode = SpriteDrawMode.Sliced;
hpFill.transform.localScale = Vector3.one;
hpFill.size = new Vector2(HpBarWidth, 0.13f);
}
mark = TextUtil.CreateWorldText(transform, UnitTypeInfo.Mark(u.Type), u.Radius * 1.1f, Color.white, 4);
if (anim != null) mark.gameObject.SetActive(false); // 아트가 있으면 병종은 이름 옆에
@ -190,9 +210,20 @@ namespace TKS
if (Sim.Active) return; // 가상 실행(적 AI 예측) = 표시 그대로
if (unit == null || hpFill == null) return;
float ratio = unit.MaxHp > 0 ? Mathf.Clamp01((float)unit.Hp / unit.MaxHp) : 0f;
hpFill.transform.localScale = new Vector3(HpBarWidth * ratio, 0.12f, 1f);
hpFill.transform.localPosition = new Vector3(-HpBarWidth * 0.5f + HpBarWidth * ratio * 0.5f, hpBg.transform.localPosition.y, 0f);
hpFill.color = ratio > 0.5f ? new Color(0.35f, 0.9f, 0.35f) : (ratio > 0.25f ? new Color(0.95f, 0.8f, 0.2f) : new Color(0.95f, 0.3f, 0.2f));
if (packHp)
{
float w = HpBarWidth * ratio;
hpFill.enabled = w > 0.045f;
hpFill.size = new Vector2(Mathf.Max(0.045f, w), 0.13f);
hpFill.transform.localPosition = new Vector3(-HpBarWidth * 0.5f + w * 0.5f, hpBg.transform.localPosition.y + 0.015f, 0f);
hpFill.color = ratio > 0.5f ? UISkin.Col("hp_hi", Color.green) : ratio > 0.25f ? UISkin.Col("hp_mid", Color.yellow) : UISkin.Col("hp_low", Color.red);
}
else
{
hpFill.transform.localScale = new Vector3(HpBarWidth * ratio, 0.12f, 1f);
hpFill.transform.localPosition = new Vector3(-HpBarWidth * 0.5f + HpBarWidth * ratio * 0.5f, hpBg.transform.localPosition.y, 0f);
hpFill.color = ratio > 0.5f ? new Color(0.35f, 0.9f, 0.35f) : (ratio > 0.25f ? new Color(0.95f, 0.8f, 0.2f) : new Color(0.95f, 0.3f, 0.2f));
}
bool dim = unit.Faction == Faction.Player && unit.ActedThisTurn;
if (flash == null)

View File

@ -735,20 +735,39 @@ namespace TKS
var panel = Img("Panel", popup.transform, Sprite("plate"), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 40f), new Vector2(900f, 1080f));
panel.type = Image.Type.Sliced;
panel.raycastTarget = true;
var head = Img("Head", panel.transform, Sprite("banner"), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), new Vector2(760f, 112f));
head.type = Image.Type.Sliced;
var bk = data.Kit("banner");
popupTitle = Label(head.transform, "", 34, Paper, new Vector2(0.5f, 1f - (bk != null ? bk.numberCenter[1] : 0.4f)));
// 스테이지 팝업만 팩 Popup(PD #1100 · 거점 kit 유지) · 테마가 없으면 kit plate·banner
bool pack = UISkin.Apply(panel, "popup");
if (pack)
{
panel.color = UISkin.Col("paper", Color.white);
var band = Img("Head", panel.transform, null, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -6f), new Vector2(-14f, 150f));
UISkin.Apply(band, "popup_head");
band.color = UISkin.Col("header", Ink);
popupTitle = Label(band.transform, "", 38, Color.white, new Vector2(0.5f, 0.5f));
popupTitle.font = UISkin.TitleFont;
popupTitle.GetComponent<Outline>().effectColor = UISkin.Col("stroke", Ink);
var x = Img("Close", panel.transform, null, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-58f, -60f), new Vector2(86f, 88f));
UISkin.Apply(x, "close");
x.raycastTarget = true;
x.gameObject.AddComponent<Button>().onClick.AddListener(ClosePopup);
}
else
{
var head = Img("Head", panel.transform, Sprite("banner"), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), new Vector2(760f, 112f));
head.type = Image.Type.Sliced;
var bk = data.Kit("banner");
popupTitle = Label(head.transform, "", 34, Paper, new Vector2(0.5f, 1f - (bk != null ? bk.numberCenter[1] : 0.4f)));
}
popupTitle.lineSpacing = 0.9f;
popupBody = Label(panel.transform, "", 30, new Color(0.25f, 0.15f, 0.1f), new Vector2(0.5f, 0.56f));
popupBody = Label(panel.transform, "", 30, pack ? UISkin.Col("ink", new Color(0.25f, 0.15f, 0.1f)) : new Color(0.25f, 0.15f, 0.1f), new Vector2(0.5f, pack ? 0.545f : 0.56f));
popupBody.GetComponent<Outline>().enabled = false;
popupBody.rectTransform.sizeDelta = new Vector2(800f, 720f);
popupBody.rectTransform.sizeDelta = new Vector2(800f, pack ? 640f : 720f);
popupBody.horizontalOverflow = HorizontalWrapMode.Wrap;
popupBody.supportRichText = true;
popupGo = WideButton("Go", panel.transform, "출전", new Vector2(-180f, 80f), new Color(0.89f, 0.33f, 0.23f), ConfirmPopup, out popupGoLabel);
popupGo = WideButton("Go", panel.transform, "출전", new Vector2(-180f, 80f), UISkin.Col("red", new Color(0.89f, 0.33f, 0.23f)), ConfirmPopup, out popupGoLabel);
Text closeLabel;
WideButton("Close", panel.transform, "닫기", new Vector2(180f, 80f), new Color(0.6f, 0.55f, 0.5f), ClosePopup, out closeLabel);
popupSweep = WideButton("Sweep", panel.transform, "소탕", new Vector2(0f, 200f), new Color(0.35f, 0.5f, 0.35f), OnSweepClicked, out popupSweepLabel);
WideButton("Close", panel.transform, "닫기", new Vector2(180f, 80f), UISkin.Col("dark", new Color(0.6f, 0.55f, 0.5f)), ClosePopup, out closeLabel);
popupSweep = WideButton("Sweep", panel.transform, "소탕", new Vector2(0f, 200f), UISkin.Col("ok", new Color(0.35f, 0.5f, 0.35f)), OnSweepClicked, out popupSweepLabel);
popupSweep.gameObject.SetActive(false);
popup.SetActive(false);
}
@ -849,11 +868,18 @@ namespace TKS
{
var img = Img(name, parent, Sprite("plate"), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0.5f), pos, new Vector2(300f, 100f));
img.type = Image.Type.Sliced;
bool pack = UISkin.Apply(img, "button");
if (pack) img.rectTransform.sizeDelta = new Vector2(320f, 112f);
img.color = tint;
img.raycastTarget = true;
var b = img.gameObject.AddComponent<Button>();
b.onClick.AddListener(() => onClick());
labelText = Label(img.transform, label, 42, Paper, new Vector2(0.5f, 0.55f));
labelText = Label(img.transform, label, 42, pack ? Color.white : Paper, new Vector2(0.5f, pack ? 0.6f : 0.55f));
if (pack)
{
labelText.font = UISkin.TitleFont;
labelText.GetComponent<Outline>().effectColor = UISkin.Col("stroke", Ink);
}
return b;
}
}

View File

@ -0,0 +1,48 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UI;
namespace TKS.Tests
{
/// <summary>UI 테마(PD #1100) — 팩 부품이 실려 있고 신규 제작(B) 스프라이트가 같은 id 를 코드 변경 없이 치환한다.</summary>
public class UIThemeTests
{
[TearDown]
public void TearDown()
{
UITheme.Use(null); // 다음 테스트는 Resources 테마로
}
[Test]
public void Theme_PackParts_AndSamgukReplacesById()
{
var theme = Resources.Load<UITheme>(UITheme.ResourcePath);
Assert.IsNotNull(theme, "Resources/UI/UITheme 없음 — 메뉴 TKS/Import UI Theme");
foreach (var skin in new[] { "card", "button", "popup", "bar_bg", "bar_fill", "tab_focus", "skill_btn", "hp_bg" })
Assert.IsNotNull(theme.Skin(skin), "스킨 " + skin);
foreach (var icon in new[] { "cur_gold", "cur_gem", "cur_stamina", "tab_hero", "chest_gold", "gear_sword", "star_on" })
Assert.IsNotNull(theme.Icon(icon), "아이콘 " + icon);
Assert.IsNotNull(theme.titleFont, "제목 폰트");
// 신규 제작 납품 = 같은 id 의 samguk 칸 → 아이콘·스킨 모두 그 스프라이트로(팩은 보존)
var copy = Object.Instantiate(theme);
var tex = new Texture2D(8, 8);
var sp = Sprite.Create(tex, new Rect(0, 0, 8, 8), new Vector2(0.5f, 0.5f), 100f);
var pack = copy.Icon("cur_gold");
copy.IconEntryOf("cur_gold").samguk = sp;
copy.Skin("card").samguk = sp;
UITheme.Use(copy);
Assert.AreSame(sp, UISkin.Icon("cur_gold"), "아이콘 치환");
Assert.AreSame(pack, copy.IconEntryOf("cur_gold").pack, "팩 원본 보존");
var go = new GameObject("ThemeProbe", typeof(RectTransform));
var img = go.AddComponent<Image>();
Assert.IsTrue(UISkin.Apply(img, "card"));
Assert.AreSame(sp, img.sprite, "스킨 치환");
var ic = UISkin.IconImage(go.transform, "cur_gold", Vector2.zero, Vector2.zero, Vector2.zero, Vector2.one * 10f);
Assert.AreSame(sp, ic.sprite);
Object.DestroyImmediate(go);
Object.DestroyImmediate(copy);
Object.DestroyImmediate(tex);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1641ce4b59f119248ba07d28ec2907bc

8
Assets/UI/Lobby.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 029e5a94b4c61a549b044c385b6d3623
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,168 @@
fileFormatVersion: 2
guid: 604887b577dfd2e4aacb8cf8509a5878
TextureImporter:
internalIDToNameTable:
- first:
213: 2710779485955313897
second: keyart_04_0
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: keyart_04_0
rect:
serializedVersion: 2
x: 0
y: 0
width: 768
height: 1344
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: 9e058c4849f9e9520800000000000000
internalID: 2710779485955313897
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

8
Assets/UI/Samguk.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eefa21a8edc415a44aba5b194c834e98
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,5 @@
# Assets/UI/Samguk — 신규 제작 UI 리소스(PD #1100 B · 세션 tks-ui-art)
- 파일 이름 = 테마 id (`cur_gold.png` · `tab_hero.png` · `frame_gold.png` · `bg_lobby.png` · `tre_<id>.png` …) · 하위 폴더 = 묶음(group).
- 들어오면 임포트 설정(Sprite 2D)과 테마 치환 테이블(`Assets/Resources/UI/UITheme.asset`)이 자동 갱신된다(`Editor/UIThemeImporter` · 수동 = 메뉴 `TKS/Import UI Theme`).
- 반영 = `Tools/apply_delivery.py apply --all --kind ui`(납품 인박스 `C:\ArtRnD\samguk_game\deliveries\*_ui_*.json`) · 기믹 `gim_*` 는 `Assets/Resources/Gimmicks/Art/` 로 간다.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 582dcc0b9fc441c4abe1945fea18f576
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,7 +6,7 @@ PD 2026-11-05: 「매번 수신하지 않아도, 요청한 작업이 완전히
인박스 = C:\\ArtRnD\\samguk_game\\deliveries\\<YYYYMMDD_HHMM>_<batch>.json
{
"batch": "bg" | "hgj" | "heroes_back" | "dz" | "cc" | "wei" | …,
"kind": "bg" | "poses",
"kind": "bg" | "poses" | "worldmap" | "ui",
"qa": "PASS",
"session": "<만든 세션 이름>",
# kind == "bg"
@ -21,6 +21,15 @@ PD 2026-11-05: 「매번 수신하지 않아도, 요청한 작업이 완전히
"maps": [{"chapter": 3, "id": "ch03", "ko": "군웅할거", "nodes": 15,
"map": "maps/ch03.png", "map_md5": "<md5>", "anchors": "anchors/ch03.json", "anchors_md5": "<md5>"}]
}
# kind == "ui" (UI 리뉴얼 B 신규 제작 · 세션 tks-ui-art · PD #1100 · 2026-11-06)
"root": "C:/ArtRnD/samguk_ui/<group>", # files 의 기준
"files": ["cur_gold.png", …] 또는 [{"file": "cur_gold.png", "md5": "<md5>"}, …], # 파일 이름(확장자 앞) = 테마 id
"md5_list": "C:/ArtRnD/samguk_ui/<group>/md5.txt" # 선택 · <root> 기준 상대 경로 · md5sum 형식
}
ui = <root>/<file> → Assets/UI/Samguk/<group>/<id>.png (group = batch 의 「ui_」 뒤 · 없으면 root 폴더 이름) ·
기믹 그림(id 가 gim_ 로 시작) → Assets/Resources/Gimmicks/Art/<id>.png(GimmickView 가 읽는 자리) ·
치환 기록 = Assets/UI/Samguk/ui_delivered.csv(id · group · 경로 · md5 · batch) · 테마 치환 테이블(Resources/UI/UITheme.asset)은
Unity 가 Samguk PNG 임포트 때 자동 갱신(Editor/UIThemeImporter · 수동 = 메뉴 TKS/Import UI Theme) — 화면 코드 변경 없음.
worldmap = 맵 PNG → Assets/Resources/ChapterMap/Maps/<id>.png · 좌표 json → Data/<id>.json(게임 노드 = stages[].at) ·
Data/world.json 의 같은 id 항목(길 roadX·roadY · 장소)을 좌표 json 으로 교체. 게임 챕터 → 맵 연결 = Assets/Data/Stages/chapters.csv map 열.
같은 맵 id 를 더 늦게 다시 납품한 json(_r2 등)이 미반영으로 있으면 앞의 것은 반영하지 않고 .superseded.json 으로 바꾼다.
@ -46,6 +55,9 @@ UNITS = os.path.join(PROJECT, "Assets", "Resources", "Units")
BOARD = os.path.join(PROJECT, "Assets", "Resources", "Board", "battlefield_01.png")
CHAPMAP = os.path.join(PROJECT, "Assets", "Resources", "ChapterMap")
WORLDMAP_ROOT = r"C:\ArtRnD\samguk_chapmap\plan"
SAMGUK_UI = os.path.join(PROJECT, "Assets", "UI", "Samguk")
GIMMICK_ART = os.path.join(PROJECT, "Assets", "Resources", "Gimmicks", "Art")
THEME_CSV = os.path.join(PROJECT, "Assets", "Data", "UI", "ui_theme.csv")
def md5_of(path):
@ -159,6 +171,56 @@ def apply_worldmap(d):
open(world_path, "w", encoding="utf-8").write(json.dumps(world, ensure_ascii=False, indent=1))
def apply_ui(d):
root = d["root"]
if d.get("md5_list"):
ok, bad = check_md5_list(root, d["md5_list"])
if bad:
sys.exit("md5 불일치 %d: %s" % (len(bad), ", ".join(bad[:5])))
print("md5 OK", ok)
batch = str(d.get("batch", ""))
group = batch[3:] if batch.startswith("ui_") else os.path.basename(os.path.normpath(root))
known = set()
if os.path.exists(THEME_CSV):
for line in open(THEME_CSV, encoding="utf-8-sig"):
f = line.split(",")
if len(f) > 1 and f[0] in ("skin", "icon"):
known.add(f[1])
log_path = os.path.join(SAMGUK_UI, "ui_delivered.csv")
rows = {}
if os.path.exists(log_path):
for line in open(log_path, encoding="utf-8-sig").read().splitlines()[1:]:
if line.strip():
rows[line.split(",")[0]] = line
replaced, added = [], []
for item in d["files"]:
rel, digest = (item, None) if isinstance(item, str) else (item["file"], item.get("md5"))
src = os.path.join(root, rel.replace("/", os.sep))
if not rel.lower().endswith(".png"):
sys.exit("PNG 가 아님: " + rel)
if digest and md5_of(src) != digest.lower():
sys.exit("ui md5 불일치: " + src)
uid = os.path.splitext(os.path.basename(rel))[0]
dst = os.path.join(GIMMICK_ART, uid + ".png") if uid.startswith("gim_") else os.path.join(SAMGUK_UI, group, uid + ".png")
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copyfile(src, dst)
m = md5_of(dst)
if digest and m != digest.lower():
sys.exit("복사 뒤 md5 불일치: " + dst)
(replaced if uid in known else added).append(uid)
try:
shown = os.path.relpath(dst, PROJECT)
except ValueError: # 다른 드라이브(시험)
shown = dst
rows[uid] = ",".join([uid, group, shown.replace(os.sep, "/"), m, batch])
os.makedirs(SAMGUK_UI, exist_ok=True)
with open(log_path, "w", encoding="utf-8-sig", newline="\n") as f:
f.write("id,group,path,md5,batch\n")
for k in sorted(rows):
f.write(rows[k] + "\n")
print("ui", group, "· 팩 치환", len(replaced), "· 새 id", len(added), (added[:8] if added else ""), "→", log_path)
def cmd_list(a):
files = pending()
old = superseded(files)
@ -195,13 +257,16 @@ def cmd_apply(a):
apply_poses(d)
elif d["kind"] == "worldmap":
apply_worldmap(d)
elif d["kind"] == "ui":
apply_ui(d)
else:
sys.exit("모르는 kind: " + str(d.get("kind")))
os.replace(p, p[:-5] + ".applied.json")
print("\n다음(Unity · PD 에디터 Play 중이면 먼저 editor.playmode stop):")
print(" unity command --project-path . editor invoke_menu --path 'TKS/Reimport Unit Art (유닛 아트 재임포트)' --result-only (Units·Board·ChapterMap)")
print(" unity command --project-path . editor invoke_menu --path 'TKS/Import UI Theme (UI 테마 임포트)' --result-only (ui · Samguk PNG 임포트 때 자동 · 확인용)")
print(" unity command --project-path . test run --mode playmode --async-tests --timeout 600 --result-only ; unity command --project-path . test status --result-only")
print(" git add -A && git commit (끝줄 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>)")
print(" git add -A && git commit (끝줄 Co-Authored-By: <실제 모델> <noreply@anthropic.com>)")
if __name__ == "__main__":
@ -212,7 +277,7 @@ if __name__ == "__main__":
ap = sp.add_parser("apply")
ap.add_argument("json", nargs="?", default="")
ap.add_argument("--all", action="store_true")
ap.add_argument("--kind", default="", help="이 kind 만(bg · poses · worldmap)")
ap.add_argument("--kind", default="", help="이 kind 만(bg · poses · worldmap · ui)")
ap.set_defaults(fn=cmd_apply)
a = p.parse_args()
if a.cmd == "apply" and not a.all and not a.json: