using System.Collections; using UnityEngine; using UnityEngine.Rendering; using BaseGames.Core.Events; namespace BaseGames.VFX { /// /// 后处理 Volume 分区管理器,挂在 Persistent 场景 [PostProcess] GameObject 上。 /// 通过 Coroutine 平滑 blend Weight,监听游戏状态/Boss/死亡/胜利事件。 /// 注意:水下后处理由 UnderwaterPostProcessingController(World.Liquid)独立负责,此组件不处理。 /// public class PostProcessManager : MonoBehaviour { [Header("Volume 引用(Persistent 场景内)")] [SerializeField] private Volume _bossArenaVolume; [SerializeField] private Volume _deathVolume; [SerializeField] private Volume _victoryVolume; [Header("事件频道")] [SerializeField] private VoidEventChannelSO _onBossFightStarted; [SerializeField] private VoidEventChannelSO _onBossFightEnded; [SerializeField] private VoidEventChannelSO _onPlayerDied; [SerializeField] private VoidEventChannelSO _onPlayerRespawned; [SerializeField] private VoidEventChannelSO _onBossDefeated; [SerializeField] private float _blendDuration = 0.4f; private Volume[] _managedVolumes; private Coroutine _blendCoroutine; /// 调用 BlendCoroutine / ResetAllCoroutine 时复用的起始权重缓存,避免每次分配新数组。 private float[] _startWeights; private readonly CompositeDisposable _subs = new(); private void Awake() { _managedVolumes = new[] { _bossArenaVolume, _deathVolume, _victoryVolume }; _startWeights = new float[_managedVolumes.Length]; } private void OnEnable() { _onBossFightStarted?.Subscribe(HandleBossFightStarted).AddTo(_subs); _onBossFightEnded?.Subscribe(HandleBossFightEnded).AddTo(_subs); _onPlayerDied?.Subscribe(HandlePlayerDied).AddTo(_subs); _onPlayerRespawned?.Subscribe(HandlePlayerRespawned).AddTo(_subs); _onBossDefeated?.Subscribe(HandleBossDefeated).AddTo(_subs); } private void OnDisable() { _subs.Clear(); } // ── 事件处理 ──────────────────────────────────────────────────────────── private void HandleBossFightStarted() => BlendTo(_bossArenaVolume); private void HandleBossFightEnded() => ResetAll(); private void HandlePlayerDied() => BlendTo(_deathVolume); private void HandlePlayerRespawned() => ResetAll(); private void HandleBossDefeated() => BlendTo(_victoryVolume); // ── 混合逻辑 ───────────────────────────────────────────────────────────── /// 将目标 Volume 权重渐变为 1,其他管理中的 Volume 渐变为 0。 private void BlendTo(Volume target) { if (_blendCoroutine != null) StopCoroutine(_blendCoroutine); _blendCoroutine = StartCoroutine(BlendCoroutine(target, 1f)); } /// 所有受管 Volume 权重渐变回 0(恢复默认后处理)。 private void ResetAll() { if (_blendCoroutine != null) StopCoroutine(_blendCoroutine); _blendCoroutine = StartCoroutine(ResetAllCoroutine()); } private IEnumerator BlendCoroutine(Volume target, float targetWeight) { float elapsed = 0f; for (int i = 0; i < _managedVolumes.Length; i++) _startWeights[i] = _managedVolumes[i] != null ? _managedVolumes[i].weight : 0f; while (elapsed < _blendDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / _blendDuration); for (int i = 0; i < _managedVolumes.Length; i++) { if (_managedVolumes[i] == null) continue; float dest = _managedVolumes[i] == target ? targetWeight : 0f; _managedVolumes[i].weight = Mathf.Lerp(_startWeights[i], dest, t); } yield return null; } // 确保精确收敛 for (int i = 0; i < _managedVolumes.Length; i++) { if (_managedVolumes[i] == null) continue; _managedVolumes[i].weight = _managedVolumes[i] == target ? targetWeight : 0f; } _blendCoroutine = null; } private IEnumerator ResetAllCoroutine() { float elapsed = 0f; for (int i = 0; i < _managedVolumes.Length; i++) _startWeights[i] = _managedVolumes[i] != null ? _managedVolumes[i].weight : 0f; while (elapsed < _blendDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / _blendDuration); for (int i = 0; i < _managedVolumes.Length; i++) { if (_managedVolumes[i] == null) continue; _managedVolumes[i].weight = Mathf.Lerp(_startWeights[i], 0f, t); } yield return null; } for (int i = 0; i < _managedVolumes.Length; i++) { if (_managedVolumes[i] != null) _managedVolumes[i].weight = 0f; } _blendCoroutine = null; } } }