114 lines
4.2 KiB
C#
114 lines
4.2 KiB
C#
using System.Collections.Generic;
|
||
using Sirenix.OdinInspector;
|
||
using SLSUtilities.LeanPoolAssistance;
|
||
using UnityEngine;
|
||
|
||
namespace Ichni
|
||
{
|
||
/// <summary>
|
||
/// 面向对象池特效的通用质量控制组件。
|
||
///
|
||
/// 挂载在由 LeanPool 生成与回收的 VFX 根节点上。每次 <see cref="OnSpawn"/> 时会先恢复受管理子对象的
|
||
/// Prefab 初始激活状态,再依据当前 <see cref="VfxQuality"/> 关闭非必要的视觉子树。
|
||
/// 设置切换仅影响之后新生成的 VFX,不会中断正在播放的效果。
|
||
/// </summary>
|
||
public class VFXObject : PooledObject
|
||
{
|
||
[Title("VFX Quality")]
|
||
[Tooltip("Medium 与 Low 都会关闭的次要视觉子树。应放入高频、长寿命或大面积覆盖的装饰层。")]
|
||
public List<GameObject> disableAtMediumAndLower = new List<GameObject>();
|
||
|
||
[Tooltip("仅 Low 会额外关闭的视觉子树。Medium 仍保留这些效果以维持基本演出层次。")]
|
||
public List<GameObject> disableAtLowOnly = new List<GameObject>();
|
||
|
||
// 仅缓存两个质量列表中出现过的对象。没有被列出的子对象永远不受本组件控制。
|
||
private readonly Dictionary<GameObject, bool> originalActiveStates = new Dictionary<GameObject, bool>();
|
||
private bool qualityObjectsCached;
|
||
|
||
private void Awake()
|
||
{
|
||
CacheOriginalActiveStates();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对象从 LeanPool 取出时执行一次质量裁剪。
|
||
/// 先恢复原始状态可避免同一个对象先以 Low 生成、回收后再以 High 生成时仍错误保持隐藏。
|
||
/// </summary>
|
||
public override void OnSpawn()
|
||
{
|
||
// LeanPool 的 Broadcast IPoolable 模式或错误的重复通知不应重新裁剪一个已播放的效果。
|
||
if (spawnExecuted)
|
||
{
|
||
return;
|
||
}
|
||
|
||
CacheOriginalActiveStates();
|
||
RestoreOriginalActiveStates();
|
||
ApplyCurrentQuality();
|
||
base.OnSpawn();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取生成瞬间的 VFX Quality 并关闭对应的完整视觉子树。
|
||
/// 未初始化 SettingsManager 的编辑器预览或独立加载流程默认使用 High,保持作者配置的完整视觉。
|
||
/// </summary>
|
||
private void ApplyCurrentQuality()
|
||
{
|
||
VfxQuality currentQuality = SettingsManager.instance?.settingsSaveData?.vfxQuality ?? VfxQuality.High;
|
||
|
||
if (currentQuality <= VfxQuality.Medium)
|
||
{
|
||
SetObjectsActive(disableAtMediumAndLower, false);
|
||
}
|
||
|
||
if (currentQuality == VfxQuality.Low)
|
||
{
|
||
SetObjectsActive(disableAtLowOnly, false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录 Quality 列表对象在 Prefab 中的初始激活状态。
|
||
/// 这样即使某个对象本来就由作者设为 inactive,也不会在下一次 Spawn 时被强制打开。
|
||
/// </summary>
|
||
private void CacheOriginalActiveStates()
|
||
{
|
||
if (qualityObjectsCached)
|
||
{
|
||
return;
|
||
}
|
||
|
||
CacheOriginalActiveStates(disableAtMediumAndLower);
|
||
CacheOriginalActiveStates(disableAtLowOnly);
|
||
qualityObjectsCached = true;
|
||
}
|
||
|
||
private void CacheOriginalActiveStates(List<GameObject> qualityObjects)
|
||
{
|
||
foreach (GameObject qualityObject in qualityObjects)
|
||
{
|
||
if (!originalActiveStates.ContainsKey(qualityObject))
|
||
{
|
||
originalActiveStates.Add(qualityObject, qualityObject.activeSelf);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void RestoreOriginalActiveStates()
|
||
{
|
||
foreach (KeyValuePair<GameObject, bool> originalState in originalActiveStates)
|
||
{
|
||
originalState.Key.SetActive(originalState.Value);
|
||
}
|
||
}
|
||
|
||
private static void SetObjectsActive(List<GameObject> qualityObjects, bool isActive)
|
||
{
|
||
foreach (GameObject qualityObject in qualityObjects)
|
||
{
|
||
qualityObject.SetActive(isActive);
|
||
}
|
||
}
|
||
}
|
||
}
|