using UnityEngine; namespace BaseGames.Player { /// /// 独立墙壁检测组件(架构 05_PlayerModule §13)。 /// ⚠️ 不嵌入 PlayerMovement,以保持单一职责。 /// 每侧发两根射线(Top + Bottom),两根均命中才视为接触墙壁(防卡角误判)。 /// WallSlideState / WallJumpState 通过 PlayerController.WallDetector 访问。 /// [RequireComponent(typeof(PlayerMovement))] public class PlayerWallDetector : MonoBehaviour { [SerializeField] private PlayerMovementConfigSO _config; [Header("墙壁 Layer(默认使用 \"Wall\" + \"Ground\")")] [SerializeField] private LayerMask _wallLayer; /// 当前是否正在触碰墙壁。 public bool IsTouchingWall { get; private set; } /// 触碰到的墙壁方向:+1 = 右墙,-1 = 左墙,0 = 无墙。 public int WallDirection { get; private set; } private void Awake() { Debug.Assert(_config != null, "[PlayerWallDetector] _config 未赋值,请在 Inspector 中指定 PlayerMovementConfigSO。", this); } private void FixedUpdate() { bool rightWall = CheckSide(Vector2.right); bool leftWall = CheckSide(Vector2.left); IsTouchingWall = rightWall || leftWall; WallDirection = rightWall ? 1 : (leftWall ? -1 : 0); } /// /// 每侧发两根射线(TopRay + BottomRay),两根均命中才返回 true。 /// private bool CheckSide(Vector2 dir) { Vector2 center = transform.position; float len = _config.WallRayLength; float oy = _config.WallRayOffsetY; int layer = _wallLayer != 0 ? (int)_wallLayer : LayerMask.GetMask("Wall", "Ground"); bool top = Physics2D.Raycast(center + Vector2.up * oy, dir, len, layer); bool bot = Physics2D.Raycast(center + Vector2.down * oy, dir, len, layer); return top && bot; } private void OnDrawGizmosSelected() { if (_config == null) return; float len = _config.WallRayLength; float oy = _config.WallRayOffsetY; Vector2 center = transform.position; Gizmos.color = IsTouchingWall ? Color.red : Color.cyan; // 右侧两根射线 Gizmos.DrawRay(center + Vector2.up * oy, Vector2.right * len); Gizmos.DrawRay(center + Vector2.down * oy, Vector2.right * len); // 左侧两根射线 Gizmos.DrawRay(center + Vector2.up * oy, Vector2.left * len); Gizmos.DrawRay(center + Vector2.down * oy, Vector2.left * len); } } }