Project_WL/AgentScripts/WL922_Probe.cs

158 lines
11 KiB
C#

// WL922_Probe.cs — PD 지시 #922 시험(Play 중 · 섬)
// PoseClip(fbxPath, timesCsv, outPrefix, side) : FBX 안 첫 애니메이션 클립을 PC 에 입혀(PlayableGraph · 원본 애니메이터 무수정) 시각(초)마다 옆에서 찍는다 — 클립 길이 · 이벤트 없음
using System.Text;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
public static class WL922_Probe
{
const string Key = "wl922log";
static StringBuilder Buf()
{
var sb = System.AppDomain.CurrentDomain.GetData(Key) as StringBuilder;
if (sb == null) { sb = new StringBuilder(); System.AppDomain.CurrentDomain.SetData(Key, sb); }
return sb;
}
public static object Log() { return Buf().ToString(); }
static float s_t0; static StringBuilder s_sb;
static void OnStage(in WL.Combat.Core.ComboStageEvent e) { if (s_sb != null) s_sb.AppendFormat("{0:F2} 단계 {1} · 클립 {2}", Time.time - s_t0, e.stage, e.clip).Append('\n'); }
/// <summary>#922 속사 모으기 중 이동 — Q 발동 뒤 moveAt 초부터 moveSec 초 동안 이동 입력 · PC 이동 거리(모으기 동안 / 그 뒤) · 이동 막힘 수 · 속사 발 수.</summary>
public static object RapidMove(float moveAt, float moveSec, float seconds)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
var sb = Buf(); sb.Length = 0; float t0 = Time.time; var p0 = pc.Get_position(); bool moving = false, moved = false; Vector3 pc0 = Vector3.zero;
int mb0 = WL.Combat.Weapon.WLSkillCancel.MoveBlocked, s0 = WL.Combat.Weapon.WLRapidShot.Shots, ab0 = WL.Combat.Weapon.WLRapidShot.Aborted;
var dir = Quaternion.Euler(0f, 90f, 0f) * pc.transform.forward; dir.y = 0f; dir.Normalize();
string res = WL.Combat.Weapon.WLSkillHotkey.Fire(1);
UnityEngine.Events.UnityAction act = null; float chargeEndDist = -1f;
act = () =>
{
float t = Time.time - t0;
if (!moved && t >= moveAt)
{
if (!moving) { pc.Set_AutoBraking(false); pc.Set_JoystickStatus(1); moving = true; sb.AppendFormat("{0:F2} ▶ 이동 입력 · 상태 {1}", t, pc.Get_CurAnim()).Append('\n'); }
pc.Run_byJoystick(dir);
if (t >= moveAt + moveSec) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); moving = false; moved = true; sb.AppendFormat("{0:F2} ■ 이동 끝 · 상태 {1} · 이동 {2:F2} m", t, pc.Get_CurAnim(), Flat(pc.Get_position() - p0)).Append('\n'); }
}
if (chargeEndDist < 0f && WL.Combat.Weapon.WLRapidShot.Shots > s0) { chargeEndDist = Flat(pc.Get_position() - p0); sb.AppendFormat("{0:F2} 첫 발(모으기 끝) · 그때까지 이동 {1:F2} m", t, chargeEndDist).Append('\n'); }
if (t > seconds)
{
Application.onBeforeRender -= act; if (moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); }
sb.AppendFormat("끝 · 이동 막힘 +{0} · 속사 발 +{1} · 끊김 +{2} · 총 이동 {3:F2} m · {4}", WL.Combat.Weapon.WLSkillCancel.MoveBlocked - mb0, WL.Combat.Weapon.WLRapidShot.Shots - s0, WL.Combat.Weapon.WLRapidShot.Aborted - ab0, Flat(pc.Get_position() - p0), res).Append('\n');
}
};
Application.onBeforeRender += act;
return "속사 + 이동 시험";
}
static float Flat(Vector3 v) { v.y = 0f; return v.magnitude; }
static int s_wantStage; static float s_pauseAt = -1f, s_delay;
static void OnStagePause(in WL.Combat.Core.ComboStageEvent e) { if (e.stage == s_wantStage && s_pauseAt < 0f) s_pauseAt = Time.time + s_delay; }
/// <summary>콤보 단계 stage 가 시작되고 delay 초 뒤 일시 정지(호 이펙트 캡처용).</summary>
public static object PauseStage(int stage, float delay, float maxSeconds)
{
var sb = Buf(); sb.Length = 0; s_wantStage = stage; s_delay = delay; s_pauseAt = -1f; float t0 = Time.time;
WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStagePause); WL.Combat.Core.CombatEvents.ComboStage.Add(OnStagePause);
UnityEngine.Events.UnityAction act = null;
act = () =>
{
if ((s_pauseAt > 0f && Time.time >= s_pauseAt) || Time.time - t0 > maxSeconds)
{
Application.onBeforeRender -= act; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStagePause);
if (s_pauseAt > 0f) UnityEditor.EditorApplication.isPaused = true;
sb.Append(s_pauseAt > 0f ? "■ 일시 정지 단계 " + stage : "못 찾음").Append(" · 호 기울임 ").Append(WL.Combat.WeaponTrailDriver.ArcTilted).Append('\n');
}
};
Application.onBeforeRender += act;
return "단계 " + stage + " 대기";
}
/// <summary>지면 이펙트(WLImpactFx.GroundShown)가 늘면 delay 초 뒤 일시 정지 · 그때 칼끝(대검 소켓 그림 끝) 위치와 이펙트 위치(LastGround) 기록.</summary>
public static object PauseGround(float delay, float maxSeconds)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
var sb = Buf(); sb.Length = 0; float t0 = Time.time; int g0 = WL.Combat.Impact.WLImpactFx.GroundShown; float at = -1f;
UnityEngine.Events.UnityAction act = null;
act = () =>
{
if (at < 0f && WL.Combat.Impact.WLImpactFx.GroundShown > g0) at = Time.time + delay;
if ((at > 0f && Time.time >= at) || Time.time - t0 > maxSeconds)
{
Application.onBeforeRender -= act;
if (at > 0f) UnityEditor.EditorApplication.isPaused = true;
Bounds b = new Bounds(); bool has = false;
if (pc.tfs_weapon != null) foreach (var s in pc.tfs_weapon) if (s != null && s.gameObject.activeInHierarchy) foreach (var r in s.GetComponentsInChildren<MeshRenderer>(false)) { if (!r.enabled) continue; if (!has) { b = r.bounds; has = true; } else b.Encapsulate(r.bounds); }
var cam = Camera.main; var vp = cam.WorldToViewportPoint(pc.Get_Center_position());
sb.AppendFormat("{0} · {1} · 무기 그림 {2}~{3} · PC {4} · PC 화면 {5:F3},{6:F3}", at > 0f ? "■ 일시 정지" : "못 찾음", WL.Combat.Impact.WLImpactFx.LastGround, has ? b.min.ToString("F2") : "-", has ? b.max.ToString("F2") : "-", pc.Get_position().ToString("F2"), vp.x, 1f - vp.y).Append('\n');
}
};
Application.onBeforeRender += act;
return "지면 이펙트 대기";
}
/// <summary>대검 기본 공격 관찰 — 콤보 단계 · 클립 순서 · 지면 충격파(4타) 로그 · 그 순간 칼끝 위치.</summary>
public static object Combo(float seconds)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
var sb = Buf(); sb.Length = 0; s_sb = sb; s_t0 = Time.time;
WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStage); WL.Combat.Core.CombatEvents.ComboStage.Add(OnStage);
int g0 = WL.Combat.Impact.WLImpactFx.GroundShown; string lg = WL.Combat.Impact.WLImpactFx.LastGround;
UnityEngine.Events.UnityAction act = null;
act = () =>
{
float t = Time.time - s_t0;
if (WL.Combat.Impact.WLImpactFx.GroundShown != g0 || WL.Combat.Impact.WLImpactFx.LastGround != lg)
{
g0 = WL.Combat.Impact.WLImpactFx.GroundShown; lg = WL.Combat.Impact.WLImpactFx.LastGround;
sb.AppendFormat("{0:F2} 지면 이펙트 #{1} · {2} · PC {3}", t, g0, lg, pc.Get_position().ToString("F2")).Append('\n');
}
if (t > seconds) { Application.onBeforeRender -= act; WL.Combat.Core.CombatEvents.ComboStage.Remove(OnStage); s_sb = null; }
};
Application.onBeforeRender += act;
return "대검 콤보 관찰 " + seconds + " 초";
}
public static object PoseClip(string fbxPath, string timesCsv, string outPrefix, float side)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
AnimationClip clip = null; string want = null;
int hash = fbxPath.IndexOf('#'); if (hash > 0) { want = fbxPath.Substring(hash + 1); fbxPath = fbxPath.Substring(0, hash); } // 경로#클립이름
foreach (var o in UnityEditor.AssetDatabase.LoadAllAssetsAtPath(fbxPath)) { var c = o as AnimationClip; if (c != null && !c.name.StartsWith("__preview__") && (want == null || c.name == want)) { clip = c; break; } }
if (clip == null) return "클립 없음 " + fbxPath;
var an = pc.m_animation; var ci = System.Globalization.CultureInfo.InvariantCulture;
var graph = PlayableGraph.Create("~WL922_Pose"); graph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
var output = AnimationPlayableOutput.Create(graph, "pose", an);
var cp = AnimationClipPlayable.Create(graph, clip); cp.SetApplyFootIK(false); output.SetSourcePlayable(cp);
var go = new GameObject("~WL922_Cam"); var cam = go.AddComponent<Camera>(); cam.enabled = false; cam.fieldOfView = 35f;
cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.12f, 0.14f, 0.18f, 1f);
int ui = LayerMask.NameToLayer("UI"); if (ui >= 0) cam.cullingMask &= ~(1 << ui); // 시험 더미는 잠깐 멀리 옮긴다(무기는 다른 층이라 층으로 거르지 않음)
var d = WL.IslandMob.WLTrailTest.Dummy; if (d != null) { d.Stop(true); d.Set_Warp(pc.Get_position() - pc.transform.forward * 25f); }
var center = pc.Get_position() + Vector3.up * 0.7f + pc.transform.forward * 0.4f;
go.transform.position = center + pc.transform.right * side + Vector3.up * 0.5f; go.transform.LookAt(center);
if (Mathf.Approximately(side, 0f) && Camera.main != null) // side 0 = 게임 카메라 각도에서 확대
{
var mc = Camera.main; go.transform.rotation = mc.transform.rotation; cam.fieldOfView = 12f;
go.transform.position = center - mc.transform.forward * Vector3.Distance(mc.transform.position, center);
}
var sb = new StringBuilder(clip.name + " " + clip.length.ToString("F2") + "s · ");
float speed0 = an.speed;
try
{
foreach (var s in timesCsv.Split(','))
{
float t = float.Parse(s, ci) * clip.length; // 정규화 시각
cp.SetTime(t); graph.Evaluate(0f);
var rt = new RenderTexture(500, 500, 24); cam.targetTexture = rt; cam.Render();
var prev = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(500, 500, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, 500, 500), 0, 0); tex.Apply(); RenderTexture.active = prev;
System.IO.File.WriteAllBytes(outPrefix + "_" + s + ".png", tex.EncodeToPNG()); Object.Destroy(tex); cam.targetTexture = null; Object.Destroy(rt);
}
}
finally { graph.Destroy(); Object.Destroy(go); an.speed = speed0 <= 0f ? 1f : speed0; pc.Play_Idle(); }
foreach (var e in clip.events) sb.Append(e.functionName).Append('@').Append((e.time / clip.length).ToString("F2")).Append(' ');
return sb.ToString();
}
}