// Assets/Scripts/World/Puzzle/PuzzleSwitch.cs using System; using Animancer; using BaseGames.World; using MoreMountains.Feedbacks; using UnityEngine; namespace BaseGames.Puzzle { public enum SwitchTriggerMode { InteractOnce, // 玩家交互一次,永久激活 InteractToggle, // 玩家交互切换开关 Pressure, // 踩上激活,离开停用 Hold, // 按住交互键持续激活 } /// /// 通用谜题开关,支持三种触发模式。 /// 实现 ISwitchable + IInteractable(玩家手动触发)。 /// [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 MMFeedbacks _activateFeedback; [Header("持久化(SO 注入,非 Instance 单例)")] [SerializeField] private WorldStateRegistry _worldState; private bool _isActive; public bool IsActive => _isActive; public event Action 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 ? "按住交互" : "交互"; public bool CanInteract => true; public void Interact(Transform player) { if (_mode == SwitchTriggerMode.InteractOnce && _isActive) return; SetState(!_isActive); } public void OnPlayerEnterRange(Transform player) { } public void OnPlayerExitRange() { } // ── 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?.PlayFeedbacks(); OnStateChanged?.Invoke(active); // 持久化到 WorldStateRegistry(激活添加标记;停用清除标记) if (!string.IsNullOrEmpty(_switchId)) { if (active) _worldState?.SetFlag("switch_" + _switchId); else _worldState?.ClearFlag("switch_" + _switchId); } } } }