源码 / Ch27/PostFxController.cs
Ch27/PostFxController.cs
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace Cookbook.Ch27 { /// 案例 27.4:运行时用脚本驱动后处理 —— 受击反馈与低血量去色。 /// 挂在一个带 Volume 组件的物体上(建议单独一个高优先级的全局 Volume,专门给游戏逻辑用)。 [RequireComponent(typeof(Volume))] public class PostFxController : MonoBehaviour { [Range(0, 1)] public float health = 1f; public float hitDecay = 3f; // 受击效果每秒衰减多少 public Color hitColor = new Color(0.6f, 0f, 0f); Volume m_Volume; Vignette m_Vignette; ChromaticAberration m_CA; ColorAdjustments m_Color; float m_Hit; void OnEnable() { m_Volume = GetComponent<Volume>(); // profile(而不是 sharedProfile)会在第一次访问时克隆一份运行时实例,改它不会污染磁盘上的资源 var profile = m_Volume.profile; m_Vignette = GetOrAdd<Vignette>(profile); m_CA = GetOrAdd<ChromaticAberration>(profile); m_Color = GetOrAdd<ColorAdjustments>(profile); } static T GetOrAdd<T>(VolumeProfile profile) where T : VolumeComponent { if (!profile.TryGet(out T comp)) comp = profile.Add<T>(); comp.active = true; return comp; } public void OnHit(float strength = 1f) => m_Hit = Mathf.Clamp01(m_Hit + strength); void Update() { m_Hit = Mathf.Max(0f, m_Hit - hitDecay * Time.deltaTime); // Override(x) 同时做两件事:overrideState = true,value = x。 // 只改 .value 而 overrideState 为 false 时,这个参数不参与混合,改了等于没改。 m_Vignette.color.Override(hitColor); m_Vignette.intensity.Override(Mathf.Lerp(0.2f, 0.55f, m_Hit)); m_Vignette.smoothness.Override(0.6f); m_CA.intensity.Override(m_Hit * 0.8f); // 低血量:血量低于 30% 后逐渐去色 float lowHp = Mathf.InverseLerp(0.3f, 0f, health); m_Color.saturation.Override(Mathf.Lerp(0f, -80f, lowHp)); } /// 读取“混合之后”的最终值(所有 Volume 按权重、优先级叠加的结果)。 /// 注意 VolumeManager.instance.stack 是默认栈,由当前渲染的相机在每帧更新。 public static float CurrentBloomIntensity() { var bloom = VolumeManager.instance.stack.GetComponent<Bloom>(); return bloom != null && bloom.IsActive() ? bloom.intensity.value : 0f; } void OnDisable() { // 用的是 profile 克隆出来的运行时实例,销毁时一并清理 if (m_Volume != null && m_Volume.HasInstantiatedProfile()) Destroy(m_Volume.profile); } } }