Project_WL/AgentScripts/WL787_Anim.cs

178 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// WL787_Anim.cs — PD 지시 #787 대쉬 상태 추가 (에디트 모드 · UnityEditor.Animations API)
// unity command run_script --file AgentScripts/WL787_Anim.cs --entry WL787_Anim.Dump
// unity command run_script --file AgentScripts/WL787_Anim.cs --entry WL787_Anim.Apply
// unity command run_script --file AgentScripts/WL787_Anim.cs --entry WL787_Anim.Remove (롤백)
//
// 하는 일: 근접 4컨트롤러(ClassConfig 의 OneHand·Shield)의 Base Layer 에
// · 상태 `dash` 를 추가하고 Stander@Chase_Start 의 클립을 물린다
// · `dash -> idle` 전이(hasExitTime 1.0 · 0.15s)를 하나 건다 — 대쉬가 그냥 끝났을 때 자세가 굳지 않게
// 진입은 전이가 아니라 DashDriver 의 Animator.CrossFadeInFixedTime 이 한다.
// → Any State 전이를 만들지 않으므로 기존 상태 전이망을 전혀 건드리지 않는다(회귀 위험 최소).
//
// 🔴 이름 충돌 주의: 이 레포에는 전역 네임스페이스에 MonoBehaviour `AnimatorController` 가 있다.
// 반드시 UnityEditor.Animations.AnimatorController 로 **완전 한정**해서 쓴다.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
public static class WL787_Anim
{
const string ClassTable = "Assets/ResWork/Table/Export/ClassConfig.json";
const string ChaseFbx = "Assets/Res_Addr/Animations/Animation/Common_PlatformAnim/Stander@Chase_Start.FBX";
const string ChaseClip = "Chase_Start";
const string DashState = "dash";
const string IdleState = "idle";
static string[] TargetControllers()
{
var txt = System.IO.File.ReadAllText(ClassTable).TrimStart('');
var list = new List<string>();
foreach (System.Text.RegularExpressions.Match m in
System.Text.RegularExpressions.Regex.Matches(txt, "\\{[^{}]*\\}"))
{
string row = m.Value;
var bt = System.Text.RegularExpressions.Regex.Match(row, "\"e_BattleType\":\\s*\"([^\"]*)\"");
var ac = System.Text.RegularExpressions.Regex.Match(row, "\"s_AnimationController\":\\s*\"([^\"]*)\"");
if (!bt.Success || !ac.Success) continue;
if (bt.Groups[1].Value != "OneHand" && bt.Groups[1].Value != "Shield") continue;
if (!list.Contains(ac.Groups[1].Value)) list.Add(ac.Groups[1].Value);
}
return list.ToArray();
}
static AnimationClip LoadChaseClip()
{
foreach (var o in AssetDatabase.LoadAllAssetsAtPath(ChaseFbx))
{
var c = o as AnimationClip;
if (c != null && c.name == ChaseClip) return c;
}
return null;
}
static UnityEditor.Animations.AnimatorState Find(
UnityEditor.Animations.AnimatorController c, string name)
{
foreach (var layer in c.layers)
foreach (var cs in layer.stateMachine.states)
if (cs.state != null && cs.state.name == name) return cs.state;
return null;
}
public static object Dump()
{
var sb = new StringBuilder();
var clip = LoadChaseClip();
sb.AppendLine("clip " + ChaseClip + " = " + (clip != null ? clip.name + " len=" + clip.length.ToString("0.000") + "s" : "(NOT FOUND)"));
foreach (var path in TargetControllers())
{
var c = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(path);
if (c == null) { sb.AppendLine("MISSING " + path); continue; }
var dash = Find(c, DashState);
sb.AppendLine("## " + System.IO.Path.GetFileName(path) +
" states=" + c.layers[0].stateMachine.states.Length +
" dash=" + (dash == null ? "(없음)" :
(dash.motion != null ? dash.motion.name : "(motion null)") +
" out=" + dash.transitions.Length));
}
return sb.ToString();
}
public static object Apply()
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
if (EditorApplication.isCompiling) return "ABORT: 컴파일 중";
var clip = LoadChaseClip();
if (clip == null) return "ABORT: 클립을 못 찾았다 — " + ChaseFbx + " / " + ChaseClip;
var sb = new StringBuilder("clip=" + clip.name + " len=" + clip.length.ToString("0.000") + "s\n");
int added = 0, wired = 0;
foreach (var path in TargetControllers())
{
var c = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(path);
if (c == null) { sb.AppendLine("MISSING " + path); continue; }
var sm = c.layers[0].stateMachine;
var dash = Find(c, DashState);
if (dash == null)
{
// idle 아래쪽에 눈에 띄게 놓는다(에디터에서 사람이 찾기 쉽게).
var idleForPos = Find(c, IdleState);
Vector3 pos = new Vector3(-260f, 260f, 0f);
foreach (var cs in sm.states)
if (idleForPos != null && cs.state == idleForPos) pos = cs.position + new Vector3(0f, 130f, 0f);
dash = sm.AddState(DashState, pos);
added++;
sb.AppendLine(" + 상태 추가 dash (" + System.IO.Path.GetFileName(path) + ")");
}
dash.motion = clip;
dash.speed = 1f;
dash.writeDefaultValues = false;
// dash -> idle : 대쉬가 공격으로 이어지지 않고 그냥 끝났을 때의 안전 출구.
var idle = Find(c, IdleState);
if (idle != null && !dash.transitions.Any(t => t.destinationState == idle))
{
var tr = dash.AddTransition(idle);
tr.hasExitTime = true;
tr.exitTime = 1f;
tr.hasFixedDuration = true;
tr.duration = 0.15f;
tr.offset = 0f;
wired++;
sb.AppendLine(" + 전이 dash -> idle exit=1.00 dur=0.15");
}
else if (idle == null) sb.AppendLine(" [WARN] idle 상태를 못 찾음 — 출구 전이 생략 " + path);
EditorUtility.SetDirty(c);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
sb.AppendLine("결과: 상태추가=" + added + " 전이추가=" + wired);
return sb.ToString();
}
/// <summary>
/// WLCombatMotionSettings.asset 을 다시 직렬화해 #787 신규 필드를 YAML 에 실제로 적어 둔다.
/// (없어도 C# 필드 초기값으로 동작하지만, PD·PM 이 파일에서 값을 읽고 고칠 수 있어야 한다 · C45)
/// </summary>
public static object TouchSettings()
{
const string p = "Assets/WL/Settings/Resources/WL/WLCombatMotionSettings.asset";
var s = AssetDatabase.LoadAssetAtPath<WL.Combat.WLCombatMotionSettings>(p);
if (s == null) return "ABORT: " + p + " 없음";
EditorUtility.SetDirty(s);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return "OK dashEnabled=" + s.dashEnabled + " min=" + s.dashMinDistance + " max=" + s.dashMaxDistance +
" stop=" + s.dashStopDistance + " speed=" + s.dashMaxSpeed + " maxSec=" + s.dashMaxSeconds +
" look=" + s.dashLookAtTarget + " cool=" + s.dashCooldownSeconds +
" state=" + s.dashStateName + " clip=" + s.dashClipName;
}
/// <summary>롤백 — dash 상태를 지운다(전이도 함께 사라진다).</summary>
public static object Remove()
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var sb = new StringBuilder();
foreach (var path in TargetControllers())
{
var c = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(path);
if (c == null) continue;
var dash = Find(c, DashState);
if (dash == null) { sb.AppendLine(" (없음) " + System.IO.Path.GetFileName(path)); continue; }
c.layers[0].stateMachine.RemoveState(dash);
EditorUtility.SetDirty(c);
sb.AppendLine(" - 삭제 dash " + System.IO.Path.GetFileName(path));
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return sb.ToString();
}
}