Files
zeling_v2/Assets/_Game/Scripts/World/DirectionalInteractable.cs
Joywayer 68d4c699ae 修复内容:
PlayerMovement:新增 _facingLocked 字段 + LockFacing(bool) 方法;UpdateFacing() 锁定时直接返回
WallSlideState:OnStateEnter 调用 LockFacing(true) + FlipFacing(_wallDir);OnStateExit 调用 LockFacing(false) 解锁
WallJumpState:OnStateEnter 保险性再调一次 LockFacing(false);WallJumpAway/Toward 同步写入 _inputVelocityX,确保解锁后 UpdateFacing 朝向正确(背墙跳 = 离墙方向,对墙跳 = 朝墙方向)
2026-05-22 10:48:52 +08:00

126 lines
5.0 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 BaseGames.Core.Events;
using BaseGames.Feedback;
using UnityEngine;
namespace BaseGames.World
{
/// <summary>
/// 单向触发机关。支持玩家按键/玩家接触/攻击三种触发方式。
/// 通过 VoidEventChannelSO 零耦合激活/停用目标(门/升降台/灯光等)。
/// _isOneShot = true 时激活状态持久化到 WorldStateRegistry。
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class DirectionalInteractable : MonoBehaviour, IInteractable
{
public enum TriggerSide { Left, Right, Top, Any }
public enum TriggerCondition { PlayerAttack, PlayerBody, InteractKey }
[Header("触发条件")]
[SerializeField] private TriggerSide _triggerSide = TriggerSide.Any;
[SerializeField] private TriggerCondition _triggerCondition = TriggerCondition.InteractKey;
[Header("行为")]
[SerializeField] private bool _isOneShot;
[SerializeField] private string _interactableId;
[Header("事件频道")]
[SerializeField] private VoidEventChannelSO _activationChannel;
[SerializeField] private VoidEventChannelSO _deactivationChannel;
[Header("反馈")]
[SerializeField] private SceneFeedback _activateFeedback;
[Header("持久化")]
[SerializeField] private WorldStateRegistry _worldState;
private bool _activated;
// ── IInteractable ─────────────────────────────────────────────────────
public bool CanInteract => !(_isOneShot && _activated);
public string InteractPrompt => _activated ? "已激活" : "交互";
public void Interact(Transform player)
{
if (_triggerCondition != TriggerCondition.InteractKey) return;
if (!CheckSide(player.position)) return;
TryActivate();
}
public void OnPlayerEnterRange(Transform player) { }
public void OnPlayerExitRange() { }
// ── Physics Triggers ──────────────────────────────────────────────────
/// <summary>
/// 判断碰撞体是否为有效触发来源。子类可覆写以扩展触发主体(如幻影身体)。
/// </summary>
protected virtual bool IsValidTriggerBody(Collider2D col) => col.CompareTag("Player");
private void OnTriggerEnter2D(Collider2D other)
{
if (_triggerCondition != TriggerCondition.PlayerBody) return;
if (!IsValidTriggerBody(other)) return;
if (!CheckSide(other.transform.position)) return;
TryActivate();
}
private void OnTriggerExit2D(Collider2D other)
{
if (_triggerCondition != TriggerCondition.PlayerBody) return;
if (!IsValidTriggerBody(other) || _isOneShot) return;
_activated = false;
_deactivationChannel?.Raise();
}
// ── Damage-based Trigger由 HurtBox/TileDamageReceiver 调用)────────
public void TryInteractFromDamage(BaseGames.Combat.DamageInfo info)
{
if (_triggerCondition != TriggerCondition.PlayerAttack) return;
if (!CheckSide(info.SourcePosition)) return;
TryActivate();
}
// ── Core ──────────────────────────────────────────────────────────────
protected void TryActivate()
{
if (_isOneShot && _activated) return;
_activated = true;
_activateFeedback?.Play();
_activationChannel?.Raise();
if (_isOneShot && !string.IsNullOrEmpty(_interactableId))
_worldState?.SetFlag("mechanism_" + _interactableId);
}
private bool CheckSide(Vector2 sourcePos)
{
if (_triggerSide == TriggerSide.Any) return true;
var dir = (sourcePos - (Vector2)transform.position).normalized;
return _triggerSide switch
{
TriggerSide.Left => dir.x < -0.4f,
TriggerSide.Right => dir.x > 0.4f,
TriggerSide.Top => dir.y > 0.4f,
_ => true
};
}
private void Start()
{
// 读档恢复:仅标记 _activated = true不广播激活事件。
// 下游组件PuzzleReceiver / 动画门等)已通过各自的 WorldStateRegistry 检查在 Start 中自行恢复,
// 重复广播会导致它们再次播放开门动画等副作用。
if (_isOneShot && !string.IsNullOrEmpty(_interactableId)
&& _worldState != null
&& _worldState.HasFlag("mechanism_" + _interactableId))
{
_activated = true;
}
}
}
}