feat(enemy): 感知系统新增 BoxLOS 槽位(横向矩形视野+遮挡)

BoxCast 是纯几何矩形无遮挡,BatchLOS/Sight 有遮挡但为圆形。
新增 BoxLOS 槽位:OverlapBox 矩形区域 + 从感知原点多点 LOS 遮挡校验,
用于走廊/平台等横向矩形视野带遮挡的场景。

- enum 末尾追加 BoxLOS(避免打乱既有序列化索引)
- 复用 boxSize/boxOffset 定义矩形、losBlockMask/losRayCount/losMinVisibility 定义遮挡
- 方向由 boxOffset.x 控制:0=前后双向对称,boxSize.x*0.5=仅前向
- Editor 补 Gizmo(矩形+眼点+实时检测连线)与条件字段

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 15:54:17 +08:00
co-authored by Claude Opus 4.8
parent fccd87df6b
commit 859d4e9bd6
2 changed files with 100 additions and 7 deletions
@@ -94,6 +94,11 @@ namespace BaseGames.Editor
}
break;
case SlotType.BoxLOS:
if (slot.boxSize.x > 0f && slot.boxSize.y > 0f)
DrawBoxLOSGizmo(system, slotCenter, facingSign, slot, fill, outline, isSelected);
break;
case SlotType.BatchLOS:
DrawBatchLOSGizmo(system, slotCenter, slot, outline, isSelected);
break;
@@ -366,6 +371,45 @@ namespace BaseGames.Editor
Handles.DrawSolidRectangleWithOutline(corners, fill, outline);
}
/// <summary>
/// BoxLOS 槽位 Gizmo:矩形区域(同 BoxCast+ 眼点 + LOS 提示 + 运行时检测连线。
/// </summary>
static void DrawBoxLOSGizmo(PhysicsPerceptionSystem system, Vector3 slotCenter, float facingSign,
PhysicsPerceptionSystem.PerceptionSlot slot, Color fill, Color outline, bool isSelected)
{
DrawBoxGizmo(slotCenter, facingSign, slot, fill, outline);
// 眼点(表明有遮挡检测能力,射线原点为感知原点)
Handles.color = outline;
Handles.DrawSolidDisc(slotCenter, Vector3.forward, 0.07f);
if (isSelected)
{
GUIStyle style = new GUIStyle(GUI.skin.label)
{
fontSize = 9,
alignment = TextAnchor.MiddleCenter,
normal = { textColor = new Color(outline.r, outline.g, outline.b, 0.85f) }
};
float labelY = slot.boxOffset.y + slot.boxSize.y * 0.5f + 0.2f;
Handles.Label(slotCenter + new Vector3(0f, labelY, 0f), "BoxLOS", style);
}
// 运行时:绿线连到检测到的目标
if (!Application.isPlaying) return;
var detected = system.EditorDetected;
if (detected == null || !detected.TryGetValue(slot.slotName, out var hits) || hits.Count == 0) return;
Color green = new Color(0.2f, 1.0f, 0.3f, 0.9f);
Handles.color = green;
foreach (var go in hits)
{
if (go == null) continue;
Handles.DrawLine(slotCenter, go.transform.position);
Handles.DrawSolidDisc(go.transform.position, Vector3.forward, 0.09f);
}
}
static Vector3 RotateVec3(Vector3 v, float angleDeg)
{
float rad = angleDeg * Mathf.Deg2Rad;
@@ -563,6 +607,23 @@ namespace BaseGames.Editor
new GUIContent("矩形偏移", "相对于原点偏移,X 随朝向翻转"));
break;
case SlotType.BoxLOS:
EditorGUILayout.PropertyField(elem.FindPropertyRelative("detectLayer"),
new GUIContent("检测层", "目标所在层(通常为 Player 层)"));
EditorGUILayout.PropertyField(elem.FindPropertyRelative("boxSize"),
new GUIContent("矩形尺寸 (m)", "宽 × 高(横向视野设宽 > 高)"));
EditorGUILayout.PropertyField(elem.FindPropertyRelative("boxOffset"),
new GUIContent("矩形偏移", "相对于原点偏移,X 随朝向翻转;\n仅前向视野把 X 设为 boxSize.x*0.5"));
EditorGUILayout.Space(2);
EditorGUILayout.LabelField("─ LOS 遮挡设置 ─", EditorStyles.miniLabel);
EditorGUILayout.PropertyField(elem.FindPropertyRelative("losBlockMask"),
new GUIContent("遮挡层", "遮挡物所在层(Platform / Wall / Ground);设为 0 则视线始终通过(退化为纯几何 BoxCast)"));
EditorGUILayout.PropertyField(elem.FindPropertyRelative("losRayCount"),
new GUIContent("LOS 采样点数", "1=中心 3=中心+上+下(推荐) 5=中心+上下左右"));
EditorGUILayout.PropertyField(elem.FindPropertyRelative("losMinVisibility"),
new GUIContent("最低可见度 (01)", "0=任一采样点通过即可 1=全部通过(严格)"));
break;
case SlotType.Sight:
EditorGUILayout.PropertyField(elem.FindPropertyRelative("radius"),
new GUIContent("视野半径 (m)", "Sight 传感器检测半径"));