Project_WL/AgentScripts/WL917_Probe.cs

200 lines
14 KiB
C#
Raw Normal View History

// WL917_Probe.cs — #917 Casual RPG VFX 후보 미리보기(Play 중) — PC 둘레 격자에 후보 프리팹을 띄우고 화면 좌표 표를 남긴다
// Grid(pathsCsv, spacing, scale) · Log()
using System.Text;
using UnityEditor;
using UnityEngine;
public static class WL917_Probe
{
const string Key = "wl917log";
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 string Vp(Vector3 w) { var cam = Camera.main; if (cam == null) return "-"; var v = cam.WorldToViewportPoint(w); return v.x.ToString("F3") + "," + (1f - v.y).ToString("F3"); }
static Actor Mob(float dist)
{
var pc = MyValue.MyPC as PCActor;
Actor d = WL.IslandMob.WLTrailTest.Dummy;
if (d == null || !d.isActiveAndEnabled)
{
float bd = float.MaxValue;
foreach (var m in Object.FindObjectsByType<MobActor>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{ if (m == null || m.IsDead()) continue; float dd = Vector3.Distance(pc.Get_position(), m.Get_position()); if (dd < bd) { bd = dd; d = m; } }
}
if (d != null && dist > 0f)
{
var f = pc.transform.forward; f.y = 0f; f.Normalize();
var want = pc.Get_position() + f * dist; UnityEngine.AI.NavMeshHit hit;
if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 1.5f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position;
d.Stop(true); d.Set_Warp(want); pc.Change_Target(true, d);
}
return d;
}
/// <summary>기본 공격 관찰 — mode 0: 겉모습 붙은 화살(~WL_Visual)이 날아가는 순간 · 1: 3타 기 모으기(WLBowCharge) 순간 · 2: 화살 명중 이펙트(WL_Fx_HitLight) 순간에 일시 정지.</summary>
public static object PauseOn(int mode, float dist, float maxSeconds)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
var d = Mob(dist); var sb = Buf(); sb.Length = 0;
float t0 = Time.time; int ch0 = WL.Combat.Weapon.WLBowChargeReceiver.Charges; float chargeAt = -1f;
UnityEngine.Events.UnityAction act = null;
act = () =>
{
float t = Time.time - t0; Vector3? at = null;
if (mode == 0)
foreach (var pb in Object.FindObjectsByType<ProjectileBase>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
if (pb != null && pb.name.StartsWith("Projectile_1040") && pb.transform.Find("~WL_Visual") != null && Vector3.Distance(pb.transform.position, pc.Get_position()) > 2.6f) { at = pb.transform.position; break; }
if (mode == 1 && WL.Combat.Weapon.WLBowChargeReceiver.Charges > ch0) { if (chargeAt < 0f) chargeAt = t; if (t - chargeAt >= 0.2f) at = pc.Get_Center_position(); }
if (mode == 2) { var h = GameObject.Find("WL_Fx_HitLight(Clone)"); if (h != null) at = h.transform.position; }
if (at.HasValue || t > maxSeconds)
{
Application.onBeforeRender -= act;
if (at.HasValue) EditorApplication.isPaused = true;
sb.AppendFormat("{0} · t {1:F2} · 대상 화면 {2} · PC 화면 {3} · 몹 화면 {4} · 겉모습 붙임 {5} · 명중 {6} · {7}", at.HasValue ? "■ 일시 정지" : "못 찾음", t, at.HasValue ? Vp(at.Value) : "-", Vp(pc.Get_Center_position()),
d != null ? Vp(d.Get_Center_position()) : "-", WL.Combat.Weapon.WLProjectileVisual.Attached, WL.Combat.Weapon.WLProjectileHitFx.Shown, WL.Combat.Weapon.WLProjectileVisual.LastLog).Append('\n');
}
};
Application.onBeforeRender += act;
return "관찰 " + mode;
}
/// <summary>3타 기 모으기 연출을 직접 띄우고(WLBowChargeReceiver.WLBowCharge · dur 초) after 초 뒤 일시 정지.</summary>
public static object Charge(float dur, float after)
{
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
var rc = pc.GetComponent<WL.Combat.Weapon.WLBowChargeReceiver>(); if (rc == null) return "수신기 없음";
rc.WLBowCharge(new AnimationEvent { floatParameter = dur });
float t0 = Time.time; UnityEngine.Events.UnityAction act = null;
act = () => { if (Time.time - t0 >= after) { Application.onBeforeRender -= act; EditorApplication.isPaused = true; } };
Application.onBeforeRender += act;
return "모으기 · PC 화면 " + Vp(pc.Get_Center_position()) + " · " + WL.Combat.Weapon.WLBowChargeReceiver.LastLog;
}
static int s_rainHits; static string s_rainLast = "";
static void OnRainHit(in WL.Combat.Core.HitConfirmedEvent e)
{
var pd = e.projectile; if (pd == null || pd.m_SkillListTableData == null) return;
if (pd.m_SkillListTableData.e_Skill.ToString() != "Skill_ArrowRain") return;
s_rainHits++; s_rainLast = e.victim != null ? e.victim.name : "-";
}
/// <summary>활 각성(E · 화살 비) — 대상 몹 dist m · 프레임마다 명중 수 · pauseAt 에 일시 정지(화면 좌표).</summary>
public static object Rain(float dist, float pauseAt, float seconds)
{
var pc = MyValue.MyPC as PCActor; if (pc == null) return "PC 없음";
var d = Mob(dist); var sb = Buf(); sb.Length = 0;
s_rainHits = 0; WL.Combat.Core.CombatEvents.HitConfirmed.Remove(OnRainHit); WL.Combat.Core.CombatEvents.HitConfirmed.Add(OnRainHit);
float t0 = Time.time; bool paused = false; int last = -1; int c0 = WL.Combat.Weapon.WLSkillVfx.Casters, ce0 = WL.Combat.Weapon.WLSkillVfx.Centers;
UnityEngine.Events.UnityAction act = null;
act = () =>
{
float t = Time.time - t0;
if (s_rainHits != last)
{
last = s_rainHits; string box = "-";
foreach (var s in Object.FindObjectsByType<Skill_ArrowRain>(FindObjectsInactive.Exclude, FindObjectsSortMode.None)) { var bc = s.GetComponent<BoxCollider>(); box = (bc != null ? bc.size.x.ToString("F1") : "?") + "m/" + s.Loop + "회/" + s.DelayTime.ToString("F3"); }
sb.AppendFormat("{0:F2} 명중 {1} ({2}) · 판정 {3}", t, s_rainHits, s_rainLast, box).Append('\n');
}
if (!paused && pauseAt > 0f && t >= pauseAt) { paused = true; EditorApplication.isPaused = true; sb.AppendFormat("■ 일시 정지 · PC 화면 {0} · 몹 화면 {1}", Vp(pc.Get_position()), d != null ? Vp(d.Get_position()) : "-").Append('\n'); }
if (Mathf.Repeat(t, 0.25f) < Time.deltaTime && d != null)
foreach (var s in Object.FindObjectsByType<Skill_ArrowRain>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{ var v = d.Get_position() - s.transform.position; v.y = 0f; sb.AppendFormat("{0:F2} 몹-판정 중심 {1:F2} m · 몹 상태 {2}", t, v.magnitude, d.Get_CurAnim()).Append('\n'); }
if (t > seconds) { Application.onBeforeRender -= act; WL.Combat.Core.CombatEvents.HitConfirmed.Remove(OnRainHit);
sb.AppendFormat("끝 · 명중 {0} · 시전자 원 +{1} · 빔 +{2} · {3}", s_rainHits, WL.Combat.Weapon.WLSkillVfx.Casters - c0, WL.Combat.Weapon.WLSkillVfx.Centers - ce0, WL.Combat.Weapon.WLSkillVfx.LastLog).Append('\n'); }
};
Application.onBeforeRender += act;
return "E: " + WL.Combat.Weapon.WLSkillHotkey.Fire(2);
}
/// <summary>검은 배경 미리보기 — 장면 위 높은 곳(y 1000)에 후보를 한 줄로 띄우고 시각별로 파티클을 Simulate 한 뒤 전용 카메라로 PNG 저장(이름 · 시각 순).</summary>
public static object Preview(string pathsCsv, float scale, float spacing, string timesCsv, string outPrefix, float pitch)
{
var paths = pathsCsv.Split('|'); var times = System.Array.ConvertAll(timesCsv.Split(','), s => float.Parse(s, System.Globalization.CultureInfo.InvariantCulture));
var root = new GameObject("~WL917_Preview"); root.transform.position = new Vector3(0f, 1000f, 0f);
var inst = new GameObject[paths.Length]; var sb = new StringBuilder();
for (int i = 0; i < paths.Length; i++)
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(paths[i]); if (prefab == null) { sb.AppendLine("없음 " + paths[i]); continue; }
var go = Object.Instantiate(prefab, root.transform);
go.transform.localPosition = new Vector3((i - (paths.Length - 1) * 0.5f) * spacing, 0f, 0f);
go.transform.localScale = prefab.transform.localScale * scale;
foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; }
inst[i] = go;
}
var camGo = new GameObject("~WL917_Cam"); var cam = camGo.AddComponent<Camera>();
float width = spacing * paths.Length;
cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.08f, 0.08f, 0.12f, 1f); cam.orthographic = false; cam.fieldOfView = 30f; cam.nearClipPlane = 0.1f; cam.farClipPlane = 200f;
float dist = width * 0.5f / Mathf.Tan(15f * Mathf.Deg2Rad) / 1.9f;
camGo.transform.rotation = Quaternion.Euler(pitch, 0f, 0f);
camGo.transform.position = root.transform.position + new Vector3(0f, 0.5f, 0f) - camGo.transform.forward * dist;
var rt = new RenderTexture(1600, 520, 24, RenderTextureFormat.ARGB32); cam.targetTexture = rt; cam.aspect = 1600f / 520f;
foreach (var t in times)
{
foreach (var go in inst) if (go != null) foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) if (ps.transform.parent == null || ps.transform.parent.GetComponentInParent<ParticleSystem>() == null) ps.Simulate(t, true, true, false);
cam.Render();
var prev = RenderTexture.active; RenderTexture.active = rt;
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply();
RenderTexture.active = prev;
var file = outPrefix + "_" + t.ToString("F2", System.Globalization.CultureInfo.InvariantCulture) + ".png";
System.IO.File.WriteAllBytes(file, tex.EncodeToPNG()); Object.Destroy(tex);
sb.AppendLine("저장 " + file);
}
cam.targetTexture = null; Object.Destroy(rt); Object.Destroy(camGo); Object.Destroy(root);
for (int i = 0; i < paths.Length; i++) sb.Append(i).Append(' ').Append(System.IO.Path.GetFileNameWithoutExtension(paths[i])).Append(" · ");
return sb.ToString();
}
/// <summary>후보 1 개를 PC 옆(카메라 오른쪽 side m)에 띄우고 pauseAt 에 일시 정지 — 화면 좌표.</summary>
public static object One(string path, float side, float scale, float pauseAt)
{
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
var cam = Camera.main; var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path); if (prefab == null) return "없음 " + path;
var right = cam.transform.right; right.y = 0f; right.Normalize();
var pos = pc.Get_position() + right * side;
var go = Object.Instantiate(prefab, pos, prefab.transform.rotation);
go.transform.localScale = prefab.transform.localScale * scale;
foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; }
Object.Destroy(go, 6f);
float t0 = Time.time; UnityEngine.Events.UnityAction act = null;
act = () => { if (Time.time - t0 >= pauseAt) { Application.onBeforeRender -= act; EditorApplication.isPaused = true; } };
Application.onBeforeRender += act;
var vp = cam.WorldToViewportPoint(pos);
return string.Format("{0} · 화면 {1:F3},{2:F3}", System.IO.Path.GetFileNameWithoutExtension(path), vp.x, 1f - vp.y);
}
public static object Grid(string pathsCsv, float spacing, float scale, float pauseAt)
{
var pc = MyValue.MyPC; if (pc == null) return "PC 없음";
var cam = Camera.main; var sb = Buf(); sb.Length = 0;
var paths = pathsCsv.Split('|'); int cols = 4;
var right = cam != null ? cam.transform.right : Vector3.right; right.y = 0f; right.Normalize();
var fwd = Vector3.Cross(right, Vector3.up) * -1f;
var origin = pc.Get_position() - right * spacing * 1.5f + fwd * spacing * 0.5f;
for (int i = 0; i < paths.Length; i++)
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(paths[i]);
if (prefab == null) { sb.AppendLine("없음 " + paths[i]); continue; }
var pos = origin + right * spacing * (i % cols) - fwd * spacing * (i / cols);
var go = Object.Instantiate(prefab, pos, prefab.transform.rotation);
go.transform.localScale = prefab.transform.localScale * scale;
foreach (var ps in go.GetComponentsInChildren<ParticleSystem>(true)) { var m = ps.main; m.scalingMode = ParticleSystemScalingMode.Hierarchy; }
Object.Destroy(go, 6f);
var vp = cam != null ? cam.WorldToViewportPoint(pos) : Vector3.zero;
sb.AppendFormat("{0} {1} · 화면 {2:F3},{3:F3}", i, System.IO.Path.GetFileNameWithoutExtension(paths[i]), vp.x, 1f - vp.y).AppendLine();
}
if (pauseAt > 0f)
{
float t0 = Time.time; UnityEngine.Events.UnityAction act = null;
act = () => { if (Time.time - t0 >= pauseAt) { Application.onBeforeRender -= act; EditorApplication.isPaused = true; } };
Application.onBeforeRender += act;
}
return sb.ToString();
}
}