using UnityEngine; using Framework; public class FlashEffectStarter : MonoBehaviour { [Header("设置")] [Tooltip("是否在OnEnable时自动设置StartTime")] public bool autoSetOnEnable = true; [Tooltip("使用 MaterialPropertyBlock(推荐,支持GPU Instancing)还是Material实例")] public bool usePropertyBlock = true; [Tooltip("是否也处理子对象的Renderer")] public bool includeChildren = false; [Tooltip("是否响应战斗暂停状态")] public bool respondToFightPause = true; [Header("效果设置")] [Tooltip("延迟时间(秒),效果会在SetStartTime后延迟这个时间再开始播放")] public float delay = 0.1f; private static readonly int StartTimePropertyID = Shader.PropertyToID("_StartTime"); private static readonly int DelayPropertyID = Shader.PropertyToID("_Delay"); private static readonly int PausedPropertyID = Shader.PropertyToID("_Paused"); private static readonly int GameTimePropertyID = Shader.PropertyToID("_GameTime"); private static readonly int GameTimeInitializedPropertyID = Shader.PropertyToID("_GameTimeInitialized"); private static FlashEffectStarter _activeInstance; private static bool _isFightPaused = false; private static float _pausedGameTime = 0f; private MaterialPropertyBlock _propertyBlock; private Renderer[] _renderers; // 缓存委托,避免每次注册/注销时创建新委托产生 GC private System.Action _onFightPauseChangedDelegate; private System.Action _onFightPauseChangedNoParamDelegate; private void Awake() { if (usePropertyBlock) { _propertyBlock = new MaterialPropertyBlock(); } if (includeChildren) { _renderers = GetComponentsInChildren(true); } else { var renderer = GetComponent(); _renderers = renderer != null ? new[] { renderer } : new Renderer[0]; } // 初始化缓存的委托 _onFightPauseChangedDelegate = OnFightPauseChanged; _onFightPauseChangedNoParamDelegate = OnFightPauseChangedNoParam; } private void OnEnable() { if (autoSetOnEnable) { SetStartTime(); } // 设置延迟 SetDelay(delay); // 更新暂停状态 UpdatePausedState(); // 注册为活动实例,负责更新全局时间 if (_activeInstance == null) { _activeInstance = this; } // 注册战斗暂停事件监听 if (respondToFightPause) { EventManager.Instance.Register(EventManager.EventName.INFIGHT_PAUSE_CHANGED, _onFightPauseChangedDelegate); EventManager.Instance.Register(EventManager.EventName.INFIGHT_PAUSE_CHANGED, _onFightPauseChangedNoParamDelegate); } } private void OnDisable() { // 如果当前实例是活动实例,清除引用 if (_activeInstance == this) { _activeInstance = null; } // 注销战斗暂停事件监听 if (respondToFightPause) { EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_PAUSE_CHANGED, _onFightPauseChangedDelegate); EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_PAUSE_CHANGED, _onFightPauseChangedNoParamDelegate); } } private void Update() { // 更新暂停状态 UpdatePausedState(); // 只有活动实例才更新全局游戏时间,避免重复更新 if (_activeInstance == this) { // 检查暂停状态:优先使用事件状态,如果没有响应事件则检查 Time.timeScale bool isPaused; if (respondToFightPause) { isPaused = _isFightPaused; } else { // 如果不响应事件,直接检查 Time.timeScale bool currentPaused = Time.timeScale == 0f; if (currentPaused && !_isFightPaused) { // 刚刚进入暂停状态,记录当前时间 _pausedGameTime = Time.time; } _isFightPaused = currentPaused; isPaused = currentPaused; } // 如果战斗暂停,使用暂停时的时间,否则使用当前时间 float gameTime = isPaused ? _pausedGameTime : Time.time; Shader.SetGlobalFloat(GameTimePropertyID, gameTime); // 标记 _GameTime 已初始化 Shader.SetGlobalFloat(GameTimeInitializedPropertyID, 1.0f); } } /// /// 战斗暂停状态改变回调(带bool参数) /// private void OnFightPauseChanged(bool isPaused) { _isFightPaused = isPaused; if (isPaused) { // 暂停时记录当前时间 _pausedGameTime = Time.time; Debug.Log($"[FlashEffectStarter] 战斗暂停,记录时间: {_pausedGameTime}"); } else { Debug.Log($"[FlashEffectStarter] 战斗恢复"); } UpdatePausedState(); } /// /// 战斗暂停状态改变回调(无参数,通过其他方式判断暂停状态) /// private void OnFightPauseChangedNoParam() { // 如果没有参数,通过 Time.timeScale 来判断 bool wasPaused = _isFightPaused; _isFightPaused = Time.timeScale == 0f; if (_isFightPaused && !wasPaused) { // 刚刚进入暂停状态,记录当前时间 _pausedGameTime = Time.time; Debug.Log($"[FlashEffectStarter] 战斗暂停(无参数事件),记录时间: {_pausedGameTime}"); } else if (!_isFightPaused && wasPaused) { Debug.Log($"[FlashEffectStarter] 战斗恢复(无参数事件)"); } UpdatePausedState(); } public void SetStartTime() { SetStartTime(Time.time); } public void SetStartTime(float startTime) { foreach (var renderer in _renderers) { if (renderer == null) continue; if (usePropertyBlock) { renderer.GetPropertyBlock(_propertyBlock); _propertyBlock.SetFloat(StartTimePropertyID, startTime); renderer.SetPropertyBlock(_propertyBlock); } else { renderer.material.SetFloat(StartTimePropertyID, startTime); } } } public void ResetAnimation() { SetStartTime(0f); } public void RestartAnimation() { SetStartTime(Time.time); } /// /// 设置延迟时间 /// public void SetDelay(float delayValue) { delay = delayValue; foreach (var renderer in _renderers) { if (renderer == null) continue; if (usePropertyBlock) { renderer.GetPropertyBlock(_propertyBlock); _propertyBlock.SetFloat(DelayPropertyID, delayValue); renderer.SetPropertyBlock(_propertyBlock); } else { renderer.material.SetFloat(DelayPropertyID, delayValue); } } } /// /// 更新暂停状态到shader /// private void UpdatePausedState() { bool isPaused; if (respondToFightPause) { isPaused = _isFightPaused; } else { isPaused = Time.timeScale == 0f; } float pausedValue = isPaused ? 1.0f : 0.0f; foreach (var renderer in _renderers) { if (renderer == null) continue; if (usePropertyBlock) { renderer.GetPropertyBlock(_propertyBlock); _propertyBlock.SetFloat(PausedPropertyID, pausedValue); renderer.SetPropertyBlock(_propertyBlock); } else { renderer.material.SetFloat(PausedPropertyID, pausedValue); } } } #if UNITY_EDITOR [ContextMenu("设置StartTime为当前时间")] private void EditorSetStartTime() { if (Application.isPlaying) { SetStartTime(); Debug.Log($"已设置 {gameObject.name} 的StartTime为 {Time.time}"); } else { Debug.LogWarning("请在运行时执行此操作"); } } [ContextMenu("重置StartTime为0")] private void EditorResetStartTime() { if (Application.isPlaying) { ResetAnimation(); Debug.Log($"已重置 {gameObject.name} 的StartTime为0"); } else { Debug.LogWarning("请在运行时执行此操作"); } } #endif }