WL 세션 3 (#803~#806 · PD 승인 2026-09-07) — 세로 전용 전환 · Attack3 검기 무기 정합(ShowEffect 0.25 · editbake 캘리브레이션 4행) · 타격감 midplus · 공용 팝업 세로 크기 · 검증 도구(SuppressPets 복원 수정 · ComboSpeed/Trace3 · 신규 WL803/804/806 도구)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b4a0977d4d
commit
abcd038656
|
|
@ -117,10 +117,23 @@ public static class WL760_Seq
|
||||||
return t != null ? t.name : "(none)";
|
return t != null ? t.name : "(none)";
|
||||||
}
|
}
|
||||||
// 펫이 있으면 같은 몹을 때려 데미지 귀속이 오염된다 → 런 동안 매 프레임 꺼 두고 끝나면 켠다(게임이 도중에 다시 켤 수 있음).
|
// 펫이 있으면 같은 몹을 때려 데미지 귀속이 오염된다 → 런 동안 매 프레임 꺼 두고 끝나면 켠다(게임이 도중에 다시 켤 수 있음).
|
||||||
|
// #806(2026-09-07) 수정: 복원(suppress=false) 때 "이 도구가 끈 펫"만 되살린다.
|
||||||
|
// 이전 버전은 씬의 비활성 PetActor 를 전부 켜서, 게임이 의도적으로 꺼 둔 펫(PCInfo.Make_Pet 의 Off() 잔여 · 펫 메뉴 미리보기 모델)까지
|
||||||
|
// 되살렸다 → #792 검증 중 "펫 재등장" 의 유력 원인【추정】. 게임 코드 게이트(WLGameplaySettings.spawnPlayerPet)와 무관한 도구 결함.
|
||||||
|
static readonly System.Collections.Generic.HashSet<int> s_suppressed = new System.Collections.Generic.HashSet<int>();
|
||||||
static void SuppressPets(bool suppress)
|
static void SuppressPets(bool suppress)
|
||||||
{
|
{
|
||||||
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
if (suppress)
|
||||||
if (p.gameObject.activeSelf == suppress) p.gameObject.SetActive(!suppress);
|
{
|
||||||
|
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||||
|
if (p.gameObject.activeSelf) { p.gameObject.SetActive(false); s_suppressed.Add(p.GetInstanceID()); }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||||
|
if (s_suppressed.Contains(p.GetInstanceID()) && !p.gameObject.activeSelf) p.gameObject.SetActive(true);
|
||||||
|
s_suppressed.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator Co(MyActor me, int classId, string tag, float dist, bool noPet)
|
public IEnumerator Co(MyActor me, int classId, string tag, float dist, bool noPet)
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,12 @@ public static class WL792_Verify
|
||||||
|
|
||||||
public static void Dirs(int classId) { Launch(h => h.Co_Dirs(classId), "dirs" + classId); }
|
public static void Dirs(int classId) { Launch(h => h.Co_Dirs(classId), "dirs" + classId); }
|
||||||
public static void Combo(int classId) { Launch(h => h.Co_Combo(classId), "combo" + classId); }
|
public static void Combo(int classId) { Launch(h => h.Co_Combo(classId), "combo" + classId); }
|
||||||
|
public static void ComboSpeed(int classId, float speed) { Launch(h => h.Co_ComboSpeed(classId, speed), "comboS" + classId); } // #804 클립 원속 재실측
|
||||||
public static void Moving(int classId) { Launch(h => h.Co_Moving(classId), "moving" + classId); }
|
public static void Moving(int classId) { Launch(h => h.Co_Moving(classId), "moving" + classId); }
|
||||||
public static void Budget() { Launch(h => h.Co_Budget(), "budget"); }
|
public static void Budget() { Launch(h => h.Co_Budget(), "budget"); }
|
||||||
public static void Regress() { Launch(h => h.Co_Regress(), "regress"); }
|
public static void Regress() { Launch(h => h.Co_Regress(), "regress"); }
|
||||||
public static void Trace(int classId) { Launch(h => h.Co_Trace(classId), "trace" + classId); }
|
public static void Trace(int classId) { Launch(h => h.Co_Trace(classId), "trace" + classId); }
|
||||||
|
public static void Trace3(int classId) { Launch(h => h.Co_Trace3(classId), "trace3_" + classId); } // #804 Attack3 한 클립 프레임 추적
|
||||||
|
|
||||||
/// <summary>런타임 캘리브레이션·기하 캐시를 비운다(설정/알고리즘을 바꾼 뒤 재실측).</summary>
|
/// <summary>런타임 캘리브레이션·기하 캐시를 비운다(설정/알고리즘을 바꾼 뒤 재실측).</summary>
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
|
|
@ -351,6 +353,94 @@ public static class WL792_Verify
|
||||||
Done(sb);
|
Done(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── ②-S 콤보 연속 · 지정 공격 속도 (#804 · 2026-09-07) ──────────────────
|
||||||
|
// 런타임 공격 속도(FinalAttackSpeed 약 3.5)에서는 Attack3 스윙 창 안 샘플이 6~9 개뿐이라
|
||||||
|
// 캘리브레이션 품질이 낮다(실측 path 0.25 m · Stab 오판). 클립 원속(1.0)으로 돌려 샘플을 채운다.
|
||||||
|
// 베이크된 캘리브레이션은 이벤트 시점에 캐릭터 로컬로 펼치므로 공격 속도와 무관하게 재사용된다.
|
||||||
|
public IEnumerator Co_ComboSpeed(int classId, float speed)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("# WL #804 콤보 연속(속도 " + speed.ToString("F2") + ") class=" + classId + " " + DateTime.Now.ToString("HH:mm:ss"));
|
||||||
|
yield return SetClass(sb, classId);
|
||||||
|
|
||||||
|
var me = Me();
|
||||||
|
if (me == null) { Done(sb); yield break; }
|
||||||
|
var anim = me.GetComponentInChildren<Animator>();
|
||||||
|
|
||||||
|
int before = WeaponTrailDriver.MappedSpawnCount;
|
||||||
|
int shots = 0;
|
||||||
|
float gap = 1.3f / Mathf.Max(speed, 0.1f);
|
||||||
|
float t0 = Time.time;
|
||||||
|
me.Play_Attack(0, speed);
|
||||||
|
float tEnd = Time.time + 6f * gap + 3f;
|
||||||
|
while (Time.time < tEnd && shots < 6)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
if (WeaponTrailDriver.MappedSpawnCount > before)
|
||||||
|
{
|
||||||
|
before = WeaponTrailDriver.MappedSpawnCount;
|
||||||
|
var p = WeaponTrailDriver.LastPlacement;
|
||||||
|
sb.AppendLine(" " + Row("comboS" + shots + " " + ClipName(anim), p));
|
||||||
|
yield return new WaitForSeconds(0.05f);
|
||||||
|
yield return Cap(tag_ + "_seq" + shots);
|
||||||
|
shots++;
|
||||||
|
}
|
||||||
|
if (Time.time - t0 > gap && shots < 6)
|
||||||
|
{
|
||||||
|
me.Play_Attack(0, speed);
|
||||||
|
t0 = Time.time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.AppendLine("캡처 " + shots + "장");
|
||||||
|
Done(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── #804 Attack3 한 클립을 프레임마다 추적 — nt · 칼끝 이동 · 스윙 창 on/off · 배치 스폰 ─────
|
||||||
|
public IEnumerator Co_Trace3(int classId)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("# WL #804 Attack3 프레임 추적 class=" + classId + " " + DateTime.Now.ToString("HH:mm:ss"));
|
||||||
|
yield return SetClass(sb, classId);
|
||||||
|
var me = Me();
|
||||||
|
if (me == null) { Done(sb); yield break; }
|
||||||
|
var anim = me.GetComponentInChildren<Animator>();
|
||||||
|
float aspd = (float)ASpd(me);
|
||||||
|
sb.AppendLine("FinalAttackSpeed=" + aspd.ToString("F3") + " animator.speed=" + anim.speed.ToString("F3"));
|
||||||
|
|
||||||
|
float t0 = Time.time; float tEnd = Time.time + 12f;
|
||||||
|
me.Play_Attack(0, aspd);
|
||||||
|
bool recorded = false;
|
||||||
|
var rows = new List<string>();
|
||||||
|
while (Time.time < tEnd && !recorded)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
if (Time.time - t0 > 1.2f) { me.Play_Attack(0, aspd); t0 = Time.time; }
|
||||||
|
string c = ClipName(anim);
|
||||||
|
if (!c.Contains("Attack3")) continue;
|
||||||
|
// Attack3 진입 — 클립이 끝날 때까지 매 프레임 기록
|
||||||
|
var drv = me.GetComponent<WeaponTrailDriver>();
|
||||||
|
Vector3 prevTip = drv != null ? drv.GetTipWorld(0) : Vector3.zero;
|
||||||
|
int spawn0 = WeaponTrailDriver.MappedSpawnCount;
|
||||||
|
int fi = 0; float path = 0f;
|
||||||
|
while (ClipName(anim).Contains("Attack3") && fi < 200)
|
||||||
|
{
|
||||||
|
var st = anim.GetCurrentAnimatorStateInfo(0);
|
||||||
|
float nt = st.normalizedTime % 1f;
|
||||||
|
Vector3 tip = drv != null ? drv.GetTipWorld(0) : Vector3.zero;
|
||||||
|
float step = Vector3.Distance(tip, prevTip); if (fi > 0) path += step;
|
||||||
|
rows.Add(string.Format(" f{0,-3} nt={1,5:F3} inTrans={2} speed={3:F2} tipStep={4:F3} path={5:F2} swing={6} spawned={7} localTip={8}",
|
||||||
|
fi, nt, anim.IsInTransition(0) ? 1 : 0, anim.speed, step, path, drv != null && drv.IsSwinging ? 1 : 0,
|
||||||
|
WeaponTrailDriver.MappedSpawnCount - spawn0, me.transform.InverseTransformPoint(tip).ToString("F2")));
|
||||||
|
prevTip = tip; fi++;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
recorded = true;
|
||||||
|
}
|
||||||
|
sb.AppendLine(recorded ? "Attack3 frames=" + rows.Count : "!! Attack3 not reached in 12 s");
|
||||||
|
foreach (var r in rows) sb.AppendLine(r);
|
||||||
|
Done(sb);
|
||||||
|
}
|
||||||
|
|
||||||
// ── ③ 이동 중 공격 ──────────────────────────────────────────────────
|
// ── ③ 이동 중 공격 ──────────────────────────────────────────────────
|
||||||
public IEnumerator Co_Moving(int classId)
|
public IEnumerator Co_Moving(int classId)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public static class WL797_MakeAsset
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>세기 프리셋. args: "weak" | "mid" | "strong" | "off"</summary>
|
/// <summary>세기 프리셋. args: "weak" | "mid" | "midplus" | "strong" | "off"</summary>
|
||||||
public static object Preset(string name)
|
public static object Preset(string name)
|
||||||
{
|
{
|
||||||
var so = AssetDatabase.LoadAssetAtPath<WLHitFeelSettings>(Path);
|
var so = AssetDatabase.LoadAssetAtPath<WLHitFeelSettings>(Path);
|
||||||
|
|
@ -85,6 +85,13 @@ public static class WL797_MakeAsset
|
||||||
so.shakeAmplitude = 0.10f; so.shakeDuration = 0.12f;
|
so.shakeAmplitude = 0.10f; so.shakeDuration = 0.12f;
|
||||||
so.scalePunch = 1.12f;
|
so.scalePunch = 1.12f;
|
||||||
break;
|
break;
|
||||||
|
case "midplus": // B+ — PD 2026-09-07 "현재보다 미세하게 강하게" (mid 와 strong 의 중간)
|
||||||
|
case "mid+":
|
||||||
|
so.enabled = true;
|
||||||
|
so.hitStopDuration = 0.04f; so.hitStopKillDuration = 0.065f;
|
||||||
|
so.shakeAmplitude = 0.11f; so.shakeDuration = 0.12f;
|
||||||
|
so.scalePunch = 1.14f;
|
||||||
|
break;
|
||||||
case "strong": // C — 발주 상한
|
case "strong": // C — 발주 상한
|
||||||
so.enabled = true;
|
so.enabled = true;
|
||||||
so.hitStopDuration = 0.05f; so.hitStopKillDuration = 0.08f;
|
so.hitStopDuration = 0.05f; so.hitStopKillDuration = 0.08f;
|
||||||
|
|
@ -92,7 +99,7 @@ public static class WL797_MakeAsset
|
||||||
so.scalePunch = 1.16f;
|
so.scalePunch = 1.16f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN preset '" + name + "' (weak|mid|strong|off)";
|
return "UNKNOWN preset '" + name + "' (weak|mid|midplus|strong|off)";
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorUtility.SetDirty(so);
|
EditorUtility.SetDirty(so);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// WL803_Portrait.cs — #803 세로(포트레이트) 전용 전환 (에디트 모드 · ProjectSettings.asset 만 수정 · PD 결정 2026-09-07)
|
||||||
|
// unity command run_script --file AgentScripts/WL803_Portrait.cs --entry WL803_Portrait.Dump
|
||||||
|
// unity command run_script --file AgentScripts/WL803_Portrait.cs --entry WL803_Portrait.Apply (세로 전용)
|
||||||
|
// unity command run_script --file AgentScripts/WL803_Portrait.cs --entry WL803_Portrait.Revert (가로 자동회전 원복 = 09-07 이전 값)
|
||||||
|
// 근거: 이전 값 defaultScreenOrientation 4(AutoRotation) · Portrait 0 · PortraitUpsideDown 0 · LandscapeRight 1 · LandscapeLeft 1.
|
||||||
|
// 코드의 방향 참조는 Assets/Script/Util/SafeArea.cs 하나(Portrait 기본 · 가로일 때만 분기) → 세로 전용에서 그대로 동작.
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public static class WL803_Portrait
|
||||||
|
{
|
||||||
|
public static object Dump()
|
||||||
|
{
|
||||||
|
return string.Format("defaultInterfaceOrientation={0} · autorotate Portrait={1} PortraitUpsideDown={2} LandscapeLeft={3} LandscapeRight={4} · animatedAutorotation={5}",
|
||||||
|
PlayerSettings.defaultInterfaceOrientation,
|
||||||
|
PlayerSettings.allowedAutorotateToPortrait, PlayerSettings.allowedAutorotateToPortraitUpsideDown,
|
||||||
|
PlayerSettings.allowedAutorotateToLandscapeLeft, PlayerSettings.allowedAutorotateToLandscapeRight,
|
||||||
|
PlayerSettings.useAnimatedAutorotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Apply()
|
||||||
|
{
|
||||||
|
PlayerSettings.defaultInterfaceOrientation = UIOrientation.Portrait;
|
||||||
|
PlayerSettings.allowedAutorotateToPortrait = true;
|
||||||
|
PlayerSettings.allowedAutorotateToPortraitUpsideDown = false;
|
||||||
|
PlayerSettings.allowedAutorotateToLandscapeLeft = false;
|
||||||
|
PlayerSettings.allowedAutorotateToLandscapeRight = false;
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return "APPLIED portrait-only → " + Dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Revert()
|
||||||
|
{
|
||||||
|
PlayerSettings.defaultInterfaceOrientation = UIOrientation.AutoRotation;
|
||||||
|
PlayerSettings.allowedAutorotateToPortrait = false;
|
||||||
|
PlayerSettings.allowedAutorotateToPortraitUpsideDown = false;
|
||||||
|
PlayerSettings.allowedAutorotateToLandscapeLeft = true;
|
||||||
|
PlayerSettings.allowedAutorotateToLandscapeRight = true;
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return "REVERTED landscape-autorotate → " + Dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
// WL804_Bake.cs — #804 Attack3 검기 캘리브레이션을 **에디트 모드 결정적 샘플링**으로 베이크 (PD 결정 2026-09-07 "3타 휘두르기 · 무기 방향에 정합")
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Bake.cs --entry WL804_Bake.BakeAll --args "[false]" (dry · 수치만)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Bake.cs --entry WL804_Bake.BakeAll --args "[true]" (SlashTrailSettings 에 기록)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Bake.cs --entry WL804_Bake.Bake --args "[10101, 7.0, 12.0, 60.0, false]"
|
||||||
|
//
|
||||||
|
// 왜 에디트 모드 베이크인가 (실측 2026-09-07)
|
||||||
|
// · Attack3 클립(Knight@Attack3_S · 32f)의 실제 스윙은 f7~f12(nt 0.22~0.38)에서 칼끝이 약 3 m 움직이는 **가슴 높이 수평 스윙**
|
||||||
|
// (오른쪽 뒤 → 앞 → 왼쪽 앞)이다 — WL804_TipProbe(에디트 30fps) 와 WL792_Verify.Trace3(런타임 60fps) 두 실측이 일치.
|
||||||
|
// · 그런데 런타임 MeasureSwingCalibration 은 Attack3 에서만 path 0.20~0.25 m · 샘플 6~9 개를 돌려준다(Attack1/2 는 5 m · 25~29 개).
|
||||||
|
// 원인은 미규명(【미확인】 — 슬롯 선택/샘플 거리 필터 의심). 이벤트 시각을 f10→f8.5 로 앞당겨도 같았다.
|
||||||
|
// · 배치는 (클래스,클립) 캘리브레이션 행을 이벤트 시점에 캐릭터 로컬 → 월드로 펼치는 구조라, 행만 정확하면 런타임 실측과 무관하게 정합된다.
|
||||||
|
// 그래서 같은 모델·무기·클립을 프리뷰 씬에서 60 Hz 로 샘플해 MeasureSwingCalibration 과 **같은 수식**(SlashArcMeasure)으로 행을 만든다.
|
||||||
|
// · quality 는 런타임 재실측(BakeCalibrations 의 "더 좋을 때만 교체")이 이 행을 덮지 않도록 크게 둔다(source 에 editbake 표기).
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using WL.Combat;
|
||||||
|
|
||||||
|
public static class WL804_Bake
|
||||||
|
{
|
||||||
|
const string ModelPath = "Assets/Res_Addr/PC/Ai01.prefab";
|
||||||
|
const string SettingsPath = "Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset";
|
||||||
|
const string ClassConfigJson = "Assets/ResWork/Table/Export/ClassConfig.json";
|
||||||
|
const string Fbx3 = "Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack3_S.FBX";
|
||||||
|
const float BakedQuality = 50f; // 런타임 q 는 2~6 → 이 행이 항상 우선
|
||||||
|
|
||||||
|
public static object BakeAll(bool apply)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var cid in new[] { 10101, 10102, 10501, 10502 })
|
||||||
|
sb.Append(Bake(cid, 7f, 12f, 60f, apply)).AppendLine();
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>frame0~frame1 (30fps 프레임 단위 · 원 클립 기준) 을 hz 로 샘플해 캘리브레이션을 만든다.</summary>
|
||||||
|
public static object Bake(int classId, float frame0, float frame1, float hz, bool apply)
|
||||||
|
{
|
||||||
|
// ── 클래스 → 무기 (ClassConfig.json · 런타임 table_classconfig 은 에디트 모드에 없다)
|
||||||
|
string weaponName = null; int socketIndex = -1;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var arr = Newtonsoft.Json.Linq.JArray.Parse(System.IO.File.ReadAllText(ClassConfigJson));
|
||||||
|
foreach (var row in arr)
|
||||||
|
{
|
||||||
|
if ((string)row["n_ClassID"] != classId.ToString()) continue;
|
||||||
|
weaponName = (string)row["s_WeaponRHPrefab"]; socketIndex = int.Parse((string)row["n_WeaponRHIndex"]);
|
||||||
|
if (string.IsNullOrEmpty(weaponName)) { weaponName = (string)row["s_WeaponLHPrefab"]; socketIndex = int.Parse((string)row["n_WeaponLHIndex"]); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex) { return "ClassConfig parse fail: " + ex.Message; }
|
||||||
|
if (string.IsNullOrEmpty(weaponName) || socketIndex < 0) return "class " + classId + " weapon not found";
|
||||||
|
|
||||||
|
var model = AssetDatabase.LoadAssetAtPath<GameObject>(ModelPath);
|
||||||
|
var clips = AssetDatabase.LoadAllAssetsAtPath(Fbx3).OfType<AnimationClip>().Where(c => !c.name.StartsWith("__preview")).ToArray();
|
||||||
|
var clip = clips.FirstOrDefault(c => c.name == "Attack3_S_" + classId) ?? clips.FirstOrDefault(c => c.name == "Attack3");
|
||||||
|
var weapon = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Res_Addr/Weapon/" + weaponName + ".prefab");
|
||||||
|
var settings = AssetDatabase.LoadAssetAtPath<SlashTrailSettings>(SettingsPath);
|
||||||
|
if (model == null || clip == null || weapon == null || settings == null)
|
||||||
|
return "load fail model=" + (model != null) + " clip=" + (clip != null) + " weapon=" + (weapon != null) + "(" + weaponName + ") settings=" + (settings != null);
|
||||||
|
|
||||||
|
var scene = EditorSceneManager.NewPreviewScene();
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
bool animMode = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var go = (GameObject)PrefabUtility.InstantiatePrefab(model, scene);
|
||||||
|
go.transform.position = Vector3.zero; go.transform.rotation = Quaternion.identity;
|
||||||
|
var pc = go.GetComponentInChildren<PCActor>(true);
|
||||||
|
if (pc == null || pc.tfs_weapon == null || socketIndex >= pc.tfs_weapon.Length) return "tfs_weapon missing";
|
||||||
|
var socket = pc.tfs_weapon[socketIndex];
|
||||||
|
var w = (GameObject)PrefabUtility.InstantiatePrefab(weapon, scene);
|
||||||
|
w.transform.SetParent(socket, false);
|
||||||
|
w.transform.localPosition = Vector3.zero; w.transform.localRotation = Quaternion.identity; w.transform.localScale = Vector3.one;
|
||||||
|
|
||||||
|
// MeasureSlot 규칙과 동일 — 가장 긴 렌더러 바운딩 장축 양끝 · 소켓에 가까운 쪽 = 자루
|
||||||
|
Renderer best = null; Mesh bestMesh = null; float bestExtent = 0f;
|
||||||
|
foreach (var r in socket.GetComponentsInChildren<Renderer>(true))
|
||||||
|
{
|
||||||
|
var mf = r.GetComponent<MeshFilter>(); var smr = r as SkinnedMeshRenderer;
|
||||||
|
var mesh = smr != null ? smr.sharedMesh : (mf != null ? mf.sharedMesh : null);
|
||||||
|
if (mesh == null) continue;
|
||||||
|
var ws = Vector3.Scale(mesh.bounds.size, Abs(r.transform.lossyScale));
|
||||||
|
float longest = Mathf.Max(ws.x, Mathf.Max(ws.y, ws.z));
|
||||||
|
if (longest > bestExtent) { bestExtent = longest; best = r; bestMesh = mesh; }
|
||||||
|
}
|
||||||
|
if (best == null) return "no renderer under socket";
|
||||||
|
var b = bestMesh.bounds; var size = Vector3.Scale(b.size, Abs(best.transform.lossyScale));
|
||||||
|
int axis = size.x >= size.y && size.x >= size.z ? 0 : (size.y >= size.z ? 1 : 2);
|
||||||
|
Vector3 axisDir = Vector3.zero; axisDir[axis] = 1f;
|
||||||
|
Vector3 eA = best.transform.TransformPoint(b.center - axisDir * b.extents[axis]);
|
||||||
|
Vector3 eB = best.transform.TransformPoint(b.center + axisDir * b.extents[axis]);
|
||||||
|
bool aIsHilt = (eA - socket.position).sqrMagnitude <= (eB - socket.position).sqrMagnitude;
|
||||||
|
Vector3 hiltLocal = socket.InverseTransformPoint(aIsHilt ? eA : eB);
|
||||||
|
Vector3 tipLocal = socket.InverseTransformPoint(aIsHilt ? eB : eA);
|
||||||
|
|
||||||
|
var anim = go.GetComponentInChildren<Animator>();
|
||||||
|
var animGo = anim.gameObject; anim.applyRootMotion = false;
|
||||||
|
var root = go.transform; // 런타임 ToLocalPos/Dir 의 기준 = 액터 루트(위치+회전)
|
||||||
|
float t0 = frame0 / 30f, t1 = frame1 / 30f;
|
||||||
|
int n = Mathf.Max(3, Mathf.RoundToInt((t1 - t0) * hz) + 1);
|
||||||
|
var tips = new Vector3[n]; var hilts = new Vector3[n];
|
||||||
|
AnimationMode.StartAnimationMode(); animMode = true;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
float t = t0 + (t1 - t0) * i / (n - 1);
|
||||||
|
AnimationMode.BeginSampling();
|
||||||
|
AnimationMode.SampleAnimationClip(animGo, clip, t);
|
||||||
|
AnimationMode.EndSampling();
|
||||||
|
tips[i] = root.InverseTransformPoint(socket.TransformPoint(tipLocal));
|
||||||
|
hilts[i] = root.InverseTransformPoint(socket.TransformPoint(hiltLocal));
|
||||||
|
}
|
||||||
|
AnimationMode.StopAnimationMode(); animMode = false;
|
||||||
|
|
||||||
|
// ── MeasureSwingCalibration 과 같은 수식 ─────────────────────────────
|
||||||
|
float fullPath = 0f, hiltPath = 0f, bladeAng = 0f;
|
||||||
|
Vector3 prevDir = (tips[0] - hilts[0]).normalized;
|
||||||
|
for (int i = 1; i < n; i++)
|
||||||
|
{
|
||||||
|
fullPath += Vector3.Distance(tips[i], tips[i - 1]);
|
||||||
|
hiltPath += Vector3.Distance(hilts[i], hilts[i - 1]);
|
||||||
|
var d = (tips[i] - hilts[i]).normalized; bladeAng += Vector3.Angle(prevDir, d); prevDir = d;
|
||||||
|
}
|
||||||
|
float tipPerHilt = fullPath / Mathf.Max(hiltPath, 1e-3f);
|
||||||
|
int kMax = 0; float dMax = 0f;
|
||||||
|
for (int i = 1; i < n; i++) { float d2 = (tips[i] - tips[0]).sqrMagnitude; if (d2 > dMax) { dMax = d2; kMax = i; } }
|
||||||
|
float chord = Mathf.Sqrt(dMax);
|
||||||
|
float pathToMax = 0f; for (int i = 1; i <= kMax; i++) pathToMax += Vector3.Distance(tips[i], tips[i - 1]);
|
||||||
|
float straight = pathToMax > 1e-5f ? chord / pathToMax : 0f;
|
||||||
|
bool swingShape = bladeAng >= settings.swingMinSweepDegrees || tipPerHilt >= settings.swingMinTipHiltRatio;
|
||||||
|
bool stabShape = chord >= settings.stabMinDistance && straight >= settings.stabMinStraightness;
|
||||||
|
var kind = swingShape ? SwingKind.Swing : (stabShape ? SwingKind.Stab : SwingKind.Swing);
|
||||||
|
|
||||||
|
var fit = SlashArcMeasure.FitArcOrdered(tips, n);
|
||||||
|
sb.AppendLine(string.Format("== class {0} weapon {1}@{2} clip {3} f{4}~f{5} @{6}Hz n={7} · path={8:F2} hiltPath={9:F2} tip/hilt={10:F2} bladeAng={11:F0}° chord={12:F2} straight={13:F2} → kind={14} · fit.valid={15} sweep={16:F1}° r={17:F3}",
|
||||||
|
classId, weaponName, socket.name, clip.name, frame0, frame1, hz, n, fullPath, hiltPath, tipPerHilt, bladeAng, chord, straight, kind, fit.valid, fit.sweepDeg, fit.radiusOuter));
|
||||||
|
if (!fit.valid) return sb.Append("!! 원 피팅 실패").ToString();
|
||||||
|
|
||||||
|
Vector3 nrm = fit.normal; Vector3 pivot = fit.center;
|
||||||
|
float tipR = SlashArcMeasure.MeanRadius(tips, n, pivot, nrm);
|
||||||
|
float hiltR = SlashArcMeasure.MeanRadius(hilts, n, pivot, nrm);
|
||||||
|
float dev = SlashArcMeasure.RadiusDeviation(tips, n, pivot, nrm, tipR);
|
||||||
|
string pivotSrc = "칼끝원";
|
||||||
|
bool bad = tipR < 1e-3f || tipR <= hiltR * 1.15f || dev > tipR * 0.25f;
|
||||||
|
if (bad)
|
||||||
|
{
|
||||||
|
Vector3 tipCentroid = SlashArcMeasure.CentroidOnPlane(tips, n, nrm, tips[0]);
|
||||||
|
pivot = SlashArcMeasure.CentroidOnPlane(hilts, n, nrm, tipCentroid);
|
||||||
|
tipR = SlashArcMeasure.MeanRadius(tips, n, pivot, nrm);
|
||||||
|
hiltR = SlashArcMeasure.MeanRadius(hilts, n, pivot, nrm);
|
||||||
|
pivotSrc = "손잡이중심";
|
||||||
|
}
|
||||||
|
Vector3 sd, bis; float sweep;
|
||||||
|
if (!SlashArcMeasure.RecomputeAboutPivot(tips, n, pivot, ref nrm, out sd, out bis, out sweep))
|
||||||
|
return sb.Append("!! RecomputeAboutPivot 실패").ToString();
|
||||||
|
float fitQ = Mathf.Clamp01(1f - (dev / Mathf.Max(tipR, 1e-3f)) * 2f);
|
||||||
|
float q = fullPath * Mathf.Max(fitQ, 0.05f);
|
||||||
|
sb.AppendLine(string.Format(" pivot({0})={1} tipR={2:F3} hiltR={3:F3} dev={4:F3} fitQ={5:F2} q={6:F2} · normal={7} bisector={8} startDir={9} sweep={10:F1}°{11}",
|
||||||
|
pivotSrc, V(pivot), tipR, hiltR, dev, fitQ, q, V(nrm), V(bis), V(sd), sweep, sweep < settings.swingMinSweepDegrees ? " (⚠ 런타임이면 칼날평면 폴백 구간 — 베이크는 원 피팅 그대로 기록)" : ""));
|
||||||
|
sb.AppendLine(" tips: " + string.Join(" ", tips.Select(V)));
|
||||||
|
|
||||||
|
if (!apply) return sb.Append(" (dry)").ToString();
|
||||||
|
|
||||||
|
var cal = new SwingCalibration
|
||||||
|
{
|
||||||
|
classId = classId, clipName = "Effect_Slash_" + classId + "_3", kind = SwingKind.Swing,
|
||||||
|
planeNormal = nrm, bisector = bis, startDir = sd, pivot = pivot, tipRadius = tipR, hiltRadius = hiltR, sweepDeg = sweep,
|
||||||
|
stabAxis = (tips[kMax] - tips[0]).normalized, stabStart = tips[0], stabLength = Mathf.Max(chord, 1e-3f),
|
||||||
|
quality = Mathf.Max(BakedQuality, q),
|
||||||
|
source = string.Format("editbake {0:yyyy-MM-dd HH:mm} {1} f{2}~f{3}@{4}Hz n={5} path={6:F2} bladeAng={7:F0}° sweep={8:F1}° pivot={9} tipR={10:F3} hiltR={11:F3} dev={12:F3} fitQ={13:F2} q={14:F2} (WL804_Bake · 런타임 재실측이 덮지 않도록 quality 고정)",
|
||||||
|
System.DateTime.Now, clip.name, frame0, frame1, hz, n, fullPath, bladeAng, sweep, pivotSrc, tipR, hiltR, dev, fitQ, q)
|
||||||
|
};
|
||||||
|
int removed = settings.swingCalibrations.RemoveAll(c => c != null && c.classId == classId && c.clipName == cal.clipName);
|
||||||
|
settings.swingCalibrations.Add(cal);
|
||||||
|
EditorUtility.SetDirty(settings);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
sb.AppendLine(" APPLIED → " + cal.clipName + " (기존 " + removed + "행 교체 · 총 " + settings.swingCalibrations.Count + "행)");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (animMode) AnimationMode.StopAnimationMode();
|
||||||
|
EditorSceneManager.ClosePreviewScene(scene);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static string V(Vector3 v) { return string.Format("({0:F2},{1:F2},{2:F2})", v.x, v.y, v.z); }
|
||||||
|
static Vector3 Abs(Vector3 v) { return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z)); }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
// WL804_TipProbe.cs — #804 공격 클립의 칼끝 궤적 실측 → ShowEffect 이벤트 시각을 "실제 스윙 구간"으로 옮기기 위한 근거 (에디트 모드 · 읽기 전용 · 프리뷰 씬)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_TipProbe.cs --entry WL804_TipProbe.All
|
||||||
|
// unity command run_script --file AgentScripts/WL804_TipProbe.cs --entry WL804_TipProbe.Run --args "[\"Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack3_S.FBX\",\"Attack3\",\"Elven_Sword_01\",2,0.29305]"
|
||||||
|
// 방법: WL791_FootProbe 와 같은 프리뷰 씬 샘플링. Ai01.prefab(PCActor.tfs_weapon 소켓) 에 클래스 무기 프리팹(ClassConfig s_WeaponRHPrefab)을
|
||||||
|
// 런타임 PCActor.Make_Weapon 과 같은 방식(소켓 자식 · 로컬 0/identity/1)으로 붙이고, WeaponTrailDriver.MeasureSlot 과 같은 규칙
|
||||||
|
// (가장 긴 렌더러 바운딩의 장축 양끝 · 소켓에 가까운 쪽 = 자루)으로 칼끝/자루를 정한 뒤 30fps 프레임마다 루트 기준 위치를 기록한다.
|
||||||
|
// 출력: 프레임별 칼끝 이동(cm) · 칼날 방향 변화(deg) · 누적 경로 → "스윙 구간"(이동량 ≥ 최대의 30% 연속 구간) 과
|
||||||
|
// 현재 ShowEffect 시각 + 창(swingWindowNormalized 0.35) 이 스윙을 얼마나 덮는지(경로 비율).
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public static class WL804_TipProbe
|
||||||
|
{
|
||||||
|
const string ModelPath = "Assets/Res_Addr/PC/Ai01.prefab";
|
||||||
|
const float Window = 0.35f; // SlashTrailSettings.swingWindowNormalized (실측값 · 표시용)
|
||||||
|
|
||||||
|
public static object All()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append(Run("Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack1_S.FBX", "Attack1", "Elven_Sword_01", 2, 0.29821f)).AppendLine();
|
||||||
|
sb.Append(Run("Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack2_S.FBX", "Attack2", "Elven_Sword_01", 2, 0.26643f)).AppendLine();
|
||||||
|
sb.Append(Run("Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack3_S.FBX", "Attack3", "Elven_Sword_01", 2, 0.29305f)).AppendLine();
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Run(string fbxPath, string clipName, string weaponName, int socketIndex, float currentEventNorm)
|
||||||
|
{
|
||||||
|
var model = AssetDatabase.LoadAssetAtPath<GameObject>(ModelPath);
|
||||||
|
var clips = AssetDatabase.LoadAllAssetsAtPath(fbxPath).OfType<AnimationClip>().Where(c => !c.name.StartsWith("__preview")).ToArray();
|
||||||
|
var clip = clips.FirstOrDefault(c => c.name == clipName) ?? clips.FirstOrDefault(c => c.name.Contains(clipName));
|
||||||
|
var weapon = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Res_Addr/Weapon/" + weaponName + ".prefab");
|
||||||
|
if (model == null || clip == null || weapon == null)
|
||||||
|
return "load fail model=" + (model != null) + " clip=" + (clip != null) + " weapon=" + (weapon != null) + " clips=[" + string.Join(",", clips.Select(c => c.name)) + "]";
|
||||||
|
|
||||||
|
var scene = EditorSceneManager.NewPreviewScene();
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
bool animMode = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var go = (GameObject)PrefabUtility.InstantiatePrefab(model, scene);
|
||||||
|
go.transform.position = Vector3.zero; go.transform.rotation = Quaternion.identity;
|
||||||
|
var pc = go.GetComponentInChildren<PCActor>(true);
|
||||||
|
if (pc == null || pc.tfs_weapon == null || socketIndex < 0 || socketIndex >= pc.tfs_weapon.Length) return "PCActor/tfs_weapon null or index out of range";
|
||||||
|
var socket = pc.tfs_weapon[socketIndex];
|
||||||
|
if (socket == null) return "socket null idx=" + socketIndex;
|
||||||
|
var w = (GameObject)PrefabUtility.InstantiatePrefab(weapon, scene);
|
||||||
|
w.transform.SetParent(socket, false);
|
||||||
|
w.transform.localPosition = Vector3.zero; w.transform.localRotation = Quaternion.identity; w.transform.localScale = Vector3.one;
|
||||||
|
|
||||||
|
// MeasureSlot 규칙
|
||||||
|
Renderer best = null; Mesh bestMesh = null; float bestExtent = 0f;
|
||||||
|
foreach (var r in socket.GetComponentsInChildren<Renderer>(true))
|
||||||
|
{
|
||||||
|
var mf = r.GetComponent<MeshFilter>(); var smr = r as SkinnedMeshRenderer;
|
||||||
|
var mesh = smr != null ? smr.sharedMesh : (mf != null ? mf.sharedMesh : null);
|
||||||
|
if (mesh == null) continue;
|
||||||
|
var ws = Vector3.Scale(mesh.bounds.size, Abs(r.transform.lossyScale));
|
||||||
|
float longest = Mathf.Max(ws.x, Mathf.Max(ws.y, ws.z));
|
||||||
|
if (longest > bestExtent) { bestExtent = longest; best = r; bestMesh = mesh; }
|
||||||
|
}
|
||||||
|
if (best == null) return "no renderer under socket " + socket.name;
|
||||||
|
var b = bestMesh.bounds; var size = Vector3.Scale(b.size, Abs(best.transform.lossyScale));
|
||||||
|
int axis = size.x >= size.y && size.x >= size.z ? 0 : (size.y >= size.z ? 1 : 2);
|
||||||
|
Vector3 axisDir = Vector3.zero; axisDir[axis] = 1f;
|
||||||
|
Vector3 eA = best.transform.TransformPoint(b.center - axisDir * b.extents[axis]);
|
||||||
|
Vector3 eB = best.transform.TransformPoint(b.center + axisDir * b.extents[axis]);
|
||||||
|
bool aIsHilt = (eA - socket.position).sqrMagnitude <= (eB - socket.position).sqrMagnitude;
|
||||||
|
Vector3 hiltLocal = socket.InverseTransformPoint(aIsHilt ? eA : eB);
|
||||||
|
Vector3 tipLocal = socket.InverseTransformPoint(aIsHilt ? eB : eA);
|
||||||
|
float bladeLen = Vector3.Distance(hiltLocal, tipLocal);
|
||||||
|
|
||||||
|
var anim = go.GetComponentInChildren<Animator>();
|
||||||
|
if (anim == null) return "animator null";
|
||||||
|
var animGo = anim.gameObject; anim.applyRootMotion = false;
|
||||||
|
float fps = clip.frameRate; int n = Mathf.RoundToInt(clip.length * fps);
|
||||||
|
var T = new Vector3[n + 1]; var H = new Vector3[n + 1];
|
||||||
|
AnimationMode.StartAnimationMode(); animMode = true;
|
||||||
|
for (int i = 0; i <= n; i++)
|
||||||
|
{
|
||||||
|
AnimationMode.BeginSampling();
|
||||||
|
AnimationMode.SampleAnimationClip(animGo, clip, i / fps);
|
||||||
|
AnimationMode.EndSampling();
|
||||||
|
T[i] = animGo.transform.InverseTransformPoint(socket.TransformPoint(tipLocal));
|
||||||
|
H[i] = animGo.transform.InverseTransformPoint(socket.TransformPoint(hiltLocal));
|
||||||
|
}
|
||||||
|
sb.AppendLine("== " + clipName + " (" + clip.name + ") len=" + clip.length.ToString("F3") + "s frames=" + n + " fps=" + fps
|
||||||
|
+ " weapon=" + weaponName + " socket=" + socket.name + " blade=" + best.name + " len=" + bladeLen.ToString("F2") + "m · currentShowEffect=" + currentEventNorm.ToString("F3") + " window=" + Window + " ==");
|
||||||
|
sb.AppendLine(" f norm | tip x y z | dTip(cm) | dDir(deg) | cum(cm)");
|
||||||
|
var d = new float[n + 1]; float accum = 0f;
|
||||||
|
for (int i = 1; i <= n; i++)
|
||||||
|
{
|
||||||
|
d[i] = Vector3.Distance(T[i], T[i - 1]); accum += d[i];
|
||||||
|
float ang = Vector3.Angle(T[i] - H[i], T[i - 1] - H[i - 1]);
|
||||||
|
sb.AppendLine(string.Format(" {0,3} {1,5:F3} | {2,6:F2} {3,5:F2} {4,5:F2} | {5,7:F1} | {6,6:F1} | {7,7:F1}",
|
||||||
|
i, i / (float)n, T[i].x, T[i].y, T[i].z, d[i] * 100f, ang, accum * 100f));
|
||||||
|
}
|
||||||
|
float dmax = d.Max(); int imax = System.Array.IndexOf(d, dmax);
|
||||||
|
float thr = dmax * 0.30f;
|
||||||
|
int s = imax, e = imax;
|
||||||
|
while (s - 1 >= 1 && d[s - 1] >= thr) s--;
|
||||||
|
while (e + 1 <= n && d[e + 1] >= thr) e++;
|
||||||
|
float swingPath = 0f; for (int i = s; i <= e; i++) swingPath += d[i];
|
||||||
|
sb.AppendLine(string.Format("SWING(dTip >= 30% of max {0:F1}cm @f{1}) = f{2}~f{3} -> norm {4:F3}~{5:F3} · path {6:F2} m / total {7:F2} m",
|
||||||
|
dmax * 100f, imax, s, e, s / (float)n, e / (float)n, swingPath, accum));
|
||||||
|
float cover = Coverage(d, n, currentEventNorm, Window);
|
||||||
|
sb.AppendLine(string.Format("CURRENT ShowEffect {0:F3} + window {1:F2} (f{2}~f{3}) covers path {4:F2} m ({5:P0} of total)",
|
||||||
|
currentEventNorm, Window, Mathf.CeilToInt(currentEventNorm * n), Mathf.Min(n, Mathf.FloorToInt((currentEventNorm + Window) * n)), cover, accum > 0 ? cover / accum : 0f));
|
||||||
|
float rec = Mathf.Max(0f, (s - 1) / (float)n);
|
||||||
|
float recCover = Coverage(d, n, rec, Window);
|
||||||
|
sb.AppendLine(string.Format("RECOMMEND ShowEffect ~ {0:F3} (f{1}) -> window covers {2:F2} m ({3:P0}) · window end f{4}{5}",
|
||||||
|
rec, s - 1, recCover, accum > 0 ? recCover / accum : 0f, Mathf.Min(n, Mathf.FloorToInt((rec + Window) * n)), (rec + Window) * n < e ? " !! window shorter than swing end f" + e : ""));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (animMode) AnimationMode.StopAnimationMode();
|
||||||
|
EditorSceneManager.ClosePreviewScene(scene);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Coverage(float[] d, int n, float startNorm, float window)
|
||||||
|
{
|
||||||
|
int a = Mathf.Max(1, Mathf.CeilToInt(startNorm * n)), z = Mathf.Min(n, Mathf.FloorToInt((startNorm + window) * n));
|
||||||
|
float c = 0f; for (int i = a; i <= z; i++) c += d[i]; return c;
|
||||||
|
}
|
||||||
|
static Vector3 Abs(Vector3 v) { return new Vector3(Mathf.Abs(v.x), Mathf.Abs(v.y), Mathf.Abs(v.z)); }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
// WL804_Tools.cs — 2026-09-07 세션 보조 도구 (#804 검기 3타 · #806 후속 검증)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.Refresh (에디트 · AssetDatabase.Refresh + 매핑 Attack3 ShowEffect 값 확인)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.FbxEvents (에디트 · Knight@Attack3_S 서브클립 ShowEffect time 확인)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.RuntimeCal (Play · WeaponTrailDriver.RuntimeCalibrations 요약)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.PetCount (Play · PetActor 인스턴스 · ActorInfo 펫 수)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.PopupShow --args "[124]" (Play · 공용 Popup 표시)
|
||||||
|
// unity command run_script --file AgentScripts/WL804_Tools.cs --entry WL804_Tools.PopupClose (Play)
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public static class WL804_Tools
|
||||||
|
{
|
||||||
|
const string MappingPath = "Assets/WL/Combat/CombatAnimMapping.asset";
|
||||||
|
const string Fbx3 = "Assets/Res_Addr/Animations/Animation/OneHand/Knight@Attack3_S.FBX";
|
||||||
|
|
||||||
|
public static object Refresh()
|
||||||
|
{
|
||||||
|
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
|
||||||
|
var so = AssetDatabase.LoadAssetAtPath<WL.Combat.CombatAnimMapping>(MappingPath);
|
||||||
|
if (so == null) return "mapping null";
|
||||||
|
var sb = new StringBuilder("refreshed · mapping Attack3 ShowEffect:\n");
|
||||||
|
foreach (var c in so.classes)
|
||||||
|
foreach (var st in c.states)
|
||||||
|
if (st.stateName == "Attack3")
|
||||||
|
foreach (var e in st.events)
|
||||||
|
if (e.functionName == "ShowEffect") sb.AppendLine(" " + c.classId + " " + st.stateName + " norm=" + e.normalizedTime + " useNorm=" + e.useNormalized);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object FbxEvents()
|
||||||
|
{
|
||||||
|
var imp = AssetImporter.GetAtPath(Fbx3) as ModelImporter;
|
||||||
|
if (imp == null) return "importer null";
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var c in imp.clipAnimations)
|
||||||
|
{
|
||||||
|
sb.Append(c.name + " [" + c.firstFrame + "~" + c.lastFrame + "f]:");
|
||||||
|
foreach (var e in c.events) sb.Append(" " + e.functionName + "@" + e.time.ToString("F4"));
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
sb.AppendLine("motionNodeName='" + imp.motionNodeName + "'");
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object RuntimeCal()
|
||||||
|
{
|
||||||
|
var rt = WL.Combat.WeaponTrailDriver.RuntimeCalibrations;
|
||||||
|
if (rt == null) return "RuntimeCalibrations null";
|
||||||
|
var sb = new StringBuilder("runtime calibrations " + rt.Count + "\n");
|
||||||
|
foreach (var kv in rt)
|
||||||
|
{
|
||||||
|
var c = kv.Value; if (c == null) continue;
|
||||||
|
sb.AppendLine(string.Format(" {0} {1} kind={2} sweep={3:F1} q={4:F2} | {5}", c.classId, c.clipName, c.kind, c.sweepDeg, c.quality, c.source));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object PetCount()
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying) return "not playing";
|
||||||
|
var pets = UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
|
var sb = new StringBuilder("PetActor instances=" + pets.Length + "\n");
|
||||||
|
foreach (var p in pets)
|
||||||
|
{
|
||||||
|
string path = p.name; var t = p.transform.parent; while (t != null) { path = t.name + "/" + path; t = t.parent; }
|
||||||
|
sb.AppendLine(" " + path + " active=" + p.gameObject.activeInHierarchy + " layer=" + LayerMask.LayerToName(p.gameObject.layer) + " pos=" + p.transform.position);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var list = ActorInfo.Ins.Get_Actors(eRole.Pet);
|
||||||
|
sb.AppendLine("ActorInfo eRole.Pet count=" + (list != null ? list.Count : -1));
|
||||||
|
var my = MyValue.MyPC != null ? MyValue.MyPC.Get_MyPet() : null;
|
||||||
|
sb.AppendLine("MyPC.Get_MyPet()=" + (my != null ? my.name : "null"));
|
||||||
|
}
|
||||||
|
catch (System.Exception ex) { sb.AppendLine("ActorInfo/MyValue err: " + ex.Message); }
|
||||||
|
sb.AppendLine("spawnPlayerPet=" + WL.Settings.WLGameplaySettings.SpawnPlayerPet);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object PopupShow(int msgKey)
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying) return "not playing";
|
||||||
|
var pop = UnityEngine.Object.FindFirstObjectByType<Popup>(FindObjectsInactive.Include);
|
||||||
|
if (pop == null) return "Popup instance not found";
|
||||||
|
pop.Set(ePopupType.One, msgKey);
|
||||||
|
var rt = pop.m_popup != null ? pop.m_popup.GetComponentInChildren<UnityEngine.UI.Button>(true) : null;
|
||||||
|
return "popup shown key=" + msgKey + " text='" + (pop.label_msg != null ? pop.label_msg.text : "") + "' btn=" + (rt != null ? ((RectTransform)rt.transform).rect.size.ToString() : "?")
|
||||||
|
+ " scale=" + (rt != null ? rt.transform.lossyScale.x.ToString("F4") : "?");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object PopupClose()
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying) return "not playing";
|
||||||
|
var pop = UnityEngine.Object.FindFirstObjectByType<Popup>(FindObjectsInactive.Include);
|
||||||
|
if (pop == null) return "Popup instance not found";
|
||||||
|
pop.OnClick_OK();
|
||||||
|
return "popup closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>에디트 모드 · SlashTrailSettings.swingCalibrations 에서 clipName 이 suffix 로 끝나는 행 제거 (재베이크 전 · 예: "_3")</summary>
|
||||||
|
public static object RemoveCal(string clipSuffix)
|
||||||
|
{
|
||||||
|
var so = AssetDatabase.LoadAssetAtPath<WL.Combat.SlashTrailSettings>("Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset");
|
||||||
|
if (so == null || so.swingCalibrations == null) return "settings null";
|
||||||
|
var removed = new StringBuilder();
|
||||||
|
int n = so.swingCalibrations.RemoveAll(c => { bool r = c != null && c.clipName != null && c.clipName.EndsWith(clipSuffix); if (r) removed.Append(c.classId + "/" + c.clipName + " q=" + c.quality.ToString("F2") + "; "); return r; });
|
||||||
|
EditorUtility.SetDirty(so); AssetDatabase.SaveAssets();
|
||||||
|
return "removed " + n + " [" + removed + "] remaining " + so.swingCalibrations.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// WL806_Popup.cs — 후속 B: SortOrder_5.prefab/PopupUI(공용 Popup 싱글톤) 를 세로 화면에서 터치·가독 가능한 크기로 (에디트 모드 · 프리팹 컨텐츠 · Play 불필요)
|
||||||
|
// unity command run_script --file AgentScripts/WL806_Popup.cs --entry WL806_Popup.Dump
|
||||||
|
// unity command run_script --file AgentScripts/WL806_Popup.cs --entry WL806_Popup.Apply
|
||||||
|
// unity command run_script --file AgentScripts/WL806_Popup.cs --entry WL806_Popup.Revert (2026-09-07 이전 원값)
|
||||||
|
// 배경(실측 Dump 2026-09-07): 캔버스 1920×1080 Expand → 세로 1080×1920 에서 scaleFactor 0.5625.
|
||||||
|
// 원값: bg 428×311 · msg 400×165 (0,30) fs20 · btn_ok 116×56 (0,-102) fs20 → 실화면 bg 241×175 px · 글자 11 px · 버튼 65×32 px (터치·가독 미달).
|
||||||
|
// 목표: TitleInfo 팝업(#794 · 1080 기준 bg 860×520 · msg 780×280 (0,70) · btn_ok 340×130 (0,-150)) 과 같은 실크기 → 1920 기준 유닛 = px / 0.5625.
|
||||||
|
// 글자: 로딩 화면(#800) 과 같은 실화면 ≈27 px → fs 48.
|
||||||
|
using System.Text;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public static class WL806_Popup
|
||||||
|
{
|
||||||
|
const string PrefabPath = "Assets/ResWork/UIPrefabs/Title/SortOrder_5.prefab";
|
||||||
|
|
||||||
|
// 1920×1080 기준 유닛 (px / 0.5625)
|
||||||
|
static readonly Vector2 BgSize = new Vector2(1529f, 924f);
|
||||||
|
static readonly Vector2 MsgSize = new Vector2(1387f, 498f); static readonly Vector2 MsgPos = new Vector2(0f, 124f);
|
||||||
|
static readonly Vector2 BtnSize = new Vector2(604f, 231f); static readonly Vector2 BtnPos = new Vector2(0f, -267f);
|
||||||
|
const float FontSize = 48f;
|
||||||
|
// 원값
|
||||||
|
static readonly Vector2 BgSize0 = new Vector2(428f, 311f);
|
||||||
|
static readonly Vector2 MsgSize0 = new Vector2(400f, 165f); static readonly Vector2 MsgPos0 = new Vector2(0f, 30f);
|
||||||
|
static readonly Vector2 BtnSize0 = new Vector2(116f, 56f); static readonly Vector2 BtnPos0 = new Vector2(0f, -102f);
|
||||||
|
const float FontSize0 = 20f;
|
||||||
|
|
||||||
|
static void Walk(Transform t, string path, StringBuilder sb, int depth)
|
||||||
|
{
|
||||||
|
var rt = t as RectTransform;
|
||||||
|
if (rt != null)
|
||||||
|
{
|
||||||
|
var txt = t.GetComponent<Text>();
|
||||||
|
var tmp = t.GetComponent<TMPro.TextMeshProUGUI>();
|
||||||
|
string font = txt != null ? (" Text fs=" + txt.fontSize + " bestFit=" + txt.resizeTextForBestFit)
|
||||||
|
: tmp != null ? (" TMP fs=" + tmp.fontSize + " auto=" + tmp.enableAutoSizing + " wrap=" + tmp.textWrappingMode + " overflow=" + tmp.overflowMode) : "";
|
||||||
|
var img = t.GetComponent<Image>();
|
||||||
|
string im = img != null ? (" Image=" + (img.sprite != null ? img.sprite.name : "null") + " type=" + img.type) : "";
|
||||||
|
var btn = t.GetComponent<Button>();
|
||||||
|
sb.AppendLine(new string(' ', depth * 2) + path + (t.gameObject.activeSelf ? "" : " (inactive)")
|
||||||
|
+ " | size=" + rt.sizeDelta + " pos=" + rt.anchoredPosition + " aMin=" + rt.anchorMin + " aMax=" + rt.anchorMax + " pivot=" + rt.pivot
|
||||||
|
+ (btn != null ? " [Button]" : "") + im + font);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < t.childCount; i++) Walk(t.GetChild(i), path + "/" + t.GetChild(i).name, sb, depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RectTransform FindPopup(GameObject root)
|
||||||
|
{
|
||||||
|
foreach (var rt in root.GetComponentsInChildren<RectTransform>(true)) if (rt.name == "PopupUI") return rt;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Dump()
|
||||||
|
{
|
||||||
|
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var scaler = root.GetComponentInChildren<CanvasScaler>(true);
|
||||||
|
if (scaler != null) sb.AppendLine("CanvasScaler ref=" + scaler.referenceResolution + " mode=" + scaler.uiScaleMode + " match=" + scaler.matchWidthOrHeight + " screenMatch=" + scaler.screenMatchMode);
|
||||||
|
var popup = FindPopup(root);
|
||||||
|
if (popup == null) return "PopupUI not found";
|
||||||
|
Walk(popup, "PopupUI", sb, 0);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
finally { PrefabUtility.UnloadPrefabContents(root); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static object Set(Vector2 bg, Vector2 msg, Vector2 msgPos, Vector2 btn, Vector2 btnPos, float fs, string tag)
|
||||||
|
{
|
||||||
|
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var popup = FindPopup(root);
|
||||||
|
if (popup == null) return "PopupUI not found";
|
||||||
|
var child = popup.Find("child");
|
||||||
|
if (child == null) return "PopupUI/child not found";
|
||||||
|
var bgRt = child.Find("bg") as RectTransform;
|
||||||
|
var msgRt = child.Find("msg") as RectTransform;
|
||||||
|
var btnRt = child.Find("btn_ok") as RectTransform;
|
||||||
|
var nameRt = btnRt != null ? btnRt.Find("btnName") : null;
|
||||||
|
if (bgRt == null || msgRt == null || btnRt == null) return "bg/msg/btn_ok missing";
|
||||||
|
var sb = new StringBuilder(tag + "\n");
|
||||||
|
sb.AppendLine("bg " + bgRt.sizeDelta + " -> " + bg); bgRt.sizeDelta = bg;
|
||||||
|
sb.AppendLine("msg " + msgRt.sizeDelta + msgRt.anchoredPosition + " -> " + msg + msgPos); msgRt.sizeDelta = msg; msgRt.anchoredPosition = msgPos;
|
||||||
|
sb.AppendLine("btn " + btnRt.sizeDelta + btnRt.anchoredPosition + " -> " + btn + btnPos); btnRt.sizeDelta = btn; btnRt.anchoredPosition = btnPos;
|
||||||
|
var msgT = msgRt.GetComponent<TMPro.TextMeshProUGUI>();
|
||||||
|
if (msgT != null) { sb.AppendLine("msg fs " + msgT.fontSize + " -> " + fs); msgT.fontSize = fs; }
|
||||||
|
var nameT = nameRt != null ? nameRt.GetComponent<TMPro.TextMeshProUGUI>() : null;
|
||||||
|
if (nameT != null) { sb.AppendLine("btnName fs " + nameT.fontSize + " -> " + fs); nameT.fontSize = fs; }
|
||||||
|
PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
finally { PrefabUtility.UnloadPrefabContents(root); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object Apply() { return Set(BgSize, MsgSize, MsgPos, BtnSize, BtnPos, FontSize, "APPLY portrait-size popup"); }
|
||||||
|
public static object Revert() { return Set(BgSize0, MsgSize0, MsgPos0, BtnSize0, BtnPos0, FontSize0, "REVERT original popup"); }
|
||||||
|
}
|
||||||
|
|
@ -92,10 +92,23 @@ public static class WL778_Seq
|
||||||
if (mi == null || a == null) return double.NaN;
|
if (mi == null || a == null) return double.NaN;
|
||||||
try { return Convert.ToDouble(mi.Invoke(a, null)); } catch { return double.NaN; }
|
try { return Convert.ToDouble(mi.Invoke(a, null)); } catch { return double.NaN; }
|
||||||
}
|
}
|
||||||
|
// #806(2026-09-07) 수정: 복원(suppress=false) 때 "이 도구가 끈 펫"만 되살린다.
|
||||||
|
// 이전 버전은 씬의 비활성 PetActor 를 전부 켜서, 게임이 의도적으로 꺼 둔 펫(PCInfo.Make_Pet 의 Off() 잔여 · 펫 메뉴 미리보기 모델)까지
|
||||||
|
// 되살렸다 → #792 검증 중 "펫 재등장" 의 유력 원인【추정】. 게임 코드 게이트(WLGameplaySettings.spawnPlayerPet)와 무관한 도구 결함.
|
||||||
|
static readonly System.Collections.Generic.HashSet<int> s_suppressed = new System.Collections.Generic.HashSet<int>();
|
||||||
static void SuppressPets(bool suppress)
|
static void SuppressPets(bool suppress)
|
||||||
{
|
{
|
||||||
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
if (suppress)
|
||||||
if (p.gameObject.activeSelf == suppress) p.gameObject.SetActive(!suppress);
|
{
|
||||||
|
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||||
|
if (p.gameObject.activeSelf) { p.gameObject.SetActive(false); s_suppressed.Add(p.GetInstanceID()); }
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var p in UnityEngine.Object.FindObjectsByType<PetActor>(FindObjectsInactive.Include, FindObjectsSortMode.None))
|
||||||
|
if (s_suppressed.Contains(p.GetInstanceID()) && !p.gameObject.activeSelf) p.gameObject.SetActive(true);
|
||||||
|
s_suppressed.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator Co(MyActor me, int classId, string tag, float dist, bool noPet, float seconds)
|
public IEnumerator Co(MyActor me, int classId, string tag, float dist, bool noPet, float seconds)
|
||||||
|
|
|
||||||
|
|
@ -3948,8 +3948,8 @@ MonoBehaviour:
|
||||||
m_faceColor:
|
m_faceColor:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
rgba: 4294967295
|
rgba: 4294967295
|
||||||
m_fontSize: 20
|
m_fontSize: 48
|
||||||
m_fontSizeBase: 20
|
m_fontSizeBase: 48
|
||||||
m_fontWeight: 400
|
m_fontWeight: 400
|
||||||
m_enableAutoSizing: 0
|
m_enableAutoSizing: 0
|
||||||
m_fontSizeMin: 10
|
m_fontSizeMin: 10
|
||||||
|
|
@ -6265,8 +6265,8 @@ RectTransform:
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 0, y: -102}
|
m_AnchoredPosition: {x: 0, y: -267}
|
||||||
m_SizeDelta: {x: 116, y: 56}
|
m_SizeDelta: {x: 604, y: 231}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &7926492337561385874
|
--- !u!222 &7926492337561385874
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
|
|
@ -8297,7 +8297,7 @@ RectTransform:
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 0, y: 0}
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
m_SizeDelta: {x: 428, y: 311}
|
m_SizeDelta: {x: 1529, y: 924}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &5685264383111004087
|
--- !u!222 &5685264383111004087
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
|
|
@ -9266,8 +9266,8 @@ RectTransform:
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||||
m_AnchoredPosition: {x: 0, y: 30}
|
m_AnchoredPosition: {x: 0, y: 124}
|
||||||
m_SizeDelta: {x: 400, y: 165}
|
m_SizeDelta: {x: 1387, y: 498}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &1314091230975439951
|
--- !u!222 &1314091230975439951
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
|
|
@ -9328,8 +9328,8 @@ MonoBehaviour:
|
||||||
m_faceColor:
|
m_faceColor:
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
rgba: 4294967295
|
rgba: 4294967295
|
||||||
m_fontSize: 20
|
m_fontSize: 48
|
||||||
m_fontSizeBase: 20
|
m_fontSizeBase: 48
|
||||||
m_fontWeight: 400
|
m_fontWeight: 400
|
||||||
m_enableAutoSizing: 0
|
m_enableAutoSizing: 0
|
||||||
m_fontSizeMin: 10
|
m_fontSizeMin: 10
|
||||||
|
|
|
||||||
|
|
@ -672,7 +672,7 @@ ModelImporter:
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
messageOptions: 0
|
messageOptions: 0
|
||||||
- time: 0.31258667
|
- time: 0.26666668
|
||||||
functionName: ShowEffect
|
functionName: ShowEffect
|
||||||
data: Effect_Slash_10101_3
|
data: Effect_Slash_10101_3
|
||||||
objectReferenceParameter: {instanceID: 0}
|
objectReferenceParameter: {instanceID: 0}
|
||||||
|
|
@ -729,7 +729,7 @@ ModelImporter:
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
messageOptions: 0
|
messageOptions: 0
|
||||||
- time: 0.31258667
|
- time: 0.26666668
|
||||||
functionName: ShowEffect
|
functionName: ShowEffect
|
||||||
data: Effect_Slash_10102_3
|
data: Effect_Slash_10102_3
|
||||||
objectReferenceParameter: {instanceID: 0}
|
objectReferenceParameter: {instanceID: 0}
|
||||||
|
|
@ -786,7 +786,7 @@ ModelImporter:
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
messageOptions: 0
|
messageOptions: 0
|
||||||
- time: 0.31258667
|
- time: 0.26666668
|
||||||
functionName: ShowEffect
|
functionName: ShowEffect
|
||||||
data: Effect_Slash_10501_3
|
data: Effect_Slash_10501_3
|
||||||
objectReferenceParameter: {instanceID: 0}
|
objectReferenceParameter: {instanceID: 0}
|
||||||
|
|
@ -843,7 +843,7 @@ ModelImporter:
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
intParameter: 0
|
intParameter: 0
|
||||||
messageOptions: 0
|
messageOptions: 0
|
||||||
- time: 0.31258667
|
- time: 0.26666668
|
||||||
functionName: ShowEffect
|
functionName: ShowEffect
|
||||||
data: Effect_Slash_10502_3
|
data: Effect_Slash_10502_3
|
||||||
objectReferenceParameter: {instanceID: 0}
|
objectReferenceParameter: {instanceID: 0}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ MonoBehaviour:
|
||||||
- functionName: ShowEffect
|
- functionName: ShowEffect
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0.29305
|
normalizedTime: 0.25
|
||||||
stringParameter: 'Effect_Slash_{CID}_3'
|
stringParameter: 'Effect_Slash_{CID}_3'
|
||||||
intParameter: 1
|
intParameter: 1
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
|
|
@ -204,7 +204,7 @@ MonoBehaviour:
|
||||||
- functionName: ShowEffect
|
- functionName: ShowEffect
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0.29305
|
normalizedTime: 0.25
|
||||||
stringParameter: 'Effect_Slash_{CID}_3'
|
stringParameter: 'Effect_Slash_{CID}_3'
|
||||||
intParameter: 1
|
intParameter: 1
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
|
|
@ -307,7 +307,7 @@ MonoBehaviour:
|
||||||
- functionName: ShowEffect
|
- functionName: ShowEffect
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0.29305
|
normalizedTime: 0.25
|
||||||
stringParameter: 'Effect_Slash_{CID}_3'
|
stringParameter: 'Effect_Slash_{CID}_3'
|
||||||
intParameter: 1
|
intParameter: 1
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
|
|
@ -410,7 +410,7 @@ MonoBehaviour:
|
||||||
- functionName: ShowEffect
|
- functionName: ShowEffect
|
||||||
time: 0
|
time: 0
|
||||||
useNormalized: 1
|
useNormalized: 1
|
||||||
normalizedTime: 0.29305
|
normalizedTime: 0.25
|
||||||
stringParameter: 'Effect_Slash_{CID}_3'
|
stringParameter: 'Effect_Slash_{CID}_3'
|
||||||
intParameter: 1
|
intParameter: 1
|
||||||
floatParameter: 0
|
floatParameter: 0
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,20 @@ MonoBehaviour:
|
||||||
throttleSeconds: 0.08
|
throttleSeconds: 0.08
|
||||||
hitStopEnabled: 1
|
hitStopEnabled: 1
|
||||||
hitStopMode: 0
|
hitStopMode: 0
|
||||||
hitStopDuration: 0.03
|
hitStopDuration: 0.04
|
||||||
hitStopKillDuration: 0.05
|
hitStopKillDuration: 0.065
|
||||||
hitStopTimeScale: 0
|
hitStopTimeScale: 0
|
||||||
hitStopMax: 0.08
|
hitStopMax: 0.08
|
||||||
hitStopMinTimescale: 0.1
|
hitStopMinTimescale: 0.1
|
||||||
cameraShakeEnabled: 1
|
cameraShakeEnabled: 1
|
||||||
shakeAmplitude: 0.1
|
shakeAmplitude: 0.11
|
||||||
shakeDuration: 0.12
|
shakeDuration: 0.12
|
||||||
shakeFrequency: 32
|
shakeFrequency: 32
|
||||||
shakeAxisWeight: {x: 1, y: 0.35, z: 0.6}
|
shakeAxisWeight: {x: 1, y: 0.35, z: 0.6}
|
||||||
shakeKillMultiplier: 1.35
|
shakeKillMultiplier: 1.35
|
||||||
suppressLegacyShake: 1
|
suppressLegacyShake: 1
|
||||||
scalePunchEnabled: 1
|
scalePunchEnabled: 1
|
||||||
scalePunch: 1.12
|
scalePunch: 1.14
|
||||||
scalePunchDuration: 0.1
|
scalePunchDuration: 0.1
|
||||||
skipPunchOnBoss: 1
|
skipPunchOnBoss: 1
|
||||||
skipPunchOnKill: 1
|
skipPunchOnKill: 1
|
||||||
|
|
|
||||||
|
|
@ -425,24 +425,6 @@ MonoBehaviour:
|
||||||
\uCE7C\uB05D/\uC190\uC7A1\uC774=3.36 sweep=308.7\xB0 straight=0.60 fwd=-0.14
|
\uCE7C\uB05D/\uC190\uC7A1\uC774=3.36 sweep=308.7\xB0 straight=0.60 fwd=-0.14
|
||||||
chord=1.56 pivot=\uCE7C\uB05D\uC6D0 tipR=0.786 hiltR=0.229 dev=0.091 fitQ=0.77
|
chord=1.56 pivot=\uCE7C\uB05D\uC6D0 tipR=0.786 hiltR=0.229 dev=0.091 fitQ=0.77
|
||||||
q=6.48"
|
q=6.48"
|
||||||
- classId: 10501
|
|
||||||
clipName: Effect_Slash_10501_3
|
|
||||||
kind: 1
|
|
||||||
planeNormal: {x: 0.028523665, y: -0.9994087, z: 0.019202197}
|
|
||||||
pivot: {x: -0.30667877, y: 0.6199608, z: -0.21064997}
|
|
||||||
bisector: {x: 0.8267103, y: 0.034384634, z: 0.5615762}
|
|
||||||
startDir: {x: 0.8696766, y: 0.015341387, z: -0.49338353}
|
|
||||||
tipRadius: 1.1213691
|
|
||||||
hiltRadius: 0.44792122
|
|
||||||
sweepDeg: 127.48248
|
|
||||||
stabAxis: {x: -0.56417817, y: -0.0007676195, z: 0.8256527}
|
|
||||||
stabStart: {x: 0.67300415, y: 0.63777435, z: -0.7664609}
|
|
||||||
stabLength: 2.0092373
|
|
||||||
quality: 2.4277308
|
|
||||||
source: "runtime 02:26:55 samples=5/11(trim 1~5) path=2.46 \uCE7C\uB0A0\uD68C\uC804=164\xB0
|
|
||||||
\uCE7C\uB05D/\uC190\uC7A1\uC774=4.09 sweep=127.5\xB0 straight=0.82 fwd=0.83
|
|
||||||
chord=2.01 pivot=\uCE7C\uB05D\uC6D0 tipR=1.121 hiltR=0.448 dev=0.008 fitQ=0.99
|
|
||||||
q=2.43"
|
|
||||||
- classId: 10501
|
- classId: 10501
|
||||||
clipName: Effect_Slash_10501_1
|
clipName: Effect_Slash_10501_1
|
||||||
kind: 1
|
kind: 1
|
||||||
|
|
@ -500,21 +482,75 @@ MonoBehaviour:
|
||||||
- classId: 10101
|
- classId: 10101
|
||||||
clipName: Effect_Slash_10101_3
|
clipName: Effect_Slash_10101_3
|
||||||
kind: 1
|
kind: 1
|
||||||
planeNormal: {x: 0.4579177, y: -0.8193166, z: 0.34500954}
|
planeNormal: {x: 0.16853563, y: -0.9836209, z: -0.06391966}
|
||||||
pivot: {x: 0.03202251, y: 0.61713797, z: 0.0052795457}
|
pivot: {x: 0.09357196, y: 1.2423787, z: -0.1380892}
|
||||||
bisector: {x: 0.57260495, y: 0.5686936, z: 0.5905178}
|
bisector: {x: 0.94817513, y: 0.14405777, z: 0.28321597}
|
||||||
startDir: {x: 0.88574374, y: 0.45364287, z: -0.09831754}
|
startDir: {x: 0.25327274, y: 0.10588382, z: -0.9615829}
|
||||||
tipRadius: 0.8851383
|
tipRadius: 0.95663345
|
||||||
hiltRadius: 0
|
hiltRadius: 0.40691212
|
||||||
sweepDeg: 90
|
sweepDeg: 181.94073
|
||||||
stabAxis: {x: -0.60237515, y: -0.0005457781, z: 0.798213}
|
stabAxis: {x: -0.12657256, y: -0.108728364, z: 0.9859805}
|
||||||
stabStart: {x: 0.7911307, y: 0.68160576, z: -0.84916687}
|
stabStart: {x: 0.3463071, y: 1.37269, z: -1.1111009}
|
||||||
stabLength: 2.3069606
|
stabLength: 2.0542197
|
||||||
quality: 2.8422709
|
quality: 50
|
||||||
source: "runtime 02:41:23 samples=5/12(trim 6~10) path=2.91 \uCE7C\uB0A0\uD68C\uC804=165\xB0
|
source: "editbake 2026-09-07 09:58 Attack3_S_10101 f7~f11@60Hz n=9 path=3.03
|
||||||
\uCE7C\uB05D/\uC190\uC7A1\uC774=4.60 sweep=135.3\xB0 straight=0.79 fwd=0.80
|
bladeAng=154\xB0 sweep=181.9\xB0 pivot=\uCE7C\uB05D\uC6D0 tipR=0.957 hiltR=0.407
|
||||||
chord=2.31 pivot=\uCE7C\uB05D\uC6D0 tipR=1.249 hiltR=0.418 dev=0.014 fitQ=0.98
|
dev=0.071 fitQ=0.85 q=2.58 (WL804_Bake \xB7 \uB7F0\uD0C0\uC784 \uC7AC\uC2E4\uCE21\uC774
|
||||||
q=2.84 [\uCE7C\uB0A0\uD3C9\uBA74 \uD3F4\uBC31 \xB7 \uCC3D\uC774 \uD1F4\uD654]"
|
\uB36E\uC9C0 \uC54A\uB3C4\uB85D quality \uACE0\uC815)"
|
||||||
|
- classId: 10102
|
||||||
|
clipName: Effect_Slash_10102_3
|
||||||
|
kind: 1
|
||||||
|
planeNormal: {x: 0.16853563, y: -0.9836209, z: -0.06391966}
|
||||||
|
pivot: {x: 0.09357196, y: 1.2423787, z: -0.1380892}
|
||||||
|
bisector: {x: 0.94817513, y: 0.14405777, z: 0.28321597}
|
||||||
|
startDir: {x: 0.25327274, y: 0.10588382, z: -0.9615829}
|
||||||
|
tipRadius: 0.95663345
|
||||||
|
hiltRadius: 0.40691212
|
||||||
|
sweepDeg: 181.94073
|
||||||
|
stabAxis: {x: -0.12657256, y: -0.108728364, z: 0.9859805}
|
||||||
|
stabStart: {x: 0.3463071, y: 1.37269, z: -1.1111009}
|
||||||
|
stabLength: 2.0542197
|
||||||
|
quality: 50
|
||||||
|
source: "editbake 2026-09-07 09:58 Attack3_S_10102 f7~f11@60Hz n=9 path=3.03
|
||||||
|
bladeAng=154\xB0 sweep=181.9\xB0 pivot=\uCE7C\uB05D\uC6D0 tipR=0.957 hiltR=0.407
|
||||||
|
dev=0.071 fitQ=0.85 q=2.58 (WL804_Bake \xB7 \uB7F0\uD0C0\uC784 \uC7AC\uC2E4\uCE21\uC774
|
||||||
|
\uB36E\uC9C0 \uC54A\uB3C4\uB85D quality \uACE0\uC815)"
|
||||||
|
- classId: 10501
|
||||||
|
clipName: Effect_Slash_10501_3
|
||||||
|
kind: 1
|
||||||
|
planeNormal: {x: 0.16179384, y: -0.98433506, z: -0.070051745}
|
||||||
|
pivot: {x: 0.06956807, y: 1.2031016, z: -0.17662044}
|
||||||
|
bisector: {x: 0.98678565, y: 0.16074902, z: 0.020341488}
|
||||||
|
startDir: {x: 0.019124245, y: 0.074101284, z: -0.99706733}
|
||||||
|
tipRadius: 0.797171
|
||||||
|
hiltRadius: 0.35618255
|
||||||
|
sweepDeg: 178.7966
|
||||||
|
stabAxis: {x: 0.11699972, y: -0.072316095, z: 0.99049556}
|
||||||
|
stabStart: {x: 0.08246852, y: 1.288136, z: -1.0311682}
|
||||||
|
stabLength: 1.7564142
|
||||||
|
quality: 50
|
||||||
|
source: "editbake 2026-09-07 09:59 Attack3_S_10501 f7~f11@60Hz n=9 path=2.56
|
||||||
|
bladeAng=153\xB0 sweep=178.8\xB0 pivot=\uCE7C\uB05D\uC6D0 tipR=0.797 hiltR=0.356
|
||||||
|
dev=0.082 fitQ=0.79 q=2.03 (WL804_Bake \xB7 \uB7F0\uD0C0\uC784 \uC7AC\uC2E4\uCE21\uC774
|
||||||
|
\uB36E\uC9C0 \uC54A\uB3C4\uB85D quality \uACE0\uC815)"
|
||||||
|
- classId: 10502
|
||||||
|
clipName: Effect_Slash_10502_3
|
||||||
|
kind: 1
|
||||||
|
planeNormal: {x: 0.16322699, y: -0.9838918, z: -0.07289651}
|
||||||
|
pivot: {x: 0.06879786, y: 1.1890548, z: -0.17932634}
|
||||||
|
bisector: {x: 0.9864896, y: 0.16381113, z: -0.0020673866}
|
||||||
|
startDir: {x: -0.012327602, y: 0.0718477, z: -0.9973394}
|
||||||
|
tipRadius: 0.74294686
|
||||||
|
hiltRadius: 0.5402583
|
||||||
|
sweepDeg: 179.8086
|
||||||
|
stabAxis: {x: 0.14303008, y: -0.07050567, z: 0.98720384}
|
||||||
|
stabStart: {x: 0.055376478, y: 1.2679739, z: -0.98118037}
|
||||||
|
stabLength: 1.6514131
|
||||||
|
quality: 50
|
||||||
|
source: "editbake 2026-09-07 09:59 Attack3_S_10502 f7~f11@60Hz n=9 path=2.41
|
||||||
|
bladeAng=153\xB0 sweep=179.8\xB0 pivot=\uCE7C\uB05D\uC6D0 tipR=0.743 hiltR=0.540
|
||||||
|
dev=0.083 fitQ=0.78 q=1.87 (WL804_Bake \xB7 \uB7F0\uD0C0\uC784 \uC7AC\uC2E4\uCE21\uC774
|
||||||
|
\uB36E\uC9C0 \uC54A\uB3C4\uB85D quality \uACE0\uC815)"
|
||||||
swingMinSweepDegrees: 150
|
swingMinSweepDegrees: 150
|
||||||
swingMinTipHiltRatio: 3
|
swingMinTipHiltRatio: 3
|
||||||
stabMinStraightness: 0.86
|
stabMinStraightness: 0.86
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ PlayerSettings:
|
||||||
AndroidProfiler: 0
|
AndroidProfiler: 0
|
||||||
AndroidFilterTouchesWhenObscured: 0
|
AndroidFilterTouchesWhenObscured: 0
|
||||||
AndroidEnableSustainedPerformanceMode: 0
|
AndroidEnableSustainedPerformanceMode: 0
|
||||||
defaultScreenOrientation: 4
|
defaultScreenOrientation: 0
|
||||||
targetDevice: 2
|
targetDevice: 2
|
||||||
useOnDemandResources: 0
|
useOnDemandResources: 0
|
||||||
accelerometerFrequency: 60
|
accelerometerFrequency: 60
|
||||||
|
|
@ -59,10 +59,10 @@ PlayerSettings:
|
||||||
iosShowActivityIndicatorOnLoading: -1
|
iosShowActivityIndicatorOnLoading: -1
|
||||||
androidShowActivityIndicatorOnLoading: -1
|
androidShowActivityIndicatorOnLoading: -1
|
||||||
iosUseCustomAppBackgroundBehavior: 0
|
iosUseCustomAppBackgroundBehavior: 0
|
||||||
allowedAutorotateToPortrait: 0
|
allowedAutorotateToPortrait: 1
|
||||||
allowedAutorotateToPortraitUpsideDown: 0
|
allowedAutorotateToPortraitUpsideDown: 0
|
||||||
allowedAutorotateToLandscapeRight: 1
|
allowedAutorotateToLandscapeRight: 0
|
||||||
allowedAutorotateToLandscapeLeft: 1
|
allowedAutorotateToLandscapeLeft: 0
|
||||||
useOSAutorotation: 1
|
useOSAutorotation: 1
|
||||||
use32BitDisplayBuffer: 1
|
use32BitDisplayBuffer: 1
|
||||||
preserveFramebufferAlpha: 0
|
preserveFramebufferAlpha: 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue