NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/Render/FlashEffectStarter.cs

302 lines
9.0 KiB
C#
Raw Normal View History

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;
2026-01-20 16:58:26 +08:00
// 缓存委托,避免每次注册/注销时创建新委托产生 GC
private System.Action<bool> _onFightPauseChangedDelegate;
private System.Action _onFightPauseChangedNoParamDelegate;
private void Awake()
{
if (usePropertyBlock)
{
_propertyBlock = new MaterialPropertyBlock();
}
if (includeChildren)
{
_renderers = GetComponentsInChildren<Renderer>(true);
}
else
{
var renderer = GetComponent<Renderer>();
_renderers = renderer != null ? new[] { renderer } : new Renderer[0];
}
2026-01-20 16:58:26 +08:00
// 初始化缓存的委托
_onFightPauseChangedDelegate = OnFightPauseChanged;
_onFightPauseChangedNoParamDelegate = OnFightPauseChangedNoParam;
}
private void OnEnable()
{
if (autoSetOnEnable)
{
SetStartTime();
}
// 设置延迟
SetDelay(delay);
// 更新暂停状态
UpdatePausedState();
// 注册为活动实例,负责更新全局时间
if (_activeInstance == null)
{
_activeInstance = this;
}
// 注册战斗暂停事件监听
if (respondToFightPause)
{
2026-01-20 16:58:26 +08:00
EventManager.Instance.Register<bool>(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)
{
2026-01-20 16:58:26 +08:00
EventManager.Instance.Unregister<bool>(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);
}
}
/// <summary>
/// 战斗暂停状态改变回调带bool参数
/// </summary>
private void OnFightPauseChanged(bool isPaused)
{
_isFightPaused = isPaused;
if (isPaused)
{
// 暂停时记录当前时间
_pausedGameTime = Time.time;
Debug.Log($"[FlashEffectStarter] 战斗暂停,记录时间: {_pausedGameTime}");
}
else
{
Debug.Log($"[FlashEffectStarter] 战斗恢复");
}
UpdatePausedState();
}
/// <summary>
/// 战斗暂停状态改变回调(无参数,通过其他方式判断暂停状态)
/// </summary>
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);
}
/// <summary>
/// 设置延迟时间
/// </summary>
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);
}
}
}
/// <summary>
/// 更新暂停状态到shader
/// </summary>
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
}