Files
gold_dolphin/unity/Assets/UI/DamageFlashOverlay.cs
2026-07-07 03:34:56 +08:00

157 lines
5.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.UI;
using IndianOceanAssets.Engine2_5D;
using Architecture.Core;
namespace GameFramework
{
/// <summary>
/// 受击泛红特效 —— 玩家受伤时屏幕四周闪烁红色暗角。
///
/// 挂载到场景中任意物体上(或由 GameHUD 自动创建)。
/// 运行时自动创建全屏暗角遮罩,监听 HealthSystem.onPlayerDamaged 事件。
/// </summary>
public class DamageFlashOverlay : MonoBehaviour
{
[Header("特效参数")]
[Tooltip("受击时暗角最大透明度(0~1")]
[SerializeField] private float flashAlpha = 0.8f;
[Tooltip("闪烁淡出时间(秒)")]
[SerializeField] private float fadeDuration = 0.4f;
[Tooltip("暗角内圈半径(0~1,0=全屏幕红色,1=只有最边缘)")]
[SerializeField] private float innerRadius = 0.8f;
private Canvas _canvas;
private Image _overlayImage;
private float _currentAlpha;
private bool _isFlashing;
[Header("SO 事件通道(替代 static HealthSystem.onPlayerDamaged 事件)")]
[SerializeField] private GameEvent onPlayerDamagedEvent;
private void OnEnable()
{
if (onPlayerDamagedEvent == null)
Debug.LogWarning("[DamageFlashOverlay] onPlayerDamagedEvent 未接线!请在场景中预置已接线的 DamageFlashOverlay 实例,否则受击不闪红。", this);
else
onPlayerDamagedEvent.Register(OnPlayerDamaged);
}
private void OnDisable()
{
if (onPlayerDamagedEvent != null)
onPlayerDamagedEvent.Unregister(OnPlayerDamaged);
}
private void OnValidate()
{
if (onPlayerDamagedEvent == null)
Debug.LogWarning($"[DamageFlashOverlay] On Player Damaged Event 未接线({gameObject.name})。受击泛红不会出现。", this);
}
private void Start()
{
CreateOverlay();
}
private void Update()
{
if (!_isFlashing) return;
// 淡出
_currentAlpha -= Time.unscaledDeltaTime / fadeDuration;
if (_currentAlpha <= 0f)
{
_currentAlpha = 0f;
_isFlashing = false;
_overlayImage.gameObject.SetActive(false);
}
_overlayImage.color = new Color(1f, 0f, 0f, _currentAlpha);
}
private void OnPlayerDamaged()
{
// 重置闪烁
_currentAlpha = flashAlpha;
_isFlashing = true;
_overlayImage.gameObject.SetActive(true);
_overlayImage.color = new Color(1f, 0f, 0f, _currentAlpha);
}
/// <summary>
/// 创建全屏暗角遮罩(运行时生成暗角纹理)。
/// </summary>
private void CreateOverlay()
{
// 创建 Canvas
var canvasObj = new GameObject("DamageFlashCanvas");
_canvas = canvasObj.AddComponent<Canvas>();
_canvas.renderMode = RenderMode.ScreenSpaceOverlay;
_canvas.sortingOrder = 10000; // 在 HUD 之上
canvasObj.AddComponent<CanvasScaler>();
// 创建全屏 Image
var imageObj = new GameObject("DamageFlashImage");
imageObj.transform.SetParent(canvasObj.transform, false);
var rect = imageObj.AddComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.sizeDelta = Vector2.zero;
_overlayImage = imageObj.AddComponent<Image>();
_overlayImage.sprite = CreateVignetteSprite();
_overlayImage.type = Image.Type.Simple;
_overlayImage.raycastTarget = false; // 不阻挡点击
imageObj.SetActive(false); // 初始隐藏
}
/// <summary>
/// 运行时生成暗角纹理(中心透明,边缘红色)。
/// </summary>
private Sprite CreateVignetteSprite()
{
int size = 256;
var texture = new Texture2D(size, size, TextureFormat.RGBA32, false);
var pixels = new Color[size * size];
float center = size * 0.5f;
float maxDist = center; // 最大距离(角到中心)
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
// 计算到中心的归一化距离
float dx = (x - center) / center;
float dy = (y - center) / center;
float dist = Mathf.Sqrt(dx * dx + dy * dy);
// 暗角效果:内圈透明,外圈不透明
float alpha;
if (dist <= innerRadius)
{
alpha = 0f;
}
else
{
// 从内圈到边缘平滑过渡
float t = (dist - innerRadius) / (1.414f - innerRadius); // 1.414 = sqrt(2)
alpha = Mathf.SmoothStep(0f, 1f, t);
}
pixels[y * size + x] = new Color(1f, 0f, 0f, alpha);
}
}
texture.SetPixels(pixels);
texture.Apply();
texture.filterMode = FilterMode.Bilinear;
return Sprite.Create(texture, new Rect(0, 0, size, size), Vector2.one * 0.5f);
}
}
}