实现移动平台乘客接口,优化乘客跟随逻辑
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -7,8 +8,16 @@ namespace BaseGames.World
|
||||
{
|
||||
/// <summary>
|
||||
/// 移动平台。Kinematic Rigidbody2D,支持三种移动模式。
|
||||
/// 乘客跟随:OnTriggerEnter2D 时 SetParent,离开时还原并附加速度。
|
||||
///
|
||||
/// 乘客跟随(Delta Position 方案,替代 SetParent):
|
||||
/// - PassengerSensor(Trigger)检测进入乘客(<see cref="IPassengerReceiver"/>)。
|
||||
/// - FixedUpdate(-300) 计算本帧期望位移 delta,推送给所有乘客,再执行 MovePosition。
|
||||
/// - 乘客在自己的 FixedUpdate(-200) 最前端消费 delta:_rb.position += delta。
|
||||
/// - 乘客离台时通过 OnLeavePlatform 继承平台速度,避免卡顿。
|
||||
///
|
||||
/// 此方案不修改 Transform 层级,与动画/摄像机/HitBox 系统完全兼容。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-300)]
|
||||
[RequireComponent(typeof(Rigidbody2D))]
|
||||
public class MovingPlatform : MonoBehaviour
|
||||
{
|
||||
@@ -24,15 +33,18 @@ namespace BaseGames.World
|
||||
[SerializeField] private VoidEventChannelSO _activationChannel;
|
||||
|
||||
[Header("乘客检测")]
|
||||
[SerializeField] private BoxCollider2D _passengerSensor; // IsTrigger,用于乘客 SetParent
|
||||
[SerializeField] private BoxCollider2D _passengerSensor; // isTrigger,检测乘客
|
||||
[SerializeField] private LayerMask _passengerLayer; // 应包含 Player + Enemy 层
|
||||
|
||||
private Rigidbody2D _rb;
|
||||
private List<Transform> _passengers = new();
|
||||
private int _waypointIndex;
|
||||
private bool _movingForward = true;
|
||||
private bool _triggered;
|
||||
private bool _waiting;
|
||||
private WaitForSeconds _waitForEndpoint;
|
||||
private Rigidbody2D _rb;
|
||||
private readonly List<IPassengerReceiver> _passengers = new();
|
||||
private readonly List<IPassengerReceiver> _passengerSnapshot = new(); // 迭代快照,防并发修改
|
||||
private int _waypointIndex;
|
||||
private bool _movingForward = true;
|
||||
private bool _triggered;
|
||||
private bool _waiting;
|
||||
private Vector2 _frameVelocity; // 本帧实际速度,供离台时继承
|
||||
private WaitForSeconds _waitForEndpoint;
|
||||
private readonly CompositeDisposable _subs = new();
|
||||
|
||||
private void Awake()
|
||||
@@ -55,17 +67,40 @@ namespace BaseGames.World
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (_wayPoints == null || _wayPoints.Length == 0) return;
|
||||
if (_moveType == MoveType.TriggeredLinear && !_triggered) return;
|
||||
if (_waiting) return;
|
||||
if (_wayPoints == null || _wayPoints.Length == 0)
|
||||
{
|
||||
_frameVelocity = Vector2.zero;
|
||||
return;
|
||||
}
|
||||
if (_moveType == MoveType.TriggeredLinear && !_triggered)
|
||||
{
|
||||
_frameVelocity = Vector2.zero;
|
||||
return;
|
||||
}
|
||||
if (_waiting)
|
||||
{
|
||||
_frameVelocity = Vector2.zero;
|
||||
return;
|
||||
}
|
||||
|
||||
MoveTowardsNextWaypoint();
|
||||
MoveAndBroadcast();
|
||||
}
|
||||
|
||||
private void MoveTowardsNextWaypoint()
|
||||
private void MoveAndBroadcast()
|
||||
{
|
||||
var target = (Vector2)_wayPoints[_waypointIndex].position;
|
||||
var next = Vector2.MoveTowards(_rb.position, target, _speed * Time.fixedDeltaTime);
|
||||
|
||||
// 计算期望 delta(在 MovePosition 执行前,不依赖物理步骤是否完成)
|
||||
Vector2 delta = next - _rb.position;
|
||||
_frameVelocity = delta / Time.fixedDeltaTime;
|
||||
|
||||
// 先广播 delta,乘客在下一个 FixedUpdate(-200) 消费
|
||||
_passengerSnapshot.Clear();
|
||||
_passengerSnapshot.AddRange(_passengers);
|
||||
foreach (var r in _passengerSnapshot)
|
||||
r.SetPlatformDelta(delta);
|
||||
|
||||
_rb.MovePosition(next);
|
||||
|
||||
if (Vector2.Distance(_rb.position, target) < 0.02f)
|
||||
@@ -103,32 +138,27 @@ namespace BaseGames.World
|
||||
|
||||
private void OnTriggered() => _triggered = true;
|
||||
|
||||
// ── Passenger Pattern ─────────────────────────────────────────────────
|
||||
// ── Passenger Detection ───────────────────────────────────────────────
|
||||
|
||||
private void OnTriggerEnter2D(Collider2D other)
|
||||
{
|
||||
if (!IsPassenger(other.gameObject)) return;
|
||||
other.transform.SetParent(transform);
|
||||
_passengers.Add(other.transform);
|
||||
if (!IsPassengerLayer(other)) return;
|
||||
if (!other.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
if (!_passengers.Contains(receiver))
|
||||
_passengers.Add(receiver);
|
||||
}
|
||||
|
||||
private void OnTriggerExit2D(Collider2D other)
|
||||
{
|
||||
if (!_passengers.Contains(other.transform)) return;
|
||||
if (!other.TryGetComponent<IPassengerReceiver>(out var receiver)) return;
|
||||
if (!_passengers.Remove(receiver)) return;
|
||||
|
||||
other.transform.SetParent(null);
|
||||
_passengers.Remove(other.transform);
|
||||
|
||||
// 离开时附加平台速度,避免卡顿
|
||||
var passengerRb = other.GetComponentInParent<Rigidbody2D>();
|
||||
if (passengerRb != null)
|
||||
passengerRb.AddForce(_rb.velocity, ForceMode2D.Impulse);
|
||||
// 继承平台速度,防止离台时速度骤变卡顿
|
||||
receiver.OnLeavePlatform(_frameVelocity);
|
||||
}
|
||||
|
||||
private static bool IsPassenger(GameObject go)
|
||||
{
|
||||
return go.CompareTag("Player") || go.CompareTag("Enemy");
|
||||
}
|
||||
private bool IsPassengerLayer(Collider2D col)
|
||||
=> (_passengerLayer.value & (1 << col.gameObject.layer)) != 0;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmos()
|
||||
@@ -144,3 +174,4 @@ namespace BaseGames.World
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user