[WL-813t2] 언덕 이동 불가 (1/2): NavMesh 재베이크 (y>3 m Not Walkable 볼륨) (#813)
- WL_Nature.prefab: NavMeshModifierVolume(Not Walkable) 노드 1기 추가(+51/-0 · 기존 노드 수정 0) world center (115,24,10) · size (260,42,260) = 베이크 볼륨 전 XZ · Y[3.00, 45.00] - WL_Nature_NavMesh.asset 재베이크: 67536 -> 61215 m^2 (-9.4%) · y>3.0 면적 6044.6 -> 0.0 최고 y 22.79 -> 2.96 · 삼각형 5504 -> 4969 · 667268 -> 593864 bytes · GUID 보존 - 연결성 무변경: 시작->스포너 23/23 PathComplete · 시작->아레나 PathComplete Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
22832ff5d0
commit
99af7c8eea
|
|
@ -0,0 +1,279 @@
|
||||||
|
// 발주서 WL-813t2 — WL_Nature NavMesh 재베이크 (언덕 = 이동 불가)
|
||||||
|
//
|
||||||
|
// 실행 (CLAUDE.md §1: using 금지 · 네임스페이스 풀어 쓰기)
|
||||||
|
// 대조군(현행 프리팹 그대로 재베이크 · 저장 0) :
|
||||||
|
// unity command --project-path E:\NerdNavis\WL_wt\systems run_script --file AgentScripts/WL813t2_Bake.cs --entry WL813t2_Bake.Control --timeout 1800
|
||||||
|
// 본 베이크(WL 볼륨 노드 포함 · 자산 갱신) :
|
||||||
|
// … --entry WL813t2_Bake.Apply --timeout 1800
|
||||||
|
// 검증(자산 재로드 후 면적·연결성·스폰 격자) :
|
||||||
|
// … --entry WL813t2_Bake.Verify --timeout 1800
|
||||||
|
//
|
||||||
|
// 🔴 이 스크립트는 씬을 새로 만들고(EmptyScene) 프리팹을 임시 인스턴스화한 뒤 베이크한다. 씬은 저장하지 않는다.
|
||||||
|
// 자산 갱신은 GUID 보존을 위해 「임시 자산 생성 → 파일 바이트 덮어쓰기 → 임시 삭제 → 재임포트」로 한다.
|
||||||
|
|
||||||
|
public static class WL813t2_Bake
|
||||||
|
{
|
||||||
|
const string kMapPrefab = "Assets/Res_Addr/Map/WL_Nature.prefab";
|
||||||
|
const string kNavAsset = "Assets/WL/Settings/WL_Nature_NavMesh.asset";
|
||||||
|
const string kTmpAsset = "Assets/WL/Settings/WL_Nature_NavMesh_813t2TMP.asset";
|
||||||
|
const string kOutDir = "AgentScripts/staging/WL813t2";
|
||||||
|
|
||||||
|
static System.Text.StringBuilder s;
|
||||||
|
static void L(string t) { s.AppendLine(t); UnityEngine.Debug.Log("[WL813t2] " + t); }
|
||||||
|
|
||||||
|
static string Abs(string rel)
|
||||||
|
{
|
||||||
|
return System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(),
|
||||||
|
rel.Replace('/', System.IO.Path.DirectorySeparatorChar));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Flush(string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.Directory.CreateDirectory(Abs(kOutDir));
|
||||||
|
System.IO.File.WriteAllText(Abs(path), s.ToString(), new System.Text.UTF8Encoding(true));
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { UnityEngine.Debug.LogWarning("[WL813t2] 로그 저장 실패: " + e.Message); }
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
public static string Control() { return Run(false, kOutDir + "/BAKE_CONTROL.txt"); }
|
||||||
|
public static string Apply() { return Run(true, kOutDir + "/BAKE_APPLY.txt"); }
|
||||||
|
|
||||||
|
static string Run(bool save, string logPath)
|
||||||
|
{
|
||||||
|
s = new System.Text.StringBuilder();
|
||||||
|
L("===== WL-813t2 베이크 " + (save ? "APPLY(자산 갱신)" : "CONTROL(저장 0)") + " · " + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " =====");
|
||||||
|
|
||||||
|
UnityEditor.AssetDatabase.ImportAsset(kMapPrefab, UnityEditor.ImportAssetOptions.ForceUpdate);
|
||||||
|
var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(kMapPrefab);
|
||||||
|
if (prefab == null) { L("[에러] 프리팹 로드 실패"); return Flush(logPath); }
|
||||||
|
|
||||||
|
// 현행(커밋본) 자산 상태
|
||||||
|
var oldData = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AI.NavMeshData>(kNavAsset);
|
||||||
|
long oldBytes = 0;
|
||||||
|
try { oldBytes = new System.IO.FileInfo(Abs(kNavAsset)).Length; } catch { }
|
||||||
|
L("현행 자산 = " + kNavAsset + " · " + oldBytes + " bytes · " + (oldData != null ? "로드 OK" : "로드 실패"));
|
||||||
|
if (oldData != null) Report("현행(커밋본)", oldData, null);
|
||||||
|
|
||||||
|
// ── 임시 씬 + 프리팹 인스턴스 ────────────────────────────────────────
|
||||||
|
var scene = UnityEditor.SceneManagement.EditorSceneManager.NewScene(
|
||||||
|
UnityEditor.SceneManagement.NewSceneSetup.EmptyScene, UnityEditor.SceneManagement.NewSceneMode.Single);
|
||||||
|
var go = UnityEditor.PrefabUtility.InstantiatePrefab(prefab) as UnityEngine.GameObject;
|
||||||
|
if (go == null) { L("[에러] 인스턴스화 실패"); return Flush(logPath); }
|
||||||
|
L("인스턴스 root transform pos=" + go.transform.position.ToString("F3")
|
||||||
|
+ " rot=" + go.transform.rotation.eulerAngles.ToString("F1") + " scale=" + go.transform.localScale.ToString("F3"));
|
||||||
|
|
||||||
|
var sf = go.GetComponentInChildren<Unity.AI.Navigation.NavMeshSurface>(true);
|
||||||
|
if (sf == null) { L("[에러] NavMeshSurface 없음"); return Flush(logPath); }
|
||||||
|
|
||||||
|
var vols = go.GetComponentsInChildren<Unity.AI.Navigation.NavMeshModifierVolume>(true);
|
||||||
|
L("NavMeshModifierVolume " + vols.Length + "개");
|
||||||
|
for (int i = 0; i < vols.Length; i++)
|
||||||
|
{
|
||||||
|
var v = vols[i];
|
||||||
|
var wc = v.transform.TransformPoint(v.center);
|
||||||
|
L(" [" + i + "] " + Path(v.transform) + " · area=" + v.area + "(" + UnityEngine.AI.NavMesh.GetAreaNames()[UnityEngine.Mathf.Clamp(v.area, 0, 31)] + ")"
|
||||||
|
+ " · center(world)=" + wc.ToString("F1") + " size=" + v.size.ToString("F1")
|
||||||
|
+ " · Y[" + (wc.y - v.size.y * 0.5f).ToString("F2") + ", " + (wc.y + v.size.y * 0.5f).ToString("F2") + "]"
|
||||||
|
+ " · active=" + v.gameObject.activeInHierarchy + " enabled=" + v.enabled
|
||||||
|
+ " · affectsAgent0=" + v.AffectsAgentType(0) + " layer=" + v.gameObject.layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 베이크 ──────────────────────────────────────────────────────────
|
||||||
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
var keep = sf.navMeshData;
|
||||||
|
sf.navMeshData = null; // 새 데이터로 굽는다(기존 자산을 덮어쓰지 않게)
|
||||||
|
sf.BuildNavMesh();
|
||||||
|
sw.Stop();
|
||||||
|
var built = sf.navMeshData;
|
||||||
|
L("베이크 " + (sw.ElapsedMilliseconds / 1000.0).ToString("F1") + " s · 결과 " + (built != null ? "OK" : "null"));
|
||||||
|
if (built == null) { sf.navMeshData = keep; return Flush(logPath); }
|
||||||
|
|
||||||
|
sf.RemoveData(); // BuildNavMesh 가 이미 AddData 했다 — Report 가 다시 올리므로 중복(면적 2배)을 막는다
|
||||||
|
Report("재베이크", built, go);
|
||||||
|
|
||||||
|
if (save)
|
||||||
|
{
|
||||||
|
// GUID 보존 저장: 임시 자산 → 파일 바이트 덮어쓰기 → 임시 삭제 → 재임포트
|
||||||
|
if (UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(kTmpAsset) != null)
|
||||||
|
UnityEditor.AssetDatabase.DeleteAsset(kTmpAsset);
|
||||||
|
UnityEditor.AssetDatabase.CreateAsset(built, kTmpAsset);
|
||||||
|
UnityEditor.AssetDatabase.SaveAssets();
|
||||||
|
System.IO.File.Copy(Abs(kTmpAsset), Abs(kNavAsset), true);
|
||||||
|
UnityEditor.AssetDatabase.DeleteAsset(kTmpAsset);
|
||||||
|
UnityEditor.AssetDatabase.ImportAsset(kNavAsset, UnityEditor.ImportAssetOptions.ForceUpdate);
|
||||||
|
long newBytes = 0;
|
||||||
|
try { newBytes = new System.IO.FileInfo(Abs(kNavAsset)).Length; } catch { }
|
||||||
|
L("자산 저장 완료 · " + oldBytes + " → " + newBytes + " bytes (" + (newBytes - oldBytes) + ")");
|
||||||
|
L("GUID = " + UnityEditor.AssetDatabase.AssetPathToGUID(kNavAsset) + " (기대 ef300913c80dbda47b476b9261fdd786)");
|
||||||
|
}
|
||||||
|
else L("저장 안 함(대조군).");
|
||||||
|
|
||||||
|
sf.navMeshData = keep;
|
||||||
|
UnityEngine.Object.DestroyImmediate(go);
|
||||||
|
L("씬 정리 완료(저장 0) · scene=" + scene.name);
|
||||||
|
return Flush(logPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
public static string Verify()
|
||||||
|
{
|
||||||
|
s = new System.Text.StringBuilder();
|
||||||
|
L("===== WL-813t2 검증 (" + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ") =====");
|
||||||
|
UnityEditor.AssetDatabase.ImportAsset(kNavAsset, UnityEditor.ImportAssetOptions.ForceUpdate);
|
||||||
|
var data = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.AI.NavMeshData>(kNavAsset);
|
||||||
|
if (data == null) { L("[에러] 자산 로드 실패"); return Flush(kOutDir + "/VERIFY.txt"); }
|
||||||
|
long bytes = 0; try { bytes = new System.IO.FileInfo(Abs(kNavAsset)).Length; } catch { }
|
||||||
|
L("자산 " + kNavAsset + " · " + bytes + " bytes");
|
||||||
|
var prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(kMapPrefab);
|
||||||
|
Report("검증", data, prefab);
|
||||||
|
return Flush(kOutDir + "/VERIFY.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
/// <summary>NavMeshData 를 잠시 올려 면적·y 대역·격자·연결성·스폰 표집을 잰다.</summary>
|
||||||
|
static void Report(string tag, UnityEngine.AI.NavMeshData data, UnityEngine.GameObject mapRoot)
|
||||||
|
{
|
||||||
|
var inst = UnityEngine.AI.NavMesh.AddNavMeshData(data);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tri = UnityEngine.AI.NavMesh.CalculateTriangulation();
|
||||||
|
int n = tri.indices.Length / 3;
|
||||||
|
double total = 0.0, a15 = 0.0, a20 = 0.0, a25 = 0.0, a30 = 0.0, a35 = 0.0, a50 = 0.0;
|
||||||
|
float yMin = 1e9f, yMax = -1e9f;
|
||||||
|
for (int t = 0; t < n; t++)
|
||||||
|
{
|
||||||
|
var a = tri.vertices[tri.indices[t * 3 + 0]];
|
||||||
|
var b = tri.vertices[tri.indices[t * 3 + 1]];
|
||||||
|
var c = tri.vertices[tri.indices[t * 3 + 2]];
|
||||||
|
double ar = UnityEngine.Vector3.Cross(b - a, c - a).magnitude * 0.5;
|
||||||
|
float cy = (a.y + b.y + c.y) / 3f;
|
||||||
|
float hy = UnityEngine.Mathf.Max(a.y, UnityEngine.Mathf.Max(b.y, c.y));
|
||||||
|
total += ar;
|
||||||
|
if (cy > 1.5f) a15 += ar; if (cy > 2.0f) a20 += ar; if (cy > 2.5f) a25 += ar;
|
||||||
|
if (cy > 3.0f) a30 += ar; if (cy > 3.5f) a35 += ar; if (cy > 5.0f) a50 += ar;
|
||||||
|
if (cy < yMin) yMin = cy; if (hy > yMax) yMax = hy;
|
||||||
|
}
|
||||||
|
L("[" + tag + "] 삼각형 " + n + " · 정점 " + tri.vertices.Length + " · 총 면적 " + total.ToString("F1")
|
||||||
|
+ " m^2 · y " + yMin.ToString("F2") + " ~ " + yMax.ToString("F2"));
|
||||||
|
L("[" + tag + "] y>1.5 " + a15.ToString("F1") + " · y>2.0 " + a20.ToString("F1") + " · y>2.5 " + a25.ToString("F1")
|
||||||
|
+ " · **y>3.0 " + a30.ToString("F1") + "** · y>3.5 " + a35.ToString("F1") + " · y>5.0 " + a50.ToString("F1") + " m^2");
|
||||||
|
|
||||||
|
if (mapRoot != null)
|
||||||
|
{
|
||||||
|
// 연결성 — 시작점 → 스포너 23 · 아레나
|
||||||
|
var portal = FindByName(mapRoot.transform, "Portal_Start");
|
||||||
|
UnityEngine.Vector3 startPos = portal != null ? portal.position : new UnityEngine.Vector3(115f, 0f, 10f);
|
||||||
|
UnityEngine.AI.NavMeshHit sh;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(startPos, out sh, 5f, UnityEngine.AI.NavMesh.AllAreas))
|
||||||
|
{ L("[" + tag + "] [에러] 시작 지점이 NavMesh 밖"); }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var mgrs = mapRoot.GetComponentsInChildren<MobControlMgr>(true);
|
||||||
|
int comp = 0, part = 0, none = 0;
|
||||||
|
for (int i = 0; i < mgrs.Length; i++)
|
||||||
|
{
|
||||||
|
var p = mgrs[i].transform.position;
|
||||||
|
UnityEngine.AI.NavMeshHit h;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(p, out h, 5f, UnityEngine.AI.NavMesh.AllAreas)) { none++; L(" #" + mgrs[i].SpawnerID + " NavMesh 밖 " + p.ToString("F1")); continue; }
|
||||||
|
var path = new UnityEngine.AI.NavMeshPath();
|
||||||
|
UnityEngine.AI.NavMesh.CalculatePath(sh.position, h.position, UnityEngine.AI.NavMesh.AllAreas, path);
|
||||||
|
if (path.status == UnityEngine.AI.NavMeshPathStatus.PathComplete) comp++;
|
||||||
|
else { part++; L(" #" + mgrs[i].SpawnerID + " " + path.status + " " + p.ToString("F1")); }
|
||||||
|
}
|
||||||
|
L("[" + tag + "] 시작→스포너 " + mgrs.Length + " : PathComplete " + comp + " · Partial/Invalid " + part + " · 밖 " + none);
|
||||||
|
|
||||||
|
var arenaCfg = UnityEngine.Resources.Load<WL.Combat.Boss.WLBossArenaSettings>("WL/WLBossArenaSettings");
|
||||||
|
if (arenaCfg != null)
|
||||||
|
{
|
||||||
|
UnityEngine.AI.NavMeshHit ah;
|
||||||
|
bool on = UnityEngine.AI.NavMesh.SamplePosition(arenaCfg.arenaCenter, out ah, 5f, UnityEngine.AI.NavMesh.AllAreas);
|
||||||
|
string st = "밖";
|
||||||
|
if (on)
|
||||||
|
{
|
||||||
|
var p2 = new UnityEngine.AI.NavMeshPath();
|
||||||
|
UnityEngine.AI.NavMesh.CalculatePath(sh.position, ah.position, UnityEngine.AI.NavMesh.AllAreas, p2);
|
||||||
|
st = ah.position.ToString("F2") + " · 시작→아레나 " + p2.status;
|
||||||
|
}
|
||||||
|
L("[" + tag + "] 아레나 " + arenaCfg.arenaCenter.ToString("F1") + " → " + st);
|
||||||
|
|
||||||
|
// 아레나 원주 12방향에서 아레나 중심으로 경로(아레나 내부 연결성)
|
||||||
|
int okr = 0;
|
||||||
|
for (int i = 0; i < 12 && on; i++)
|
||||||
|
{
|
||||||
|
float ang = i * UnityEngine.Mathf.PI * 2f / 12f;
|
||||||
|
var rp = arenaCfg.arenaCenter + new UnityEngine.Vector3(UnityEngine.Mathf.Cos(ang), 0f, UnityEngine.Mathf.Sin(ang)) * (arenaCfg.arenaRadius * 0.8f);
|
||||||
|
UnityEngine.AI.NavMeshHit rh;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(rp, out rh, 5f, UnityEngine.AI.NavMesh.AllAreas)) continue;
|
||||||
|
var p3 = new UnityEngine.AI.NavMeshPath();
|
||||||
|
UnityEngine.AI.NavMesh.CalculatePath(rh.position, ah.position, UnityEngine.AI.NavMesh.AllAreas, p3);
|
||||||
|
if (p3.status == UnityEngine.AI.NavMeshPathStatus.PathComplete) okr++;
|
||||||
|
}
|
||||||
|
L("[" + tag + "] 아레나 원주 12점 → 중심 PathComplete " + okr + "/12");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 주요 앵커 y
|
||||||
|
string[] anchors = { "Portal_Start", "Reincarnation_Start" };
|
||||||
|
for (int i = 0; i < anchors.Length; i++)
|
||||||
|
{
|
||||||
|
var tr = FindByName(mapRoot.transform, anchors[i]);
|
||||||
|
if (tr == null) { L("[" + tag + "] 앵커 " + anchors[i] + " 없음"); continue; }
|
||||||
|
UnityEngine.AI.NavMeshHit hh;
|
||||||
|
bool onA = UnityEngine.AI.NavMesh.SamplePosition(tr.position, out hh, 5f, UnityEngine.AI.NavMesh.AllAreas);
|
||||||
|
L("[" + tag + "] 앵커 " + anchors[i] + " " + tr.position.ToString("F2") + " → " + (onA ? hh.position.ToString("F2") : "NavMesh 밖"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 스폰 표집 — 스포너별 랜덤 표집(원본 DSUtil 규약 재현) 1000점의 y 분포
|
||||||
|
SpawnProbe(tag, mgrs, sh.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally { UnityEngine.AI.NavMesh.RemoveNavMeshData(inst); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>원본 Get_RandomPos_onNavMesh_Reachable 규약(스냅 2 m · 50회 · PathComplete)으로 1000점 표집.</summary>
|
||||||
|
static void SpawnProbe(string tag, MobControlMgr[] mgrs, UnityEngine.Vector3 startOnMesh)
|
||||||
|
{
|
||||||
|
if (mgrs.Length == 0) return;
|
||||||
|
UnityEngine.Random.InitState(813);
|
||||||
|
int samples = 1000, hi = 0, offMesh = 0, fellBack = 0;
|
||||||
|
var band = new System.Collections.Generic.SortedDictionary<int, int>();
|
||||||
|
for (int i = 0; i < samples; i++)
|
||||||
|
{
|
||||||
|
var mgr = mgrs[i % mgrs.Length];
|
||||||
|
var origin = mgr.transform.position;
|
||||||
|
var p = DSUtil.Get_RandomPos_onNavMesh_Reachable(origin, 0f, mgr.MobGenRange, 2f, 50);
|
||||||
|
if ((p - origin).sqrMagnitude < 1e-6f) fellBack++;
|
||||||
|
UnityEngine.AI.NavMeshHit h;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(p, out h, 1f, UnityEngine.AI.NavMesh.AllAreas)) offMesh++;
|
||||||
|
float y = h.hit ? h.position.y : p.y;
|
||||||
|
if (y > 3.0f) hi++;
|
||||||
|
int k = UnityEngine.Mathf.FloorToInt(y); int c; band.TryGetValue(k, out c); band[k] = c + 1;
|
||||||
|
}
|
||||||
|
L("[" + tag + "] 스폰 표집 " + samples + "점 · **y>3.0 " + hi + "** · NavMesh 밖 " + offMesh + " · 원점 폴백 " + fellBack);
|
||||||
|
var sb = new System.Text.StringBuilder(" y 분포:");
|
||||||
|
foreach (var kv in band) sb.Append(" [" + kv.Key + "]=" + kv.Value);
|
||||||
|
L(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Path(UnityEngine.Transform t)
|
||||||
|
{
|
||||||
|
string p = t.name;
|
||||||
|
while (t.parent != null) { t = t.parent; p = t.name + "/" + p; }
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UnityEngine.Transform FindByName(UnityEngine.Transform t, string name)
|
||||||
|
{
|
||||||
|
if (t.name == name) return t;
|
||||||
|
for (int i = 0; i < t.childCount; i++)
|
||||||
|
{
|
||||||
|
var r = FindByName(t.GetChild(i), name);
|
||||||
|
if (r != null) return r;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,318 @@
|
||||||
|
// 발주서 WL-813t2 — 언덕 지형 이동 불가: 실측 프로브 (NavMesh 출처·베이크 설정·고지대 면적·스포너 겹침)
|
||||||
|
//
|
||||||
|
// 실행 (CLAUDE.md §1: using 금지 · 네임스페이스 풀어 쓰기)
|
||||||
|
// unity command --project-path E:\NerdNavis\WL_wt\systems run_script --file AgentScripts/WL813t2_Probe.cs --entry WL813t2_Probe.Measure --timeout 900
|
||||||
|
//
|
||||||
|
// 재는 것 — 전부 AssetDatabase 읽기 전용(에셋 수정 0):
|
||||||
|
// (a) WL_Nature 가 쓰는 NavMesh 출처 = NavMeshSurface.m_NavMeshData 자산 경로 · 베이크 설정(agent radius/height/slope/climb)
|
||||||
|
// (b) NavMesh 삼각형 면적을 y 대역별로 집계 → 「언덕 위 NavMesh 면적」
|
||||||
|
// (c) 샘플 격자(2 m) — 표면 바운즈 안 (x,z) 마다 SamplePosition → on/off · y
|
||||||
|
// (d) 고지대 삼각형을 4 m 격자로 묶어 연결 성분(클러스터) → AABB + 면적 (= NavMeshModifierVolume 배치 후보)
|
||||||
|
// (e) 스포너 노드(MobControlMgr) 좌표 · y · 고지대 여부
|
||||||
|
// (f) 경로 연결성 — 시작점 · 존 앵커 · 아레나 사이 CalculatePath
|
||||||
|
//
|
||||||
|
// 수치는 전부 프로젝트 데이터에서 읽는다(C45). 프로브 상수는 "어디를 보나"(대역 폭 · 격자 간격)뿐이다.
|
||||||
|
|
||||||
|
public static class WL813t2_Probe
|
||||||
|
{
|
||||||
|
const string kMapPrefab = "Assets/Res_Addr/Map/WL_Nature.prefab";
|
||||||
|
const string kOutDir = "AgentScripts/staging/WL813t2";
|
||||||
|
const string kLog = kOutDir + "/MEASURE.txt";
|
||||||
|
|
||||||
|
static System.Text.StringBuilder s;
|
||||||
|
static void L(string t) { s.AppendLine(t); UnityEngine.Debug.Log("[WL813t2] " + t); }
|
||||||
|
|
||||||
|
static string Abs(string rel)
|
||||||
|
{
|
||||||
|
return System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(),
|
||||||
|
rel.Replace('/', System.IO.Path.DirectorySeparatorChar));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Flush(string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
System.IO.Directory.CreateDirectory(Abs(kOutDir));
|
||||||
|
System.IO.File.WriteAllText(Abs(path), s.ToString(), new System.Text.UTF8Encoding(true));
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { UnityEngine.Debug.LogWarning("[WL813t2] 로그 저장 실패: " + e.Message); }
|
||||||
|
return s.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
public static string Measure()
|
||||||
|
{
|
||||||
|
s = new System.Text.StringBuilder();
|
||||||
|
L("===== WL-813t2 실측 (" + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ") =====");
|
||||||
|
|
||||||
|
var root = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.GameObject>(kMapPrefab);
|
||||||
|
if (root == null) { L("[에러] 맵 프리팹 로드 실패: " + kMapPrefab); return Flush(kLog); }
|
||||||
|
|
||||||
|
// ── (a) NavMesh 출처 · 베이크 설정 ───────────────────────────────────
|
||||||
|
L("");
|
||||||
|
L("── (a) NavMesh 출처 · 베이크 설정 ──");
|
||||||
|
var surfaces = root.GetComponentsInChildren<Unity.AI.Navigation.NavMeshSurface>(true);
|
||||||
|
L(" NavMeshSurface 개수 = " + surfaces.Length);
|
||||||
|
UnityEngine.AI.NavMeshData navData = null;
|
||||||
|
UnityEngine.Bounds volBounds = new UnityEngine.Bounds(UnityEngine.Vector3.zero, UnityEngine.Vector3.one);
|
||||||
|
for (int i = 0; i < surfaces.Length; i++)
|
||||||
|
{
|
||||||
|
var sf = surfaces[i];
|
||||||
|
string dataPath = sf.navMeshData != null ? UnityEditor.AssetDatabase.GetAssetPath(sf.navMeshData) : "(null)";
|
||||||
|
L(" [" + i + "] " + sf.name + " · agentTypeID=" + sf.agentTypeID + " · collectObjects=" + sf.collectObjects
|
||||||
|
+ " · size=" + sf.size.ToString("F0") + " · center=" + sf.center.ToString("F0")
|
||||||
|
+ " · layerMask=" + sf.layerMask.value + " · useGeometry=" + sf.useGeometry
|
||||||
|
+ " · defaultArea=" + sf.defaultArea + " · minRegionArea=" + sf.minRegionArea
|
||||||
|
+ " · overrideVoxel=" + sf.overrideVoxelSize + " voxel=" + sf.voxelSize.ToString("F5")
|
||||||
|
+ " · overrideTile=" + sf.overrideTileSize + " tile=" + sf.tileSize);
|
||||||
|
L(" 데이터 = " + dataPath + (sf.navMeshData != null ? " · 원점=" + sf.navMeshData.position.ToString("F1") : ""));
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
navData = sf.navMeshData;
|
||||||
|
volBounds = new UnityEngine.Bounds(sf.transform.TransformPoint(sf.center), sf.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var bs = UnityEngine.AI.NavMesh.GetSettingsByID(0);
|
||||||
|
L(" Agent 0 (" + UnityEngine.AI.NavMesh.GetSettingsNameFromID(0) + ") radius=" + bs.agentRadius
|
||||||
|
+ " height=" + bs.agentHeight + " slope=" + bs.agentSlope + " climb=" + bs.agentClimb
|
||||||
|
+ " minRegionArea=" + bs.minRegionArea + " voxel=" + bs.voxelSize.ToString("F5"));
|
||||||
|
L(" NavMeshModifierVolume 기존 개수 = " + root.GetComponentsInChildren<Unity.AI.Navigation.NavMeshModifierVolume>(true).Length);
|
||||||
|
L(" NavMeshModifier 기존 개수 = " + root.GetComponentsInChildren<Unity.AI.Navigation.NavMeshModifier>(true).Length);
|
||||||
|
if (navData == null) { L("[에러] NavMeshData 없음"); return Flush(kLog); }
|
||||||
|
|
||||||
|
// ── 기준 지면 · 앵커 ────────────────────────────────────────────────
|
||||||
|
var portal = FindByName(root.transform, "Portal_Start");
|
||||||
|
UnityEngine.Vector3 startPos = portal != null ? portal.position : new UnityEngine.Vector3(115f, 0f, 10f);
|
||||||
|
L(" Portal_Start = " + startPos.ToString("F2"));
|
||||||
|
|
||||||
|
var inst = UnityEngine.AI.NavMesh.AddNavMeshData(navData);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tri = UnityEngine.AI.NavMesh.CalculateTriangulation();
|
||||||
|
int triCount = tri.indices.Length / 3;
|
||||||
|
L(" NavMesh 삼각형 " + triCount + " · 정점 " + tri.vertices.Length);
|
||||||
|
|
||||||
|
// ── (b) y 대역별 면적 ────────────────────────────────────────────
|
||||||
|
L("");
|
||||||
|
L("── (b) NavMesh 면적 y 대역 분포 (삼각형 3D 면적 합 · m^2) ──");
|
||||||
|
double total = 0.0;
|
||||||
|
double yMin = 1e9, yMax = -1e9;
|
||||||
|
var band = new System.Collections.Generic.SortedDictionary<int, double>();
|
||||||
|
for (int t = 0; t < triCount; t++)
|
||||||
|
{
|
||||||
|
var a = tri.vertices[tri.indices[t * 3 + 0]];
|
||||||
|
var b = tri.vertices[tri.indices[t * 3 + 1]];
|
||||||
|
var c = tri.vertices[tri.indices[t * 3 + 2]];
|
||||||
|
double ar = UnityEngine.Vector3.Cross(b - a, c - a).magnitude * 0.5;
|
||||||
|
total += ar;
|
||||||
|
float cy = (a.y + b.y + c.y) / 3f;
|
||||||
|
if (cy < yMin) yMin = cy; if (cy > yMax) yMax = cy;
|
||||||
|
int k = UnityEngine.Mathf.FloorToInt(cy); // 1 m 대역
|
||||||
|
double cur; band.TryGetValue(k, out cur); band[k] = cur + ar;
|
||||||
|
}
|
||||||
|
L(" 총 면적 = " + total.ToString("F1") + " m^2 · y 범위 " + yMin.ToString("F2") + " ~ " + yMax.ToString("F2"));
|
||||||
|
double cum = 0.0;
|
||||||
|
foreach (var kv in band)
|
||||||
|
{
|
||||||
|
cum += kv.Value;
|
||||||
|
L(" y [" + kv.Key + ", " + (kv.Key + 1) + ") 면적 " + kv.Value.ToString("F1")
|
||||||
|
+ " (" + (kv.Value / total * 100.0).ToString("F2") + " %) · 누적 " + (cum / total * 100.0).ToString("F2") + " %");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
float thr = 1.5f + i * 1.0f;
|
||||||
|
double hi = 0.0;
|
||||||
|
for (int t = 0; t < triCount; t++)
|
||||||
|
{
|
||||||
|
var a = tri.vertices[tri.indices[t * 3 + 0]];
|
||||||
|
var b = tri.vertices[tri.indices[t * 3 + 1]];
|
||||||
|
var c = tri.vertices[tri.indices[t * 3 + 2]];
|
||||||
|
float cy = (a.y + b.y + c.y) / 3f;
|
||||||
|
if (cy > thr) hi += UnityEngine.Vector3.Cross(b - a, c - a).magnitude * 0.5;
|
||||||
|
}
|
||||||
|
L(" y > " + thr.ToString("F1") + " m 면적 = " + hi.ToString("F1") + " m^2 (" + (hi / total * 100.0).ToString("F2") + " %)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── (c) 샘플 격자 ────────────────────────────────────────────────
|
||||||
|
L("");
|
||||||
|
L("── (c) 샘플 격자 2 m (셀 중심 수직선 1 m 간격 · SamplePosition 반경 1 m) ──");
|
||||||
|
GridScan(volBounds, 2f, 1f, (float)yMin - 1f, (float)yMax + 1f);
|
||||||
|
|
||||||
|
// ── (d) 고지대 클러스터 (4 m 셀 · 8이웃 연결 성분) ───────────────
|
||||||
|
L("");
|
||||||
|
L("── (d) 고지대 NavMesh 클러스터 (셀 4 m · y > 2.0 m 삼각형) ──");
|
||||||
|
Clusters(tri, triCount, 2.0f, 4f);
|
||||||
|
L("");
|
||||||
|
L("── (d2) 고지대 NavMesh 클러스터 (셀 4 m · y > 1.5 m 삼각형) ──");
|
||||||
|
Clusters(tri, triCount, 1.5f, 4f);
|
||||||
|
|
||||||
|
// ── (e) 스포너 노드 ──────────────────────────────────────────────
|
||||||
|
L("");
|
||||||
|
L("── (e) 스포너 노드 (MobControlMgr) ──");
|
||||||
|
var mgrs = root.GetComponentsInChildren<MobControlMgr>(true);
|
||||||
|
L(" 스포너 " + mgrs.Length + "기");
|
||||||
|
for (int i = 0; i < mgrs.Length; i++)
|
||||||
|
{
|
||||||
|
var p = mgrs[i].transform.position;
|
||||||
|
UnityEngine.AI.NavMeshHit h;
|
||||||
|
bool on = UnityEngine.AI.NavMesh.SamplePosition(p, out h, 2f, UnityEngine.AI.NavMesh.AllAreas);
|
||||||
|
L(" #" + mgrs[i].SpawnerID + " " + mgrs[i].name + " pos=" + p.ToString("F2")
|
||||||
|
+ " range=" + mgrs[i].MobGenRange
|
||||||
|
+ " · NavMesh " + (on ? "y=" + h.position.y.ToString("F2") + " d=" + UnityEngine.Vector3.Distance(p, h.position).ToString("F2") : "밖"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── (f) 경로 연결성 ──────────────────────────────────────────────
|
||||||
|
L("");
|
||||||
|
L("── (f) 경로 연결성 (시작점 ↔ 스포너 원점 · 아레나) ──");
|
||||||
|
Connectivity(root, mgrs, startPos);
|
||||||
|
}
|
||||||
|
finally { UnityEngine.AI.NavMesh.RemoveNavMeshData(inst); }
|
||||||
|
|
||||||
|
L("");
|
||||||
|
L("===== 끝 =====");
|
||||||
|
return Flush(kLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
/// <summary>격자 스캔. 셀 중심 수직선을 1 m 간격으로 훑어 그 (x,z) 위에 있는 NavMesh 높이를 전부 모은다.</summary>
|
||||||
|
public static void GridScan(UnityEngine.Bounds b, float step, float snap, float yLo, float yHi)
|
||||||
|
{
|
||||||
|
int on = 0, off = 0, hiCells = 0;
|
||||||
|
var band = new System.Collections.Generic.SortedDictionary<int, int>();
|
||||||
|
float x0 = b.min.x, x1 = b.max.x, z0 = b.min.z, z1 = b.max.z;
|
||||||
|
for (float x = x0; x <= x1; x += step)
|
||||||
|
for (float z = z0; z <= z1; z += step)
|
||||||
|
{
|
||||||
|
bool found = false; float best = -1e9f;
|
||||||
|
for (float y = yLo; y <= yHi; y += 1f)
|
||||||
|
{
|
||||||
|
UnityEngine.AI.NavMeshHit h;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(new UnityEngine.Vector3(x, y, z), out h, snap, UnityEngine.AI.NavMesh.AllAreas)) continue;
|
||||||
|
if (UnityEngine.Mathf.Abs(h.position.x - x) > snap || UnityEngine.Mathf.Abs(h.position.z - z) > snap) continue;
|
||||||
|
found = true; if (h.position.y > best) best = h.position.y;
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
on++; int k = UnityEngine.Mathf.FloorToInt(best); int c; band.TryGetValue(k, out c); band[k] = c + 1;
|
||||||
|
if (best > 2.0f) hiCells++;
|
||||||
|
}
|
||||||
|
else off++;
|
||||||
|
}
|
||||||
|
L(" 격자 " + (on + off) + " 점(간격 " + step + " m) · NavMesh 위 " + on + " · 밖 " + off + " · **y > 2.0 셀 " + hiCells + "**");
|
||||||
|
foreach (var kv in band) L(" y [" + kv.Key + ", " + (kv.Key + 1) + ") " + kv.Value + " 점");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 고지대 삼각형을 셀로 묶어 8이웃 연결 성분을 만든다.
|
||||||
|
static void Clusters(UnityEngine.AI.NavMeshTriangulation tri, int triCount, float thr, float cell)
|
||||||
|
{
|
||||||
|
var cells = new System.Collections.Generic.Dictionary<long, double>();
|
||||||
|
var cellY = new System.Collections.Generic.Dictionary<long, UnityEngine.Vector2>(); // (minY, maxY)
|
||||||
|
for (int t = 0; t < triCount; t++)
|
||||||
|
{
|
||||||
|
var a = tri.vertices[tri.indices[t * 3 + 0]];
|
||||||
|
var b = tri.vertices[tri.indices[t * 3 + 1]];
|
||||||
|
var c = tri.vertices[tri.indices[t * 3 + 2]];
|
||||||
|
float cy = (a.y + b.y + c.y) / 3f;
|
||||||
|
if (cy <= thr) continue;
|
||||||
|
double ar = UnityEngine.Vector3.Cross(b - a, c - a).magnitude * 0.5;
|
||||||
|
float cx = (a.x + b.x + c.x) / 3f, cz = (a.z + b.z + c.z) / 3f;
|
||||||
|
int ix = UnityEngine.Mathf.FloorToInt(cx / cell), iz = UnityEngine.Mathf.FloorToInt(cz / cell);
|
||||||
|
long key = ((long)(ix + 100000) << 20) | (long)(iz + 100000);
|
||||||
|
double cur; cells.TryGetValue(key, out cur); cells[key] = cur + ar;
|
||||||
|
UnityEngine.Vector2 yy;
|
||||||
|
float lo = UnityEngine.Mathf.Min(a.y, UnityEngine.Mathf.Min(b.y, c.y));
|
||||||
|
float hi = UnityEngine.Mathf.Max(a.y, UnityEngine.Mathf.Max(b.y, c.y));
|
||||||
|
if (cellY.TryGetValue(key, out yy)) cellY[key] = new UnityEngine.Vector2(UnityEngine.Mathf.Min(yy.x, lo), UnityEngine.Mathf.Max(yy.y, hi));
|
||||||
|
else cellY[key] = new UnityEngine.Vector2(lo, hi);
|
||||||
|
}
|
||||||
|
L(" 고지대 셀 " + cells.Count + "개 (셀 " + cell + " m)");
|
||||||
|
|
||||||
|
var seen = new System.Collections.Generic.HashSet<long>();
|
||||||
|
var stack = new System.Collections.Generic.Stack<long>();
|
||||||
|
var results = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<double, string>>();
|
||||||
|
int cid = 0;
|
||||||
|
foreach (var kv in cells)
|
||||||
|
{
|
||||||
|
if (seen.Contains(kv.Key)) continue;
|
||||||
|
seen.Add(kv.Key); stack.Clear(); stack.Push(kv.Key);
|
||||||
|
double area = 0.0; int n = 0;
|
||||||
|
float minX = 1e9f, maxX = -1e9f, minZ = 1e9f, maxZ = -1e9f, minY = 1e9f, maxY = -1e9f;
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
long k = stack.Pop();
|
||||||
|
int ix = (int)((k >> 20) & 0xFFFFF) - 100000;
|
||||||
|
int iz = (int)(k & 0xFFFFF) - 100000;
|
||||||
|
area += cells[k]; n++;
|
||||||
|
float cx0 = ix * cell, cz0 = iz * cell;
|
||||||
|
if (cx0 < minX) minX = cx0; if (cx0 + cell > maxX) maxX = cx0 + cell;
|
||||||
|
if (cz0 < minZ) minZ = cz0; if (cz0 + cell > maxZ) maxZ = cz0 + cell;
|
||||||
|
var yy = cellY[k];
|
||||||
|
if (yy.x < minY) minY = yy.x; if (yy.y > maxY) maxY = yy.y;
|
||||||
|
for (int dx = -1; dx <= 1; dx++)
|
||||||
|
for (int dz = -1; dz <= 1; dz++)
|
||||||
|
{
|
||||||
|
if (dx == 0 && dz == 0) continue;
|
||||||
|
long nk = ((long)(ix + dx + 100000) << 20) | (long)(iz + dz + 100000);
|
||||||
|
if (cells.ContainsKey(nk) && !seen.Contains(nk)) { seen.Add(nk); stack.Push(nk); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cid++;
|
||||||
|
results.Add(new System.Collections.Generic.KeyValuePair<double, string>(area,
|
||||||
|
string.Format(" 셀 {0,4} · 면적 {1,9:F1} m^2 · X[{2,7:F1},{3,7:F1}] Z[{4,7:F1},{5,7:F1}] Y[{6,5:F2},{7,5:F2}] · 중심 ({8:F1}, {9:F1}) · 크기 {10:F1}×{11:F1}",
|
||||||
|
n, area, minX, maxX, minZ, maxZ, minY, maxY, (minX + maxX) * 0.5f, (minZ + maxZ) * 0.5f, maxX - minX, maxZ - minZ)));
|
||||||
|
}
|
||||||
|
results.Sort(delegate (System.Collections.Generic.KeyValuePair<double, string> a, System.Collections.Generic.KeyValuePair<double, string> b)
|
||||||
|
{ return b.Key.CompareTo(a.Key); });
|
||||||
|
for (int i = 0; i < results.Count && i < 60; i++) L(" C" + (i + 1) + results[i].Value);
|
||||||
|
if (results.Count > 60) L(" … 외 " + (results.Count - 60) + "개 클러스터");
|
||||||
|
L(" 클러스터 총 " + cid + "개");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Connectivity(UnityEngine.GameObject root, MobControlMgr[] mgrs, UnityEngine.Vector3 startPos)
|
||||||
|
{
|
||||||
|
UnityEngine.AI.NavMeshHit sh;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(startPos, out sh, 5f, UnityEngine.AI.NavMesh.AllAreas))
|
||||||
|
{ L(" [에러] 시작 지점이 NavMesh 밖"); return; }
|
||||||
|
L(" 시작 스냅 = " + sh.position.ToString("F2"));
|
||||||
|
int comp = 0, part = 0, none = 0;
|
||||||
|
for (int i = 0; i < mgrs.Length; i++)
|
||||||
|
{
|
||||||
|
var p = mgrs[i].transform.position;
|
||||||
|
UnityEngine.AI.NavMeshHit h;
|
||||||
|
if (!UnityEngine.AI.NavMesh.SamplePosition(p, out h, 5f, UnityEngine.AI.NavMesh.AllAreas)) { none++; L(" #" + mgrs[i].SpawnerID + " NavMesh 밖"); continue; }
|
||||||
|
var path = new UnityEngine.AI.NavMeshPath();
|
||||||
|
UnityEngine.AI.NavMesh.CalculatePath(sh.position, h.position, UnityEngine.AI.NavMesh.AllAreas, path);
|
||||||
|
if (path.status == UnityEngine.AI.NavMeshPathStatus.PathComplete) comp++;
|
||||||
|
else { part++; L(" #" + mgrs[i].SpawnerID + " " + path.status + " " + p.ToString("F1")); }
|
||||||
|
}
|
||||||
|
L(" 스포너 " + mgrs.Length + " → PathComplete " + comp + " · Partial/Invalid " + part + " · NavMesh 밖 " + none);
|
||||||
|
|
||||||
|
// 아레나 중심(813e = 139, 0.8, −20) 은 SO 에서 읽는다.
|
||||||
|
var arenaCfg = UnityEngine.Resources.Load<WL.Combat.Boss.WLBossArenaSettings>("WL/WLBossArenaSettings");
|
||||||
|
if (arenaCfg != null)
|
||||||
|
{
|
||||||
|
var ac = arenaCfg.arenaCenter;
|
||||||
|
UnityEngine.AI.NavMeshHit ah;
|
||||||
|
bool on = UnityEngine.AI.NavMesh.SamplePosition(ac, out ah, 5f, UnityEngine.AI.NavMesh.AllAreas);
|
||||||
|
L(" 아레나 중심(SO) = " + ac.ToString("F2") + " r=" + arenaCfg.arenaRadius + " · NavMesh " + (on ? ah.position.ToString("F2") : "밖"));
|
||||||
|
if (on)
|
||||||
|
{
|
||||||
|
var path = new UnityEngine.AI.NavMeshPath();
|
||||||
|
UnityEngine.AI.NavMesh.CalculatePath(sh.position, ah.position, UnityEngine.AI.NavMesh.AllAreas, path);
|
||||||
|
L(" 시작 → 아레나 = " + path.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else L(" [미확인] WLBossArenaSettings 로드 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
static UnityEngine.Transform FindByName(UnityEngine.Transform t, string name)
|
||||||
|
{
|
||||||
|
if (t.name == name) return t;
|
||||||
|
for (int i = 0; i < t.childCount; i++)
|
||||||
|
{
|
||||||
|
var r = FindByName(t.GetChild(i), name);
|
||||||
|
if (r != null) return r;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2654,6 +2654,7 @@ Transform:
|
||||||
- {fileID: 5625234380910883685}
|
- {fileID: 5625234380910883685}
|
||||||
- {fileID: 8168532711777316576}
|
- {fileID: 8168532711777316576}
|
||||||
- {fileID: 8130050000000000002}
|
- {fileID: 8130050000000000002}
|
||||||
|
- {fileID: 8132000000000000002}
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!114 &5695711385560286233
|
--- !u!114 &5695711385560286233
|
||||||
|
|
@ -69610,3 +69611,53 @@ MonoBehaviour:
|
||||||
m_hasFontAssetChanged: 0
|
m_hasFontAssetChanged: 0
|
||||||
m_renderer: {fileID: 8130240000000000006}
|
m_renderer: {fileID: 8130240000000000006}
|
||||||
m_maskType: 0
|
m_maskType: 0
|
||||||
|
--- !u!1 &8132000000000000001
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 8132000000000000002}
|
||||||
|
- component: {fileID: 8132000000000000003}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: WL813t2_HillNotWalkable
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &8132000000000000002
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8132000000000000001}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 115, y: 24, z: 10}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 4502284306717827522}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &8132000000000000003
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8132000000000000001}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 35e95dc5ff2b64380880dd7ac5922847, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Unity.AI.Navigation::Unity.AI.Navigation.NavMeshModifierVolume
|
||||||
|
m_SerializedVersion: 0
|
||||||
|
m_Size: {x: 260, y: 42, z: 260}
|
||||||
|
m_Center: {x: 0, y: 0, z: 0}
|
||||||
|
m_Area: 1
|
||||||
|
m_AffectedAgents:
|
||||||
|
- -1
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue