ThreeKingdomsSlingshot/Tools/prefab_tree.py

87 lines
3.8 KiB
Python

# 팩 데모 프리팹을 레퍼런스로 화면을 옮길 때 수치 추출(PD 2026-11-09 화면별 레퍼런스 재제작) — 예: <ComfyUI venv python> -I Tools/prefab_tree.py "<팩>/Prefabs/Prefabs_DemoScene_Panels/Collection_List.prefab" "<팩>/ResourcesData" Content 4
# Unity 프리팹 YAML → 계층(이름 · 앵커 · 위치 · 크기 · 이미지 스프라이트/색 · TMP 글자/크기/색) 출력 — 인수: 프리팹 · 스프라이트 루트 · [시작 이름] · [최대 깊이]
import os
import re
import sys
path, sproot = sys.argv[1], sys.argv[2]
start = sys.argv[3] if len(sys.argv) > 3 else None
maxd = int(sys.argv[4]) if len(sys.argv) > 4 else 99
txt = open(path, encoding='utf-8', errors='ignore').read()
docs = {}
for m in re.finditer(r'--- !u!(\d+) &(-?\d+)[^\n]*\n(.*?)(?=\n--- !u!|\Z)', txt, re.S):
docs[m.group(2)] = (m.group(1), m.group(3))
guid2name = {}
for root, _, files in os.walk(sproot):
for f in files:
if f.endswith('.meta'):
g = re.search(r'guid: (\w+)', open(os.path.join(root, f), encoding='utf-8', errors='ignore').read())
if g:
guid2name[g.group(1)] = f[:-5]
def field(body, key):
m = re.search(r'\n\s*' + key + r': (.*)', '\n' + body)
return m.group(1).strip() if m else ''
go_comps = {}
go_name = {}
for fid, (t, body) in docs.items():
if t == '1':
go_name[fid] = field(body, 'm_Name')
go_comps[fid] = re.findall(r'component: \{fileID: (-?\d+)\}', body)
rt_of = {}
for g, comps in go_comps.items():
for c in comps:
if c in docs and docs[c][0] == '224':
rt_of[g] = c
go_of_rt = {v: k for k, v in rt_of.items()}
def v2(s):
m = re.findall(r'[-\d.]+', s)
return '(' + ','.join(str(round(float(x), 1)) for x in m) + ')' if m else ''
def desc(g):
rt = docs[rt_of[g]][1]
out = '%s a%s-%s p%s pos%s size%s' % (go_name[g], v2(field(rt, 'm_AnchorMin')), v2(field(rt, 'm_AnchorMax')), v2(field(rt, 'm_Pivot')), v2(field(rt, 'm_AnchoredPosition')), v2(field(rt, 'm_SizeDelta')))
for c in go_comps[g]:
if c not in docs or docs[c][0] != '114':
continue
b = docs[c][1]
if not re.search(r'm_Enabled: 1', b):
continue
sp = re.search(r'm_Sprite: \{fileID: -?\d+, guid: (\w+)', b)
col = re.search(r'm_Color: \{r: ([\d.]+), g: ([\d.]+), b: ([\d.]+), a: ([\d.]+)\}', b)
if sp:
hexc = '#%02X%02X%02X/%.2f' % tuple([int(float(x) * 255 + 0.5) for x in col.groups()[:3]] + [float(col.group(4))]) if col else ''
ty = field(b, 'm_Type')
ppu = field(b, 'm_PixelsPerUnitMultiplier')
out += ' | IMG ' + guid2name.get(sp.group(1), sp.group(1)[:8]) + ' ' + hexc + ' t' + ty + (' ppu' + ppu if ppu and ppu != '1' else '')
tx = re.search(r'm_text: (.*)', b)
if tx:
fc = re.search(r'm_fontColor: \{r: ([\d.]+), g: ([\d.]+), b: ([\d.]+), a: ([\d.]+)\}', b)
hexc = '#%02X%02X%02X' % tuple(int(float(x) * 255 + 0.5) for x in fc.groups()[:3]) if fc else ''
out += ' | TXT "%s" size%s %s align%s' % (tx.group(1)[:30], field(b, 'm_fontSize'), hexc, field(b, 'm_HorizontalAlignment') or field(b, 'm_textAlignment'))
if 'm_EffectDistance' in b:
out += ' | FX ' + v2(field(b, 'm_EffectDistance'))
return out
def walk(g, d):
if d > maxd:
return
print(' ' * d + desc(g))
rt = docs[rt_of[g]][1]
for c in re.findall(r'- \{fileID: (-?\d+)\}', rt.split('m_Children:')[1].split('m_Father')[0]) if 'm_Children:' in rt else []:
if c in go_of_rt:
walk(go_of_rt[c], d + 1)
roots = [g for g in go_name if g in rt_of and (start and go_name[g] == start)]
if not roots:
roots = [g for g in go_name if g in rt_of and field(docs[rt_of[g]][1], 'm_Father') == '{fileID: 0}']
walk(roots[0], 0)