ThreeKingdomsSlingshot/Tools/wl_vfx_import.py

375 lines
16 KiB
Python
Raw Permalink Normal View History

스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
"""WL 프로젝트 이펙트(VFX) → 이 게임으로 옮기기 (파일 작업만 · GUID 유지 · 원본 무수정).
PD 2026-11-09: 「이 게임에 활용 가능한 리소스를 WL 프로젝트에서 체크해보고 이 프로젝트에도 그대로 쓸 수 있도록 옮기고」.
WL = URP · 이 게임 = 빌트인 렌더 파이프라인 → 재질 셰이더가 빌트인에서 도는 프리팹만 옮긴다.
판정(재질 1장씩 · 프리팹 = 의존 재질이 전부 통과해야 통과):
통과 = 빌트인 셰이더 · 셰이더 그래프에 Built-In 타깃이 있음(이 게임에 com.unity.shadergraph 필요) ·
.shader 가 URP 인클루드 없이 CG/빌트인 경로를 가짐
탈락 = URP 패키지 셰이더·재질 · URP 전용 .shader · URP 타깃만 있는 셰이더 그래프 · 셰이더 파일 없음 ·
프리팹이 쓰는 스크립트가 이 게임에 없는 네임스페이스(URP · HDRP · Input System · DOTween …)를 씀
복사 = 프리팹 + 의존 파일(guid 참조 · 셰이더 #include · .meta 의 외부 재질 참조) 을 .meta 째 복사(GUID 그대로 → 참조 유지).
경로 = WL Assets/<rel> → 이 게임 <dest>/<rel>(앞의 ResWork/VFX/ · ResWork/ 는 뗌) · 같은 GUID 가 이미 있으면 건너뜀.
실행(어느 python 이든 · 표준 라이브러리만):
python -I -B -X utf8 Tools/wl_vfx_import.py scan <WL Assets 기준 폴더|프리팹> ... # 판정 + 크기 표(복사 없음)
python -I -B -X utf8 Tools/wl_vfx_import.py copy <WL Assets 기준 폴더|프리팹> ... # 통과 프리팹만 복사
옵션: --wl <WL 루트>(없으면 환경 변수 WL_PROJECT_ROOT) · --dest Assets/VFX · --list(프리팹별 판정 줄) · --report <csv>
복사 뒤 = Unity 에디터 Assets 새로고침(자동) → 메뉴 TKS/VFX/Check Imported VFX (분홍 재질 검사).
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
다른 Unity 프로젝트도 같은 방식(2026-11-10 PD 「IB(쿠로네코) 이펙트를 그대로 써서 확인」):
python -I -B -X utf8 Tools/wl_vfx_import.py copy AssetResources/FX/ef_booster.prefab … --wl E:/NerdNavis/Kuroneko/KuronekoClient --dest Assets/VFX/IB --skip-scripts
--skip-scripts = 프리팹이 붙인 그 게임 전용 스크립트(.cs)는 판정·복사에서 뺀다(사본 Resources/Fx/TKS_Fx_* 에서 빠진 스크립트 부품을 지움).
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
"""
import argparse
import collections
import csv
import os
import re
import shutil
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
TKS_ROOT = os.path.dirname(HERE)
GUID_YAML = re.compile(r"guid:\s*([0-9a-f]{32})")
HEX32 = re.compile(r"(?<![0-9a-f])([0-9a-f]{32})(?![0-9a-f])")
SHADER_LINE = re.compile(r"m_Shader:\s*\{fileID:\s*(-?\d+)(?:,\s*guid:\s*([0-9a-f]{32}))?")
INCLUDE = re.compile(r'#include\s+"([^"]+)"')
USING = re.compile(r"^\s*using\s+(?:static\s+)?([A-Za-z_][\w\.]*)\s*;", re.M)
BUILTIN_GUIDS = {"0000000000000000f000000000000000", "0000000000000000e000000000000000", "0000000000000000d000000000000000"}
YAML_EXT = {".prefab", ".mat", ".asset", ".controller", ".anim", ".overridecontroller", ".mask", ".physicmaterial",
".physicsmaterial2d", ".rendertexture", ".flare", ".playable", ".signal", ".spriteatlas", ".unity", ".lighting"}
GRAPH_EXT = {".shadergraph", ".shadersubgraph", ".vfx", ".vfxoperator", ".vfxblock"}
SHADER_EXT = {".shader", ".cginc", ".hlsl", ".compute", ".glslinc"}
OK_CLASSES = {"builtin", "sg_builtin", "shader_builtin", "shader_multi", "port"}
# port = 이 게임 Assets/VFX/_BuiltinPorts/ 에 같은 GUID 의 빌트인 이식판이 이미 있는 URP 셰이더(재질 무수정으로 그대로 씀 · 2026-11-09)
# 이 게임에서 컴파일되는 네임스페이스(그 밖 = 스크립트 위험 → 그 프리팹 탈락)
SAFE_NS = ("System", "UnityEngine", "UnityEditor", "TMPro")
BAD_NS = ("UnityEngine.Rendering.Universal", "UnityEngine.Rendering.HighDefinition", "UnityEditor.Rendering.Universal",
"UnityEditor.Rendering.HighDefinition", "UnityEngine.InputSystem", "UnityEngine.Experimental.Rendering.Universal",
"Unity.Mathematics", "UnityEngine.Rendering.PostProcessing", "Cinemachine", "Unity.Cinemachine")
def strip_rel(rel):
for pre in ("ResWork/VFX/", "ResWork/"):
if rel.startswith(pre):
rel = rel[len(pre):]
break
# 팩·WL 사본은 Resources 밖에 둔다(빌드 포함 · Resources.Load 경로 충돌 방지) — 게임이 읽는 사본은 Resources/Fx/TKS_Fx_* 로 따로 만든다
return "/".join(p for p in rel.split("/") if p != "Resources")
class Index:
def __init__(self, root, with_pkg):
self.root = root
self.g2p = {}
self._walk(os.path.join(root, "Assets"))
if with_pkg:
pc = os.path.join(root, "Library", "PackageCache")
if os.path.isdir(pc):
self._walk(pc, only=(".shader.meta", ".shadergraph.meta", ".mat.meta", ".shadersubgraph.meta", ".hlsl.meta", ".asset.meta"))
def _walk(self, base, only=None):
for dp, dn, fn in os.walk(base):
for f in fn:
if not f.endswith(".meta") or (only and not f.endswith(only)):
continue
p = os.path.join(dp, f)
try:
with open(p, "r", encoding="utf-8", errors="ignore") as fh:
m = GUID_YAML.search(fh.read(400))
except OSError:
continue
if m:
self.g2p[m.group(1)] = os.path.normpath(p[:-5])
def read_text(p, n=None):
try:
with open(p, "r", encoding="utf-8", errors="ignore") as fh:
return fh.read(n) if n else fh.read()
except OSError:
return ""
class Scanner:
def __init__(self, wl_root):
self.wl = wl_root
self.assets = os.path.normpath(os.path.join(wl_root, "Assets"))
print("WL guid 색인 중…", flush=True)
self.idx = Index(wl_root, True)
print(" guid %d" % len(self.idx.g2p), flush=True)
self.tks = Index(TKS_ROOT, False)
self.deps_cache = {}
self.shader_cache = {}
self.script_cache = {}
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
self.skip_scripts = False
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
def in_pkg(self, p):
return "PackageCache" in p
def direct_deps(self, p):
if p in self.deps_cache:
return self.deps_cache[p]
ext = os.path.splitext(p)[1].lower()
out = set()
guids = set()
if ext in YAML_EXT:
guids.update(GUID_YAML.findall(read_text(p)))
elif ext in GRAPH_EXT:
guids.update(HEX32.findall(read_text(p)))
elif ext in SHADER_EXT:
d = os.path.dirname(p)
for inc in INCLUDE.findall(read_text(p)):
if inc.startswith("Packages/"):
continue
cand = os.path.normpath(os.path.join(self.wl, inc)) if inc.startswith("Assets/") else os.path.normpath(os.path.join(d, inc))
if os.path.isfile(cand):
out.add(cand)
meta = p + ".meta"
if os.path.isfile(meta):
for g in GUID_YAML.findall(read_text(meta))[1:]: # 첫 줄 = 자기 guid
guids.add(g)
for g in guids:
if g in BUILTIN_GUIDS:
continue
q = self.idx.g2p.get(g)
if q and os.path.normpath(q) != os.path.normpath(p):
out.add(q)
self.deps_cache[p] = out
return out
def closure(self, start):
seen = set()
stack = [os.path.normpath(start)]
while stack:
p = stack.pop()
if p in seen:
continue
seen.add(p)
if self.in_pkg(p):
continue # 패키지 안은 따라가지 않음(판정만)
for q in self.direct_deps(p):
if q not in seen:
stack.append(q)
return seen
def classify_shader(self, fileid, guid):
if guid is None or guid in BUILTIN_GUIDS:
return "builtin"
if guid in self.shader_cache:
return self.shader_cache[guid]
tp = self.tks.g2p.get(guid)
if tp and tp.endswith(".shader") and "com.unity.render-pipelines.universal" not in read_text(tp):
self.shader_cache[guid] = "port"
return "port"
path = self.idx.g2p.get(guid)
if not path:
res = "missing"
elif self.in_pkg(path):
res = "urp_pkg" if "render-pipelines.universal" in path else "srp_pkg"
elif path.endswith((".shadergraph", ".shadersubgraph")):
t = read_text(path)
res = "sg_builtin" if "BuiltInTarget" in t else ("sg_urp" if "UniversalTarget" in t else "sg_other")
elif path.endswith(".shader"):
t = read_text(path)
urp = "com.unity.render-pipelines.universal" in t
cg = ("UnityCG.cginc" in t) or ("CGPROGRAM" in t)
subs = t.count("SubShader")
tagged = len(re.findall(r'"RenderPipeline"\s*=\s*"UniversalPipeline"', t))
if urp and not cg:
res = "shader_urp"
elif urp and cg:
res = "shader_multi"
elif tagged and tagged >= subs:
res = "shader_urp_tag"
else:
res = "shader_builtin"
else:
res = "other"
self.shader_cache[guid] = res
return res
def script_risk(self, p):
if p in self.script_cache:
return self.script_cache[p]
bad = []
for ns in USING.findall(read_text(p)):
if ns.startswith(BAD_NS) or not ns.startswith(SAFE_NS):
bad.append(ns)
self.script_cache[p] = bad
return bad
def judge(self, prefab):
files = self.closure(prefab)
why = []
for f in files:
low = f.lower()
if self.in_pkg(f):
why.append("패키지 의존 " + os.path.basename(f))
continue
if low.endswith(".mat"):
m = SHADER_LINE.search(read_text(f, 6000))
cls = "noshader" if not m else self.classify_shader(m.group(1), m.group(2))
if cls not in OK_CLASSES:
why.append("%s:%s" % (cls, os.path.basename(f)))
elif low.endswith(".cs"):
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
if self.skip_scripts:
continue
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
bad = self.script_risk(f)
if bad:
why.append("스크립트 %s(%s)" % (os.path.basename(f), ",".join(sorted(set(bad)))))
size = 0
for f in files:
if not self.in_pkg(f):
try:
size += os.path.getsize(f)
except OSError:
pass
return files, why, size
def collect_prefabs(sc, targets):
out = []
for t in targets:
p = os.path.normpath(os.path.join(sc.assets, t))
if os.path.isfile(p) and p.lower().endswith(".prefab"):
out.append(p)
elif os.path.isdir(p):
for dp, dn, fn in os.walk(p):
# 데모 · 씬 폴더 프리팹은 제외(데모 연출용 · 카메라 · 조명)
if re.search(r"(^|[\\/])(Demo|Demos|Scene|Scenes|Showcase)([\\/]|$)", os.path.relpath(dp, p), re.I):
continue
for f in fn:
if f.lower().endswith(".prefab"):
out.append(os.path.join(dp, f))
else:
print(" 없음:", t)
return out
def unpack(pkg_path, dest, skip_scenes):
"""빌트인용 .unitypackage(WL 폴더 안 · 예: GAP UniqueToonProjectiles BIRP)를 GUID 째 <dest>/<경로>(앞 Assets/ 뗌)로 풀기.
같은 GUID 가 이미 있으면 건너뜀 · 경로는 Assets/ 로 시작하고 .. 가 없을 때만."""
import tarfile
tks = Index(TKS_ROOT, False)
entries = {}
with tarfile.open(pkg_path, "r:gz") as t:
for m in t.getmembers():
if not m.isfile():
continue
g, _, kind = m.name.partition("/")
entries.setdefault(g, {})[kind] = m
done = skip = 0
for g, e in entries.items():
if "pathname" not in e or "asset" not in e:
continue
path = t.extractfile(e["pathname"]).read().decode("utf-8").splitlines()[0].strip()
if not path.startswith("Assets/") or ".." in path.split("/"):
continue
if skip_scenes and path.lower().endswith(".unity"):
continue
if g in tks.g2p:
skip += 1
continue
dst = os.path.normpath(os.path.join(TKS_ROOT, dest, path[len("Assets/"):]))
os.makedirs(os.path.dirname(dst), exist_ok=True)
with open(dst, "wb") as fh:
fh.write(t.extractfile(e["asset"]).read())
if "asset.meta" in e:
with open(dst + ".meta", "wb") as fh:
fh.write(t.extractfile(e["asset.meta"]).read())
done += 1
print("풀기 %d · 이미 있음 %d → %s" % (done, skip, os.path.join(TKS_ROOT, dest)))
def main():
ap = argparse.ArgumentParser()
ap.add_argument("cmd", choices=["scan", "copy", "unpack"])
ap.add_argument("targets", nargs="+")
ap.add_argument("--wl", default=os.environ.get("WL_PROJECT_ROOT", ""))
ap.add_argument("--dest", default="Assets/VFX")
ap.add_argument("--list", action="store_true")
ap.add_argument("--report", default="")
ap.add_argument("--with-scenes", action="store_true", help="unpack: 데모 씬도 풀기")
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
ap.add_argument("--skip-scripts", action="store_true", help="프리팹이 쓰는 .cs 스크립트는 판정·복사에서 제외")
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
a = ap.parse_args()
if not a.wl or not os.path.isdir(os.path.join(a.wl, "Assets")):
sys.exit("WL 루트를 --wl 또는 환경 변수 WL_PROJECT_ROOT 로 주세요")
if a.cmd == "unpack":
for t in a.targets:
unpack(os.path.join(a.wl, "Assets", t), a.dest, not a.with_scenes)
return
sc = Scanner(a.wl)
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
sc.skip_scripts = a.skip_scripts
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
prefabs = collect_prefabs(sc, a.targets)
print("프리팹 %d" % len(prefabs), flush=True)
ok_files = set()
rows = []
per_pack = collections.OrderedDict()
for pf in prefabs:
files, why, size = sc.judge(pf)
rel = os.path.relpath(pf, sc.assets).replace("\\", "/")
pack = next((t for t in a.targets if rel.startswith(t.replace("\\", "/"))), "?")
st = per_pack.setdefault(pack, {"ok": 0, "ng": 0, "reasons": collections.Counter()})
if why:
st["ng"] += 1
for w in why:
st["reasons"][w.split(":")[0].split("(")[0]] += 1
else:
st["ok"] += 1
ok_files.update(f for f in files if not sc.in_pkg(f))
rows.append((rel, "OK" if not why else "NG", round(size / 1048576, 2), "; ".join(sorted(set(why)))[:300]))
if a.list:
print(" %s %s %.1fMB %s" % ("OK" if not why else "NG", rel, size / 1048576, "; ".join(sorted(set(why)))[:160]))
total = 0
for f in ok_files:
try:
total += os.path.getsize(f)
except OSError:
pass
print("\n팩별 판정")
for pk, st in per_pack.items():
print(" %-70s 통과 %4d · 탈락 %4d %s" % (pk, st["ok"], st["ng"], dict(st["reasons"].most_common(4))))
print("통과 프리팹 의존 파일 %d · %.1f MB" % (len(ok_files), total / 1048576))
if a.report:
os.makedirs(os.path.dirname(os.path.abspath(a.report)), exist_ok=True)
with open(a.report, "w", encoding="utf-8-sig", newline="") as fh:
w = csv.writer(fh)
w.writerow(["s_prefab", "e_result", "f_mb", "Memo"])
w.writerows(rows)
print("판정 표 →", a.report)
if a.cmd != "copy":
return
tks = Index(TKS_ROOT, False)
copied = skipped = 0
for src in sorted(ok_files):
rel = os.path.relpath(src, sc.assets).replace("\\", "/")
스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
if rel.startswith("..") or (a.skip_scripts and src.lower().endswith((".cs", ".dll"))):
스킬 이펙트 작업 준비 — 스킬 테스트 씬 · WL 이펙트 이식 · 이펙트 표 · 보병·창·기병 슬링샷 공격 = WL 창 돌진 이펙트 시험(PD 2026-11-09) PD 「WL 프로젝트에서 스킬을 구현하던 방식과 동일하게 유닛 별 스킬 구현」 · 「테스트 목적: 보병, 창, 기병을 슬링샷으로 공격할 때 WL 창 돌진 공격 이펙트」 - 스킬 테스트 씬 Assets/Scenes/SkillTest.unity(SkillTestBootstrap · 메뉴 TKS/Skill Test/Build Scene): 영웅 목록 선택(heroes.csv 56) · 더미(죽지 않음 · 행동 토글 · 배치 표 Resources/Skills/skill_test.csv) · 슬링샷 · 액티브 버튼 · 쿨 무시 · 영웅 무적 · 이펙트 보기(에디터 · Assets/VFX 검색 → 영웅에 띄움) - 이펙트 표 Resources/Skills/skill_vfx.csv → Runtime/Skills/SkillVfx(키 = 스킬 id 또는 basic_<병종> · cast/travel/hit · f_every 반복 · 사본 Resources/Fx/TKS_Fx_*) · 훅 = SkillSystem Announce/BeginAction/EndAction/Hit/Instant - WL 이펙트 이식 Assets/VFX(약 640 MB · 빌트인 호환분만 · GUID 유지 · 도구 Tools/wl_vfx_import.py) · URP 셰이더 빌트인 이식 4종 Assets/VFX/_BuiltinPorts · com.unity.shadergraph 17.3.0 · 카메라 깊이 텍스처 켬(셰이더 그래프 소프트 파티클) - 시험: basic_infantry/spear/cavalry travel = TKS_Fx_SpearRush · SpearRushWrap · SpearRushDust(WL WLStaffEarth rushFx 1~3 값 환산) — 표 9행 삭제 = 원래대로 - 테스트 118/118 · console error 0 · 이 PC 첫 임포트 자동 재직렬화(png·tga .meta · UITheme · ProjectSettings · InputManager)는 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:12:52 +00:00
continue
dst = os.path.normpath(os.path.join(TKS_ROOT, a.dest, strip_rel(rel)))
meta = src + ".meta"
g = GUID_YAML.search(read_text(meta, 400))
g = g.group(1) if g else None
if g and g in tks.g2p:
skipped += 1
continue
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
if os.path.isfile(meta):
shutil.copy2(meta, dst + ".meta")
copied += 1
print("복사 %d · 이미 있음 %d → %s" % (copied, skipped, os.path.join(TKS_ROOT, a.dest)))
if __name__ == "__main__":
main()