319 lines
19 KiB
C#
319 lines
19 KiB
C#
|
|
// 발주서 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;
|
|||
|
|
}
|
|||
|
|
}
|