111 lines
4.3 KiB
C#
111 lines
4.3 KiB
C#
// 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, // 按住交互键持续激活
|
||
}
|
||
|
||
/// <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 MMFeedbacks _activateFeedback;
|
||
|
||
[Header("持久化(SO 注入,非 Instance 单例)")]
|
||
[SerializeField] private WorldStateRegistry _worldState;
|
||
|
||
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 ? "按住交互" : "交互";
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|