BurningTimesAi/공유/기획/FirstRising/리뉴얼/tools/build_ui_proposal.py

158 lines
14 KiB
Python
Raw Permalink Normal View History

# FirstRising 로비 UI 개편안(검토용) — 라이트 템플릿
import os, sys
sys.path.insert(0, os.path.dirname(__file__))
from deckkit_light import *
from PIL import Image as PImage
BASE = r"E:\BurningTimes\공유\기획\FirstRising"
TB = lambda n: os.path.join(BASE, "레퍼런스_TBH", "img", n)
MK = lambda n: os.path.join(BASE, "리뉴얼", "img", n)
OUT = os.path.join(BASE, "리뉴얼", "2026-09-20_FirstRising_로비UI개편안_v1.pptx")
# TBH 하단 메뉴·상단 바 잘라내기
tr = PImage.open(TB("09_trade_ship.jpg")).convert("RGB"); tr.crop((0, int(tr.height * 0.86), tr.width, tr.height)).save(MK("thumb_menu5.jpg"), quality=90)
hp = PImage.open(TB("03_hero_panel.jpg")).convert("RGB"); hp.crop((int(hp.width * 0.60), 0, hp.width, int(hp.height * 0.06))).save(MK("thumb_topbar.jpg"), quality=90)
D = Deck(brand="FIRSTRISING", brand_sub="Lobby UI Proposal · BurningTimes")
SRC = "근거 FirstRising 현재 로비(PD 제공 스크린샷) · TBH 실행 화면·UI 계층(TBH 분석 v2) · 와이어프레임은 배치 예시"
# 01 표지 ---------------------------------------------------------------------------------
D.page += 1
sl = D.prs.slides.add_slide(D.blank)
D.rect(sl, 0, 0, 0.19, 7.5, fill=RED); D.rect(sl, 7.22, 0, 6.11, 7.5, fill=PANEL)
D.text(sl, 0.81, 0.81, 6.1, 0.2, "FIRSTRISING LOBBY UI PROPOSAL", size=9, bold=True, color=SUB)
D.text(sl, 0.81, 1.15, 6.4, 0.3, "리뉴얼 기획서 v2 부속 · 검토용", size=12.5, color=RED, face=F_MED)
D.text(sl, 0.75, 1.52, 6.45, 1.45, ["로비 UI 개편안", [("메뉴판 → 전투 화면", {"color": RED})]], size=34, color=INK, face=F_BLACK, spacing=1.05)
D.text(sl, 0.81, 3.1, 6.1, 0.85, "버튼 25개가 한 번에 보이는 지금 로비를 TBH처럼 '전투 1줄 + 메뉴 5개 + 필요할 때 여는 패널'로 바꾸는 안입니다.", size=10.5, color=BODY, spacing=1.3)
D.hline(sl, 0.81, 4.03, 6.11)
for i, (k, v) in enumerate((("진단", "진입점 약 25 · 빨간 점 약 17 · 재화 3종 · 전투 안 보임"), ("구조", "전투 스트립 → 하단 메뉴 5 → 패널 3 → 상단 유틸"), ("매핑", "지금 요소 10묶음의 이동·통합·삭제"))):
yy = 4.1 + i * 0.345
D.text(sl, 0.81, yy, 1.17, 0.22, k, size=9.5, bold=True, color=RED); D.text(sl, 1.97, yy, 4.94, 0.22, v, size=9.5, color=BODY); D.hline(sl, 0.81, yy + 0.28, 6.11)
D.text(sl, 0.81, 5.33, 1.2, 0.4, "총괄PM", size=18, bold=True, color=INK); D.text(sl, 2.0, 5.42, 3.0, 0.3, "BurningTimes", size=12.5, color=SUB, face=F_LIGHT)
D.text(sl, 0.81, 5.8, 6.1, 0.2, "2026-09-20 · BT22-Plan 후속", size=9, color=SUB)
D.text(sl, 7.72, 0.25, 5.27, 0.4, "지금 → 바뀐 뒤", size=11, bold=True, color=SUB, align=PP_ALIGN.CENTER)
D.image(sl, MK("ui_current_annotated.png"), 7.55, 0.8, 1.95, 3.48, fill=WHITE)
D.caption(sl, 7.55, 4.33, 1.95, "지금", "모바일 로비")
D.arrow(sl, 9.6, 2.5, 9.95, 2.5, width=2.5)
D.image(sl, MK("ui_wire_menu.png"), 10.05, 1.25, 2.95, 1.66, fill=WHITE)
D.caption(sl, 10.05, 2.96, 2.95, "바뀐 뒤", "메뉴 열림 모드")
D.image(sl, MK("ui_wire_idle.png"), 10.05, 3.35, 2.95, 1.66, fill=WHITE)
D.caption(sl, 10.05, 5.06, 2.95, "바뀐 뒤", "기본(상주) 모드")
D.hline(sl, 7.61, 6.5, 5.33, color=RGBColor(0xDD, 0xDD, 0xDD))
D.text(sl, 7.61, 6.6, 5.33, 0.18, "REVIEW POINTS", size=7.5, color=SUB)
D.text(sl, 7.61, 6.8, 5.33, 0.32, "레이아웃 2종 · 매핑 10묶음 · 검토 요청 4건", size=13, bold=True, color=INK)
# 02 진단 ---------------------------------------------------------------------------------
sl = D.slide("지금 로비 진단 — 메뉴판 로비", "AS-IS | 지금 로비", "한 화면에 모든 기능이 떠 있고, 정작 전투는 보이지 않습니다. 번호는 다음 쪽부터의 매핑 번호와 같습니다.", SRC)
D.image(sl, MK("ui_current_annotated.png"), X0, Y0, 2.9, 5.08, fill=PANEL)
D.kpis(sl, 3.85, Y0, 8.78, 1.05, [("약 25", "한 화면 진입점", True), ("약 17", "빨간 점(알림)", True), ("3종", "재화(에너지·젬·골드)", True), ("0", "보이는 전투", True)], big=19)
probs = [("진입점 과다", "왼쪽 BM 5 · 오른쪽 3 · 가운데 5 · 도전 2 · 하단 탭 5 · 메뉴 · 게임 시작"), ("알림 과다", "모든 버튼이 '지금 눌러야 할 것'처럼 보인다"),
("재화 3종 고정 노출", "에너지 30/30은 켜둔 채 방치하는 구조와 충돌"), ("전투가 안 보임", "로비는 메뉴판, 전투는 '게임 시작'을 눌러야 시작"),
("세로 전체 화면", "작업표시줄에 상주할 수 없는 형태"), ("버그", "게임 시작 버튼 아래 자리표시 'New Text' 노출")]
for i, (t, s) in enumerate(probs):
col = i % 2; row = i // 2
px = 3.85 + col * 4.47; py = 2.88 + row * 1.08
D.card(sl, px, py, 4.31, 0.94)
D.rect(sl, px, py, 4.31, 0.94, fill=None, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.05)
D.num(sl, px + 0.16, py + 0.15, i + 1, d=0.24, size=8.5)
D.text(sl, px + 0.5, py + 0.1, 3.7, 0.28, t, size=11, bold=True, color=INK)
D.text(sl, px + 0.5, py + 0.42, 3.7, 0.5, s, size=9, color=SUB)
# 03 원칙 ---------------------------------------------------------------------------------
sl = D.slide("TBH에서 가져올 원칙 4가지", "PRINCIPLES | TBH 참고", "TBH는 전투를 늘 보여 주고, 나머지는 필요할 때 여는 패널로 뺐습니다.", "근거 TBH 실행 화면(스트립·하단 메뉴·상단 바·아이템 툴팁) · TBH 로컬라이즈(우클릭 자동 이동 문구)")
cards = [(MK("thumb_strip.jpg"), "① 전투가 메인 화면", "스트립의 전투가 항상 보이고, 메뉴는 그 위에 뜬다"),
(MK("thumb_menu5.jpg"), "② 메뉴 5개로 압축", "하단 5개: 창고 · 스테이터스 · 룬 · 큐브 · 교역선"),
(MK("thumb_topbar.jpg"), "③ 재화·알림 최소", "재화는 골드 하나 · 상단 바는 고정·소리·우편·상점·종료"),
(MK("thumb_hero.jpg"), "④ 데스크톱 조작", "마우스 올리면 툴팁 · 우클릭 '자동(큐브)·자동(싣기)' · 드래그")]
cw = (XW - 0.42) / 4
for i, (p, t, s) in enumerate(cards):
cx = X0 + i * (cw + 0.14)
D.rect(sl, cx, Y0, cw, 4.3, fill=WHITE, line=RULE, shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.03)
D.image(sl, p, cx + 0.12, Y0 + 0.12, cw - 0.24, 2.0, fill=DARK, border=False)
D.text(sl, cx + 0.16, Y0 + 2.3, cw - 0.32, 0.3, t, size=12, bold=True, color=RED)
D.text(sl, cx + 0.16, Y0 + 2.7, cw - 0.32, 1.4, s, size=10, color=BODY, spacing=1.3)
D.banner(sl, "핵심", "전투 1줄 + 메뉴 5개 + 필요할 때 여는 패널 — 이 세 층으로 로비를 다시 짭니다.", y=6.1)
# 04 레이아웃 A ------------------------------------------------------------------------------
sl = D.slide("새 레이아웃 A — 기본(상주) 모드", "LAYOUT A | 기본 모드", "평소에는 작업표시줄 위 전투 스트립만 보입니다. 업무 화면을 가리지 않습니다.", SRC)
D.image(sl, MK("ui_wire_idle.png"), X0, Y0, 8.3, 4.67, fill=PANEL)
D.caption(sl, X0, 6.36, 8.3, "와이어프레임 A", "전투 영상은 FirstRising 실캡처 합성 · UI는 배치 예시")
D.panel(sl, 9.2, Y0, 3.43, 4.67, radius=0.04)
D.text(sl, 9.38, Y0 + 0.14, 3.1, 0.28, "보이는 것 7", size=11, bold=True, color=INK)
items = ["스테이지 · 좌우 포털", "파티 3인 HP", "웨이브 진행", "상자 카운터(자동 개봉)", "골드 — 유일 재화", "메뉴 토글", "전투 로그 한 줄"]
for i, t in enumerate(items):
D.num(sl, 9.38, Y0 + 0.55 + i * 0.44, i + 1, d=0.24, size=8.5); D.text(sl, 9.72, Y0 + 0.52 + i * 0.44, 2.8, 0.3, t, size=10, color=BODY)
D.hline(sl, 9.38, 5.35, 3.07)
D.text(sl, 9.38, 5.42, 3.07, 0.8, [[("보이지 않는 것 ", {"bold": True, "color": RED}), ("에너지 · 젬 · BM 버튼 · 게임 시작 버튼", {})]], size=9.5, color=BODY)
# 05 레이아웃 B ------------------------------------------------------------------------------
sl = D.slide("새 레이아웃 B — 메뉴 열림(관리) 모드", "LAYOUT B | 메뉴 열림", "메뉴를 열면 스트립 위에 패널이 뜹니다. 빨간 태그는 지금 로비에서 옮겨 온 기능입니다.", SRC)
D.image(sl, MK("ui_wire_menu.png"), X0, Y0, 8.3, 4.67, fill=PANEL)
D.caption(sl, X0, 6.36, 8.3, "와이어프레임 B", "패널 3 · 하단 메뉴 5 · 상단 유틸 5")
D.panel(sl, 9.2, Y0, 3.43, 4.67, radius=0.04)
rows = [("왼쪽 패널", "스테이터스 · 창고"), ("가운데 패널", "영웅·장비 · 교역선 · 상점 · 우편"), ("오른쪽 패널", "포털(스테이지 지도) · 큐브"),
("하단 메뉴 5", "창고 · 스테이터스 · 방치 트리 · 큐브 · 교역선"), ("상단 유틸 5", "고정 · 소리 · 우편 · 상점 · 설정")]
for i, (k, v) in enumerate(rows):
yy = Y0 + 0.2 + i * 0.68
D.pill(sl, 9.38, yy, k, fill=DARK if i < 3 else RED, w=1.1, size=8)
D.text(sl, 9.38, yy + 0.3, 3.07, 0.36, v, size=9.5, color=BODY)
D.hline(sl, 9.38, 5.62, 3.07)
D.text(sl, 9.38, 5.68, 3.07, 0.6, [[("조작 ", {"bold": True, "color": RED}), ("호버 툴팁 · 우클릭 자동 이동 · 드래그", {})]], size=9.5, color=BODY)
# 06 매핑 ---------------------------------------------------------------------------------
sl = D.slide("지금 → 바뀐 뒤 — 10묶음 매핑", "MAPPING | 이동 · 통합 · 대체 · 삭제", "왼쪽 번호가 지금 로비의 위치입니다. 모든 기능이 사라지지 않고 제자리를 찾습니다(에너지·게임 시작만 삭제).", SRC)
D.image(sl, MK("ui_current_annotated.png"), X0, Y0, 2.75, 4.9, fill=PANEL)
T_MOVE, T_MERGE, T_SUB, T_DEL = ("이동", INK), ("통합", RED), ("대체", RED), ("삭제", SUB)
rows = [["#", "지금", "바뀐 뒤", "처리"],
["1", "재화 3종(에너지·젬·골드)", "골드 하나 — 에너지 삭제 · 젬은 Steam 결제(DLC)", T_DEL],
["2", "캠프 침공 카드(남은 횟수 3)", "포털 패널 '도전' 탭(후순위)", T_MOVE],
["3", "챕터 선택('숨겨진 사연')", "포털 패널(스테이지 지도) + 스트립 좌우 포털", T_MOVE],
["4", "왼쪽 BM 5(첫 구매·월정액·시즌 티켓·성장 지원·패키지)", "상단 '상점' 하나로 — 메인 화면에서 제거", T_MERGE],
["5", "미션 센터 · 가방 · 죽지 않는 방법", "목표 표시(로그 영역) · 창고 · 설정 도움말", T_MOVE],
["6", "빠른 파밍 · 챕터 보상 상자 · 순찰", "스테이지 반복 · 상자 카운터 · 오프라인 보상 팝업", T_SUB],
["7", "일일 도전 · 특별한 도전", "포털 패널 탭", T_MOVE],
["8", "게임 시작(잠금) · 'New Text'", "삭제 — 전투는 멈추지 않는다 · 자리표시 버그 수정", T_DEL],
["9", "하단 탭 5(상점·장비·전투·도전·진화)", "하단 메뉴 5(창고·스테이터스·방치 트리·큐브·교역선)", T_SUB],
["10", "햄버거 메뉴", "상단 유틸 바의 '설정'", T_MOVE]]
D.table(sl, 3.7, Y0, 8.93, 4.9, rows, colw=[0.45, 3.1, 4.4, 0.98], size=9, hsize=9.5, first_bold=True, col_bold=[True, False, True, True])
# 07 알림·재화·조작 -------------------------------------------------------------------------
sl = D.slide("알림 · 재화 · 조작 정리", "RULES | 알림·재화·조작", "알림은 '행동이 필요한 것'만, 재화는 하나만, 조작은 마우스에 맞춥니다.", SRC)
blocks = [("알림", "약 17 → 3종", ["상자 가득 참(방치 손실 경고)", "상위 등급 드롭", "거래 완료(교역선)"], "나머지 빨간 점은 모두 제거"),
("재화", "3종 → 1종", ["골드 — 모든 성장·편의의 단일 재화", "에너지 — 삭제(전투는 멈추지 않음)", "젬 — 삭제, 유료는 Steam 결제(DLC)"], "화면에는 골드 하나만 표시"),
("조작", "터치 → 마우스", ["호버 — 아이템·스킬 툴팁", "우클릭 — 장착·큐브·창고·교역선 자동 이동", "드래그 — 칸 이동·큐브 넣기"], "TBH 툴팁의 '자동(큐브)·자동(싣기)' 방식")]
bw = (XW - 0.3) / 3
for i, (t, big, lines, note) in enumerate(blocks):
bx = X0 + i * (bw + 0.15)
D.panel(sl, bx, Y0, bw, 4.3, radius=0.04)
D.pill(sl, bx + 0.2, Y0 + 0.2, t, fill=RED, w=0.7)
D.text(sl, bx + 0.2, Y0 + 0.6, bw - 0.4, 0.5, big, size=20, bold=True, color=INK)
D.card(sl, bx + 0.2, Y0 + 1.25, bw - 0.4, 2.2)
D.bullets(sl, bx + 0.35, Y0 + 1.4, bw - 0.7, 2.0, lines, size=10.5, color=BODY, gap=8)
D.text(sl, bx + 0.2, Y0 + 3.6, bw - 0.4, 0.55, note, size=9.5, color=SUB)
D.banner(sl, "핵심", "보이는 것이 줄어들수록 '켜 두는 게임'이 됩니다 — 알림 3종 · 재화 1종 · 마우스 조작.", y=6.1)
# 08 적용 순서 · 검토 요청 --------------------------------------------------------------------
sl = D.slide("적용 순서와 검토 요청", "NEXT | 적용 순서 · 검토", "리뉴얼 로드맵(P0·P1)과 같은 순서로 적용합니다. 아래 4건을 검토해 주십시오.", "")
ph = [("P0", "상주 프로토타입", "전투 스트립 · 메뉴 토글 · 패널 3(포털 · 스테이터스 · 창고)"),
("P1", "파밍 코어", "큐브 · 방치 트리 · 교역선 · 우클릭 자동 이동 · 오프라인 보상 팝업"),
("후순위", "확장", "캠프 침공 · 목표 표시 · 모바일 동반 앱(같은 패널을 세로 배치로 재사용)")]
for i, (p, t, s) in enumerate(ph):
py = Y0 + i * 0.78
D.pill(sl, X0, py + 0.05, p, fill=RED if i == 0 else DARK, w=0.8)
D.text(sl, X0 + 1.0, py, 2.0, 0.35, t, size=12, bold=True, color=INK)
D.text(sl, X0 + 3.0, py + 0.02, 8.9, 0.6, s, size=10.5, color=BODY)
D.hline(sl, X0, py + 0.62, XW)
D.panel(sl, X0, 4.1, XW, 1.95, radius=0.05)
D.text(sl, X0 + 0.2, 4.22, 6, 0.28, "검토 요청", size=11, bold=True, color=INK)
asks = ["에너지 삭제 — 전투가 멈추지 않는 구조로 확정할지", "BM 버튼 5개를 메인 화면에서 빼고 상점 하나로 통합할지",
"캠프 침공 · 일일/특별 도전을 포털 패널로 옮기고 캠프 침공은 후순위로 둘지", "모바일은 동반 앱(같은 패널 재사용)으로 따로 판단할지"]
for i, a in enumerate(asks):
col = i % 2; row = i // 2
ax = X0 + 0.2 + col * 5.9; ay = 4.62 + row * 0.62
D.num(sl, ax, ay + 0.02, i + 1, d=0.26, size=9); D.text(sl, ax + 0.38, ay, 5.4, 0.55, a, size=10.5, color=BODY)
D.rect(sl, 0, 6.2, 13.333, 0.5, fill=RED)
D.text(sl, X0, 6.2, XW, 0.5, "결정되면 P0 프로토타입의 UI 범위를 이 문서의 와이어프레임 A·B로 확정합니다.", size=11, bold=True, color=WHITE, anchor=MSO_ANCHOR.MIDDLE)
n = D.save(OUT); print("saved", OUT, "slides:", n)