Project_WL/AgentScripts/WL_CombatAnimSetup.cs

401 lines
24 KiB
C#
Raw Normal View History

WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// ─────────────────────────────────────────────────────────────────────────────
// WL_CombatAnimSetup.cs — 클래스 컨트롤러의 공격 모션 교체 + 이벤트 재기록 (에디터 전용)
//
// PD 지시 #760 (2026-09-06) 1단계 산출물 · **스테이징**(아직 Assets/ 밖).
// 호출 = Unity CLI: unity command run_script --file AgentScripts/WL_CombatAnimSetup.cs
// --entry WL_CombatAnimSetup.Run --args '["dryRun"]'
//
// ── 왜 이 순서인가 ────────────────────────────────────────────────────────────
// 이벤트가 전투의 심장이다(INVENTORY §3): Projectile = 타격 판정, ShowEffect = 트레일 신호.
// 따라서 "클립을 바꾼다"가 아니라 "이벤트를 실은 클립을 만들고 상태에 물린다" 가 본체다.
//
// ── 이벤트를 어디에 심는가 (실측으로 고른 방식) ───────────────────────────────
// 후보 ① AnimationUtility.SetAnimationEvents(clip, events)
// → FBX 에서 임포트된 클립은 **읽기 전용**이다. 호출은 되지만 재임포트 때 사라진다.
// .anim 사본에만 유효. ✕ 채택하지 않음
// 후보 ② ModelImporter.clipAnimations[i].events = … → SaveAndReimport()
// → FBX 임포터가 클립을 구울 때 이벤트를 함께 굽는다. **영속적**이고
// 한 FBX 에서 이름·이벤트가 다른 서브클립을 여러 개 뽑을 수 있다(MAPPING §3-1).
// ✔ **채택**
// ※ ②는 SerializedObject 우회 없이 공개 API 로 된다. 단 clipAnimations 는 배열 복사본을
// 돌려주므로 **통째로 다시 대입**해야 반영된다(Unity 의 오래된 함정).
//
// ── 안전장치 ─────────────────────────────────────────────────────────────────
// · dryRun 기본 ON. 실행하려면 인자에 "apply" 를 준다.
// · 컨트롤러 13종 + 손대는 FBX 의 .meta 를 backupRoot 로 먼저 복사한다(C8 롤백 경로).
// · Play 중이면 즉시 중단한다(#746 사고 재발 방지).
// · 원본 클립(10502 등 기존 클래스가 쓰는 것)은 **절대 수정하지 않는다** — 항상 서브클립 추가.
// ─────────────────────────────────────────────────────────────────────────────
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
public static class WL_CombatAnimSetup
{
// ── 진입점 ──────────────────────────────────────────────────────────────
// args: "dryRun"(기본) | "apply" | "report"
// "mapping=<경로>" 기본 Assets/WL/Combat/CombatAnimMapping.asset
public static string Run(string[] args)
{
var sb = new StringBuilder();
bool apply = args != null && Array.IndexOf(args, "apply") >= 0;
bool reportOnly = args != null && Array.IndexOf(args, "report") >= 0;
string mappingPath = "Assets/WL/Combat/CombatAnimMapping.asset";
if (args != null)
foreach (var a in args)
if (a != null && a.StartsWith("mapping=")) mappingPath = a.Substring(8);
// 1) 가드 — Play 중이면 손대지 않는다 (#746)
if (UnityEditor.EditorApplication.isPlaying || UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
return "ABORT: 에디터가 Play 중이다. PD 테스트 중일 수 있으므로 중단한다.";
if (UnityEditor.EditorApplication.isCompiling)
return "ABORT: 컴파일 중이다. recompile_status 가 completed 가 된 뒤 다시 호출할 것.";
// 2) 매핑 데이터 로드 (C45 — 값은 전부 여기서 온다)
var mapping = UnityEditor.AssetDatabase.LoadAssetAtPath<WL.Combat.CombatAnimMapping>(mappingPath);
if (mapping == null) return "ABORT: 매핑 에셋을 찾지 못했다 — " + mappingPath;
sb.AppendLine("WL_CombatAnimSetup mode=" + (reportOnly ? "report" : apply ? "APPLY" : "dryRun"));
sb.AppendLine("mapping=" + mappingPath + " classes=" + mapping.classes.Count);
// 3) 클래스 테이블에서 컨트롤러 경로를 읽는다 (SOT = ClassConfig.json · C45)
var ctrlByClass = LoadControllerPathsFromClassConfig(sb);
// 4) 백업 (C8) — apply 일 때만
string backupDir = null;
if (apply && !reportOnly)
{
backupDir = ResolveBackupDir(mapping.backupRoot);
Directory.CreateDirectory(backupDir);
sb.AppendLine("backup -> " + backupDir);
}
int touchedClips = 0, touchedStates = 0, errors = 0;
foreach (var spec in mapping.classes)
{
if (spec == null || !spec.enabled) continue;
string ctrlPath = !string.IsNullOrEmpty(spec.controllerPathOverride)
? spec.controllerPathOverride
: (ctrlByClass.ContainsKey(spec.classId) ? ctrlByClass[spec.classId] : null);
if (string.IsNullOrEmpty(ctrlPath))
{
sb.AppendLine(" [ERR] " + spec.classId + " 컨트롤러 경로 미해결"); errors++; continue;
}
var ctrl = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(ctrlPath);
if (ctrl == null) { sb.AppendLine(" [ERR] 컨트롤러 로드 실패 " + ctrlPath); errors++; continue; }
sb.AppendLine("── " + spec.classId + " " + spec.note + " (" + Path.GetFileName(ctrlPath) + ")");
if (apply && !reportOnly && backupDir != null) BackupFile(ctrlPath, backupDir, sb);
UnityEditor.Animations.AnimatorState prevState = null; // #839 체인 연결용(직전 행의 상태)
bool prevCreated = false; // #840 직전 행이 이번 apply 에서 신설된 상태인가
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
foreach (var st in spec.states)
{
if (st == null || string.IsNullOrEmpty(st.sourceFbxPath)) continue;
// 4-1) 서브클립 보장 (이벤트 포함) — ModelImporter 경유
string clipName = string.IsNullOrEmpty(st.subClipName)
? st.sourceClipName
: st.subClipName.Replace("{CID}", spec.classId.ToString());
var clip = EnsureSubClip(st, spec.classId, clipName, apply && !reportOnly, backupDir, sb, ref errors);
if (clip != null) touchedClips++;
// 4-2) 상태에 물리기
var state = FindState(ctrl, st.stateName);
bool createdNow = false;
if (state != null && prevState != null && prevCreated && apply && !reportOnly)
{
// #840 — 직전 행이 신설 상태(→idle 로 닫혀 있음)이고 이번 행이 기존 상태면, 직전 → 기존 으로 다시 잇는다(창 Attack2b/2c → Attack3).
foreach (var old in prevState.transitions.ToArray()) prevState.RemoveTransition(old);
var into2 = prevState.AddTransition(state);
into2.hasExitTime = true; into2.exitTime = st.incomingExitTime > 0f ? st.incomingExitTime : 0.8f; into2.duration = mapping.transitionDuration; into2.hasFixedDuration = false;
sb.AppendLine(" ~ 전이 재연결 " + prevState.name + " → " + st.stateName);
}
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
if (state == null && st.createIfMissing && apply && !reportOnly)
{
createdNow = true;
// #839 — 새 콤보 상태: 직전 행 상태의 나가는 전이를 걷어내고 직전 → 새 → idle 로 잇는다(마법 3타 · 대검 4타).
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
state = ctrl.layers[0].stateMachine.AddState(st.stateName);
if (prevState != null)
{
foreach (var old in prevState.transitions.ToArray()) prevState.RemoveTransition(old);
var into = prevState.AddTransition(state);
into.hasExitTime = true; into.exitTime = st.incomingExitTime > 0f ? st.incomingExitTime : 0.8f; into.duration = mapping.transitionDuration; into.hasFixedDuration = false;
var idle = FindState(ctrl, "idle");
if (idle != null) { var outT = state.AddTransition(idle); outT.hasExitTime = true; outT.exitTime = 0.8f; outT.duration = 0.25f; outT.hasFixedDuration = true; }
sb.AppendLine(" + 상태 생성 " + st.stateName + " (" + prevState.name + " → " + st.stateName + " → idle)");
}
else sb.AppendLine(" + 상태 생성 " + st.stateName + " (직전 상태 없음 · 전이 미연결)");
}
else if (state == null && st.createIfMissing)
sb.AppendLine(" (dryRun) 상태 생성 예정 " + st.stateName);
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
if (state == null) { sb.AppendLine(" [WARN] 상태 없음: " + st.stateName); continue; }
if (!st.stateName.Equals("idle", StringComparison.OrdinalIgnoreCase)) { prevState = state; prevCreated = createdNow; }
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
sb.AppendLine(" " + st.stateName + " motion: " +
(state.motion != null ? state.motion.name : "(null)") + " -> " + clipName +
" events=" + st.events.Count);
if (apply && !reportOnly && clip != null)
{
state.motion = clip;
if (st.speed > 0f) state.speed = st.speed; // #834 상태 재생 속도(비수 3타 등)
if (state.mirror != (st.mirror != 0)) { state.mirror = st.mirror != 0; state.mirrorParameterActive = false; sb.AppendLine(" 거울 = " + (st.mirror != 0 ? "켬" : "끔") + " → " + state.name); } // #948-6 빈손 내뻗기(오른손 클립 거울)
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
touchedStates++;
}
// 4-3) 들어오는 전이 exitTime 조정
if (st.incomingExitTime > 0f && apply && !reportOnly)
SetIncomingExitTime(ctrl, state, st.incomingExitTime, mapping.transitionDuration, sb);
}
}
if (apply && !reportOnly)
{
UnityEditor.AssetDatabase.SaveAssets();
UnityEditor.AssetDatabase.Refresh();
}
sb.AppendLine("결과: clips=" + touchedClips + " states=" + touchedStates + " errors=" + errors +
(apply ? "" : " (dryRun — 아무것도 쓰지 않았다)"));
return sb.ToString();
}
// #948 — 매핑 행의 maskPath(AvatarMask 에셋)를 임포터 서브클립에 씌운다(CopyFromOther). 비우면 원본 설정 그대로.
// 상체 마스크(루트·다리 제외) = 지팡이 3타를 점프 없이 제자리에서 상체만(PD 「하체는 서 있는 상태에서 상체만」).
private static void ApplyMask(UnityEditor.ModelImporterClipAnimation c, WL.Combat.StateClipSpec st, StringBuilder sb)
{
if (c == null || st == null) return;
if (st.bakeRootY >= 0) // #948 Root Y Bake Into Pose 덮기(0 = 점프 제거)
{
c.lockRootHeightY = st.bakeRootY != 0;
sb.AppendLine(" 루트 Y Bake Into Pose = " + (st.bakeRootY != 0 ? "켬" : "끔") + " → " + c.name);
}
if (string.IsNullOrEmpty(st.maskPath)) return;
var mask = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AvatarMask>(st.maskPath);
if (mask == null) { sb.AppendLine(" [WARN] 마스크 에셋 없음: " + st.maskPath); return; }
c.maskType = UnityEditor.ClipAnimationMaskType.CopyFromOther;
c.maskSource = mask;
sb.AppendLine(" 마스크 " + mask.name + " → " + c.name);
}
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// ── 서브클립 보장 ────────────────────────────────────────────────────────
// FBX 임포터의 clipAnimations 에 <clipName> 이 없으면 원본 클립 구간을 복제해 추가하고,
// events 를 매핑대로 다시 쓴다. 이미 있으면 events 만 갱신한다.
// ※ 기존 클립(다른 클래스가 쓰는 것)은 건드리지 않는다.
private static UnityEngine.AnimationClip EnsureSubClip(
WL.Combat.StateClipSpec st, int classId, string clipName,
bool write, string backupDir, StringBuilder sb, ref int errors)
{
// #838 — FR(FirstRising) .anim 에셋(휴머노이드 클립)을 소스로 받는다: 클래스별 사본(.anim)을 만들고 이벤트를 **초** 단위로 굽는다.
if (st.sourceFbxPath.EndsWith(".anim", StringComparison.OrdinalIgnoreCase))
return EnsureAnimCopy(st, classId, clipName, write, backupDir, sb, ref errors);
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
var importer = UnityEditor.AssetImporter.GetAtPath(st.sourceFbxPath) as UnityEditor.ModelImporter;
if (importer == null) { sb.AppendLine(" [ERR] ModelImporter 없음: " + st.sourceFbxPath); errors++; return null; }
var clips = importer.clipAnimations;
if (clips == null || clips.Length == 0) clips = importer.defaultClipAnimations; // 아직 손대지 않은 FBX
if (clips == null || clips.Length == 0) { sb.AppendLine(" [ERR] 클립 0개: " + st.sourceFbxPath); errors++; return null; }
var srcName = string.IsNullOrEmpty(st.sourceClipName) ? clips[0].name : st.sourceClipName;
var src = clips.FirstOrDefault(c => c.name == srcName) ?? clips[0];
// #839 — 프레임 범위 덮기(마법 1·2타 = 발사 구간만 등). 지정 시 이벤트 정규화는 이 범위 기준.
bool rangeOverride = st.lastFrame > 0 && st.firstFrame >= 0 && st.lastFrame > st.firstFrame;
float useFirst = rangeOverride ? st.firstFrame : src.firstFrame;
float useLast = rangeOverride ? st.lastFrame : src.lastFrame;
float clipLength = (useLast - useFirst) / 30f; // 실측: 전 클립 sampleRate 30
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
var evs = st.events.Select(e => new UnityEngine.AnimationEvent
{
functionName = e.functionName,
// #828 — 임포터(clipAnimations) 이벤트의 time 은 **정규화(0~1)** 다. 초(= 정규화 × 길이)로 쓰면 길이가 1 s 가
// 아닌 클립에서 실효 시각이 길이 배만큼 어긋난다(09-07 실측: 매핑 0.25 → 메타 0.2667 → 런타임 0.279).
// 정규화 지정은 그대로, 초 지정은 길이로 나눠 정규화한다.
time = e.useNormalized ? UnityEngine.Mathf.Clamp01(e.normalizedTime)
: (clipLength > 0f ? UnityEngine.Mathf.Clamp01(e.time / clipLength) : e.time),
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
stringParameter = e.ResolveString(classId),
intParameter = e.intParameter,
floatParameter = e.floatParameter,
}).OrderBy(e => e.time).ToArray();
var list = clips.ToList();
int idx = list.FindIndex(c => c.name == clipName);
if (idx < 0)
{
var copy = new UnityEditor.ModelImporterClipAnimation
{
name = clipName,
takeName = src.takeName,
firstFrame = useFirst,
lastFrame = useLast,
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
loopTime = src.loopTime,
loopPose = src.loopPose,
lockRootRotation = src.lockRootRotation,
lockRootHeightY = src.lockRootHeightY,
lockRootPositionXZ = src.lockRootPositionXZ,
keepOriginalOrientation = src.keepOriginalOrientation,
keepOriginalPositionY = src.keepOriginalPositionY,
keepOriginalPositionXZ = src.keepOriginalPositionXZ,
heightFromFeet = src.heightFromFeet,
cycleOffset = src.cycleOffset,
maskType = src.maskType,
events = evs,
};
ApplyMask(copy, st, sb); // #948
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
list.Add(copy);
sb.AppendLine(" + 서브클립 생성 " + clipName + " (" + src.name + " 복제 · " +
src.firstFrame + "~" + src.lastFrame + "f · " + clipLength.ToString("0.000") + "s)");
}
else
{
var c = list[idx];
c.events = evs;
if (rangeOverride) { c.firstFrame = useFirst; c.lastFrame = useLast; }
ApplyMask(c, st, sb); // #948
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
list[idx] = c;
sb.AppendLine(" ~ 서브클립 이벤트 갱신 " + clipName + (rangeOverride ? " (범위 " + useFirst + "~" + useLast + "f)" : ""));
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
}
if (write)
{
if (backupDir != null) BackupFile(st.sourceFbxPath + ".meta", backupDir, sb);
importer.clipAnimations = list.ToArray(); // ★ 통째 대입해야 반영된다
importer.SaveAndReimport();
}
// 재임포트 후에 서브에셋에서 찾는다
foreach (var o in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(st.sourceFbxPath))
{
var ac = o as UnityEngine.AnimationClip;
if (ac != null && ac.name == clipName) return ac;
}
if (write) { sb.AppendLine(" [ERR] 재임포트 후 클립 미발견: " + clipName); errors++; }
return null;
}
// ── .anim 사본 (#838 FR 활) ─────────────────────────────────────────────
// FBX 임포터 경로와 달리 .anim 에셋은 AnimationUtility.SetAnimationEvents 가 영속된다(사본에만 쓴다 · 원본 FR 클립 무수정).
// AnimationEvent.time 은 초 — 매핑의 정규화 시각은 클립 길이를 곱한다(임포터 서브클립의 정규화 규칙과 반대 · #828 참조).
private static UnityEngine.AnimationClip EnsureAnimCopy(
WL.Combat.StateClipSpec st, int classId, string clipName,
bool write, string backupDir, StringBuilder sb, ref int errors)
{
var src = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AnimationClip>(st.sourceFbxPath);
if (src == null) { sb.AppendLine(" [ERR] .anim 없음: " + st.sourceFbxPath); errors++; return null; }
string dir = Path.GetDirectoryName(st.sourceFbxPath).Replace('\\', '/');
string copyPath = dir + "/" + clipName + ".anim";
var copy = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AnimationClip>(copyPath);
float len = src.length;
var evs = st.events.Select(e => new UnityEngine.AnimationEvent
{
functionName = e.functionName,
time = e.useNormalized ? UnityEngine.Mathf.Clamp01(e.normalizedTime) * len : UnityEngine.Mathf.Clamp(e.time, 0f, len),
stringParameter = e.ResolveString(classId),
intParameter = e.intParameter,
floatParameter = e.floatParameter,
}).OrderBy(e => e.time).ToArray();
if (!write)
{
sb.AppendLine(" " + (copy == null ? "+ .anim 사본 생성 예정 " : "~ .anim 사본 이벤트 갱신 예정 ") + clipName +
" (" + src.name + " · " + len.ToString("0.000") + "s · events " + evs.Length + ")");
return copy;
}
if (copy == null)
{
if (!UnityEditor.AssetDatabase.CopyAsset(st.sourceFbxPath, copyPath)) { sb.AppendLine(" [ERR] .anim 복사 실패: " + copyPath); errors++; return null; }
copy = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AnimationClip>(copyPath);
if (copy == null) { sb.AppendLine(" [ERR] 복사본 로드 실패: " + copyPath); errors++; return null; }
sb.AppendLine(" + .anim 사본 생성 " + clipName + " (" + src.name + " · " + len.ToString("0.000") + "s)");
}
else
{
if (backupDir != null) BackupFile(copyPath, backupDir, sb);
sb.AppendLine(" ~ .anim 사본 이벤트 갱신 " + clipName);
}
copy.name = clipName;
UnityEditor.AnimationUtility.SetAnimationEvents(copy, evs);
UnityEditor.EditorUtility.SetDirty(copy);
return copy;
}
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// ── 유틸 ────────────────────────────────────────────────────────────────
private static UnityEditor.Animations.AnimatorState FindState(
UnityEditor.Animations.AnimatorController ctrl, string name)
{
foreach (var layer in ctrl.layers)
foreach (var cs in layer.stateMachine.states)
if (cs.state != null && cs.state.name == name) return cs.state;
return null;
}
private static void SetIncomingExitTime(
UnityEditor.Animations.AnimatorController ctrl, UnityEditor.Animations.AnimatorState dst,
float exitTime, float duration, StringBuilder sb)
{
foreach (var layer in ctrl.layers)
foreach (var cs in layer.stateMachine.states)
{
if (cs.state == null) continue;
foreach (var t in cs.state.transitions)
{
if (t.destinationState != dst) continue;
t.hasExitTime = true;
t.exitTime = exitTime;
t.duration = duration;
sb.AppendLine(" transition " + cs.state.name + " -> " + dst.name +
" exit=" + exitTime + " dur=" + duration);
}
}
}
private static Dictionary<int, string> LoadControllerPathsFromClassConfig(StringBuilder sb)
{
// SOT = 테이블(JSON). 코드에 경로를 박지 않는다(C45).
var map = new Dictionary<int, string>();
const string p = "Assets/ResWork/Table/Export/ClassConfig.json";
if (!File.Exists(p)) { sb.AppendLine("[WARN] ClassConfig.json 없음 — controllerPathOverride 필요"); return map; }
string txt = File.ReadAllText(p, new UTF8Encoding(false));
// 최소 파서: "n_ClassID":"10101" … "s_AnimationController":"Assets/…"
var idRx = new System.Text.RegularExpressions.Regex("\"n_ClassID\"\\s*:\\s*\"?(\\d+)\"?");
var acRx = new System.Text.RegularExpressions.Regex("\"s_AnimationController\"\\s*:\\s*\"([^\"]+)\"");
var ids = idRx.Matches(txt); var acs = acRx.Matches(txt);
int n = Math.Min(ids.Count, acs.Count);
for (int i = 0; i < n; i++)
map[int.Parse(ids[i].Groups[1].Value)] = acs[i].Groups[1].Value;
sb.AppendLine("ClassConfig 에서 컨트롤러 경로 " + map.Count + "건 로드");
return map;
}
private static string ResolveBackupDir(string template)
{
string stamp = DateTime.Now.ToString("yyyyMMdd_HHmm");
string rel = (template ?? "공유/개발팀_백업/WL/Animators_{yyyyMMdd_HHmm}").Replace("{yyyyMMdd_HHmm}", stamp);
// 프로젝트 루트(Assets 의 부모) 기준 — Assets/ 밖에 남긴다(임포트 유발 방지)
string root = Directory.GetParent(UnityEngine.Application.dataPath).FullName;
return Path.Combine(root, rel.Replace('/', Path.DirectorySeparatorChar));
}
private static void BackupFile(string assetPath, string backupDir, StringBuilder sb)
{
try
{
string abs = Path.Combine(Directory.GetParent(UnityEngine.Application.dataPath).FullName, assetPath);
if (!File.Exists(abs)) return;
string dst = Path.Combine(backupDir, assetPath.Replace('/', '_'));
if (!File.Exists(dst)) File.Copy(abs, dst);
}
catch (Exception e) { sb.AppendLine(" [WARN] 백업 실패 " + assetPath + " : " + e.Message); }
}
}