302 lines
9.0 KiB
C#
302 lines
9.0 KiB
C#
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<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];
|
||
}
|
||
|
||
// 初始化缓存的委托
|
||
_onFightPauseChangedDelegate = OnFightPauseChanged;
|
||
_onFightPauseChangedNoParamDelegate = OnFightPauseChangedNoParam;
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (autoSetOnEnable)
|
||
{
|
||
SetStartTime();
|
||
}
|
||
|
||
// 设置延迟
|
||
SetDelay(delay);
|
||
|
||
// 更新暂停状态
|
||
UpdatePausedState();
|
||
|
||
// 注册为活动实例,负责更新全局时间
|
||
if (_activeInstance == null)
|
||
{
|
||
_activeInstance = this;
|
||
}
|
||
|
||
// 注册战斗暂停事件监听
|
||
if (respondToFightPause)
|
||
{
|
||
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)
|
||
{
|
||
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
|
||
}
|
||
|