Files
zeling_v2/Assets/_Game/Scripts/World/Puzzle/PuzzleSwitch.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

154 lines
6.1 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.
// Assets/Scripts/World/Puzzle/PuzzleSwitch.cs
using System;
using Animancer;
using BaseGames.Feedback;
using BaseGames.Input;
using BaseGames.World;
using UnityEngine;
namespace BaseGames.Puzzle
{
public enum SwitchTriggerMode
{
InteractOnce, // 玩家交互一次,永久激活
InteractToggle, // 玩家交互切换开关
Pressure, // 踩上激活,离开停用
Hold, // 按住交互键持续激活
}
/// <summary>
/// 通用谜题开关,支持三种触发模式。
/// 实现 ISwitchable + IInteractable玩家手动触发
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class PuzzleSwitch : MonoBehaviour, ISwitchable, IInteractable
{
[Header("触发模式")]
[SerializeField] private SwitchTriggerMode _mode = SwitchTriggerMode.InteractOnce;
[Header("状态")]
[SerializeField] private bool _startsActive = false;
[SerializeField] private string _switchId; // 持久化唯一 ID空串则不持久化
[Header("视觉")]
[SerializeField] private AnimancerComponent _animancer;
[SerializeField] private AnimationClip _activeClip;
[SerializeField] private AnimationClip _inactiveClip;
[SerializeField] private SceneFeedback _activateFeedback;
[Header("持久化SO 注入,非 Instance 单例)")]
[SerializeField] private WorldStateRegistry _worldState;
[Header("Hold 模式输入SwitchTriggerMode.Hold 时必填)")]
[SerializeField] private InputReaderSO _inputReader;
private bool _isActive;
public bool IsActive => _isActive;
public event Action<bool> OnStateChanged;
private void Awake()
{
bool savedState = !string.IsNullOrEmpty(_switchId)
&& _worldState != null
&& _worldState.HasFlag("switch_" + _switchId);
_isActive = savedState || _startsActive;
}
private void Start()
{
if (_isActive && _activeClip != null) _animancer?.Play(_activeClip);
else if (_inactiveClip != null) _animancer?.Play(_inactiveClip);
}
// ── IInteractable ────────────────────────────────────────────────────
public string InteractPrompt => _mode == SwitchTriggerMode.Hold ? "按住交互" : "交互";
/// <summary>
/// 压板模式不需要交互提示物理触发InteractOnce 已激活后隐藏提示。
/// </summary>
public bool CanInteract => _mode switch
{
SwitchTriggerMode.InteractOnce => !_isActive,
SwitchTriggerMode.InteractToggle => true,
SwitchTriggerMode.Hold => true,
SwitchTriggerMode.Pressure => false,
_ => false,
};
public void Interact(Transform player)
{
// Hold 模式通过 OnPlayerEnterRange 订阅输入事件处理,此处不响应
if (_mode == SwitchTriggerMode.Hold) return;
if (_mode == SwitchTriggerMode.InteractOnce && _isActive) return;
SetState(!_isActive);
}
public void OnPlayerEnterRange(Transform player)
{
if (_mode != SwitchTriggerMode.Hold || _inputReader == null) return;
_inputReader.InteractEvent += OnHoldStarted;
_inputReader.InteractCancelledEvent += OnHoldCancelled;
}
public void OnPlayerExitRange()
{
UnsubscribeHold();
// 玩家离开范围时停用 Hold 开关
if (_mode == SwitchTriggerMode.Hold && _isActive)
SetState(false);
}
// ── ISwitchable ──────────────────────────────────────────────────────
public void ForceState(bool active) => SetState(active);
// ── 压板模式 ──────────────────────────────────────────────────────────
private void OnTriggerEnter2D(Collider2D col)
{
if (_mode != SwitchTriggerMode.Pressure) return;
if (col.CompareTag("Player") || col.CompareTag("PushBox"))
SetState(true);
}
private void OnTriggerExit2D(Collider2D col)
{
if (_mode != SwitchTriggerMode.Pressure) return;
if (col.CompareTag("Player") || col.CompareTag("PushBox"))
SetState(false);
}
private void SetState(bool active)
{
if (_isActive == active) return;
_isActive = active;
if (active) _animancer?.Play(_activeClip);
else _animancer?.Play(_inactiveClip);
_activateFeedback?.Play();
OnStateChanged?.Invoke(active);
// 持久化到 WorldStateRegistry激活添加标记停用清除标记
if (!string.IsNullOrEmpty(_switchId))
{
if (active) _worldState?.SetFlag("switch_" + _switchId);
else _worldState?.ClearFlag("switch_" + _switchId);
}
}
// ── Hold 模式辅助 ──────────────────────────────────────────────────────
private void OnHoldStarted() => SetState(true);
private void OnHoldCancelled() => SetState(false);
private void UnsubscribeHold()
{
if (_inputReader == null) return;
_inputReader.InteractEvent -= OnHoldStarted;
_inputReader.InteractCancelledEvent -= OnHoldCancelled;
}
private void OnDestroy() => UnsubscribeHold();
}
}