Files
zeling_v2/Assets/_Game/Scripts/Enemies/IPathAgent.cs
T
joywayerandClaude Opus 4.8 31b77fa473 feat(enemy): Wander 巡逻改为当前段内随机 + 按身体宽度内缩两端
修复地面单位 Wander 巡逻"走一小段就停":
- 随机点从全图(GetRandomPointOnGraph)改为限定在 agent 当前所站的 NavSegment 上,
  不再跨越跳/落/拐角 NavLink,避免路径跨沟被崖边夹停或卡在过不去的 link。
- 段内随机 t 按"身体前缘安全余量(碰撞体半宽 + 悬崖检测前向偏移)"从两端各内缩,
  避免随机点落在角色宽度站不住的崖沿(WouldFallAhead 夹停、IsFollowingAPath 不复位→卡死)。
- NavAgent.SetRandomDestinationOnCurrentSegment(endMargin):段内采样+内缩(段过短退化到中点)
- EnemyMovement.EdgeSafeMargin:身体前缘安全余量
- IPathAgent.WalkToRandomOnSegment():EnemyNavAgent 传入 margin;飞行单位复用局部偏移;Null 返回 false

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 12:45:01 +08:00

108 lines
4.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using UnityEngine;
namespace BaseGames.Enemies
{
// ── 连接段类型(映射 PB2d LinkTypeName 字符串)───────────────────────
public enum NavLinkType
{
None,
Segment, // 普通地面/平台段
Jump,
Fall,
Corner,
Climb,
Elevator,
Teleport,
Custom
}
/// <summary>
/// 导航代理抽象接口(架构 07_EnemyModule §5)。
/// EnemyBase 和 BD Task 只依赖此接口,不依赖具体导航库。
/// 实现:EnemyNavAgentPathBerserker2d);测试用 NullPathAgent。
/// </summary>
public interface IPathAgent
{
// ── 核心移动 ────────────────────────────────────────────────────
/// <summary>请求移动到世界坐标 target。</summary>
void RequestMoveTo(Vector2 target);
/// <summary>立即停止导航(清除路径)。</summary>
void StopNavigation();
/// <summary>是否已到达目标(距离 ≤ stoppingDistance)。</summary>
bool IsAtDestination();
/// <summary>运行时覆盖移动速度。</summary>
void SetSpeed(float speed);
/// <summary>当前帧是否在移动(速度 > 0.01 且有有效路径)。</summary>
bool IsMoving { get; }
/// <summary>是否接近平台边缘(脚下或前方无地面时为 true)。</summary>
bool IsNearEdge();
// ── 连接段感知(NavLink 平台跳跃/爬梯/传送)─────────────────────
/// <summary>当前是否正在穿越连接段(跳跃/下落/爬梯等)。</summary>
bool IsOnLink { get; }
/// <summary>当前正在穿越的连接段类型;不在连接段时为 None。</summary>
NavLinkType CurrentLinkType { get; }
/// <summary>当前连接段的起始世界坐标;不在连接段时为 Vector2.zero。</summary>
Vector2 CurrentLinkStart { get; }
/// <summary>当前连接段的目标世界坐标;不在连接段时为 Vector2.zero。</summary>
Vector2 CurrentLinkEnd { get; }
/// <summary>检查目标是否可以从当前位置导航到达(同步轻量查询)。</summary>
bool CanReach(Vector2 target);
/// <summary>移动到随机可到达点(巡逻 fallback)。</summary>
bool WalkToRandom();
/// <summary>
/// 段内随机游走:随机点限制在 agent 当前所站的这一条导航段上,不跨越任何 NavLink(跳/落/拐角)。
/// 供 Wander 巡逻使用——地面单位只在当前平台段内来回,避免被崖边夹停或卡在过不去的 link 处。
/// 未映射到导航段(不在图上)时返回 false。
/// </summary>
bool WalkToRandomOnSegment();
// ── 连接段事件 ──────────────────────────────────────────────────
/// <summary>开始穿越连接段时触发(传入连接段类型)。</summary>
event Action<NavLinkType> OnLinkStarted;
/// <summary>连接段穿越完成时触发。</summary>
event Action<NavLinkType> OnLinkCompleted;
/// <summary>路径寻路失败事件(目标不可达时触发)。</summary>
event Action OnNavPathFailed;
/// <summary>到达目标事件(替代轮询 IsAtDestination)。</summary>
event Action OnGoalReached;
}
// ── 无导航 / 测试用空实现 ─────────────────────────────────────────────
public sealed class NullPathAgent : IPathAgent
{
public void RequestMoveTo(Vector2 _) { }
public void StopNavigation() { }
public bool IsAtDestination() => true;
public void SetSpeed(float _) { }
public bool IsMoving => false;
public bool IsNearEdge() => false;
public bool IsOnLink => false;
public NavLinkType CurrentLinkType => NavLinkType.None;
public Vector2 CurrentLinkStart => Vector2.zero;
public Vector2 CurrentLinkEnd => Vector2.zero;
public bool CanReach(Vector2 _) => false;
public bool WalkToRandom() => false;
public bool WalkToRandomOnSegment() => false;
public event Action<NavLinkType> OnLinkStarted { add { } remove { } }
public event Action<NavLinkType> OnLinkCompleted{ add { } remove { } }
public event Action OnNavPathFailed { add { } remove { } }
public event Action OnGoalReached { add { } remove { } }
}
}