139 lines
4.7 KiB
C#
139 lines
4.7 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using IndianOceanAssets.Engine2_5D;
|
||
|
||
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;
|
||
|
||
private void Start()
|
||
{
|
||
CreateOverlay();
|
||
HealthSystem.onPlayerDamaged += OnPlayerDamaged;
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
HealthSystem.onPlayerDamaged -= OnPlayerDamaged;
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
}
|