using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;
namespace AibisDream.FixSystem
{
///
/// 模块运行状态的纯表现层:Module Pic 始终显示,文字负责常亮/闪烁/熄灭,
/// 火花依据 Module Pic 的世界包围盒收束在底边,并渲染在本体下层。
///
public class ModuleConditionVisual : MonoBehaviour
{
private static readonly Vector2[] SparkDirections =
{
new(-0.5f, -0.86f),
new(0f, -0.86f),
new(0.5f, -0.86f)
};
[Header("模块引用")]
[SerializeField] private SpriteRenderer modulePicRenderer;
[SerializeField] private SpriteRenderer moduleTextRenderer;
[Header("损坏文字闪烁")]
[SerializeField, Min(0.01f)] private float flickerMinInterval = 0.06f;
[SerializeField, Min(0.01f)] private float flickerMaxInterval = 0.18f;
[Header("损坏火花")]
[SerializeField] private VisualEffectAsset sparkAsset;
[SerializeField, Min(0.01f)] private float sparkMinInterval = 0.3f;
[SerializeField, Min(0.01f)] private float sparkMaxInterval = 1f;
[SerializeField, Min(0f)] private float sparkBoundsPadding = 0.01f;
[SerializeField] private Vector2 sparkBoundsScale = new(0.9f, 0.92f);
[SerializeField] private Vector2 sparkCenterOffset = Vector2.zero;
[SerializeField] private Vector2 sparkBurstCount = new(3f, 6f);
[SerializeField] private Vector2 sparkLifetime = new(0.1f, 0.24f);
[SerializeField] private Vector2 sparkSizeFactor = new(0.032f, 0.065f);
[SerializeField, Min(0.01f)] private float sparkWidth = 0.45f;
[SerializeField, Min(0f)] private float sparkVelocityFactor = 0.9f;
private readonly List _sparkEmitters = new();
private Coroutine _textFlickerCoroutine;
private Coroutine _sparkCoroutine;
private Color _originalTextColor = Color.white;
private ModuleCondition _condition = ModuleCondition.Normal;
private bool _initialized;
private bool _hasAppliedCondition;
public bool IsConfigured => modulePicRenderer != null && moduleTextRenderer != null;
private void Awake()
{
Initialize();
}
private void OnEnable()
{
if (_initialized && _hasAppliedCondition)
ApplyConditionInternal(_condition, true);
}
private void OnDisable()
{
StopRunningEffects();
}
public void ApplyCondition(ModuleCondition condition)
{
Initialize();
ApplyConditionInternal(condition, false);
}
private void Initialize()
{
if (_initialized) return;
_initialized = true;
if (moduleTextRenderer != null)
_originalTextColor = moduleTextRenderer.color;
CreateSparkEmitters();
}
private void ApplyConditionInternal(ModuleCondition condition, bool force)
{
if (!force && _hasAppliedCondition && _condition == condition)
return;
_condition = condition;
_hasAppliedCondition = true;
StopRunningEffects();
if (!IsConfigured)
return;
switch (condition)
{
case ModuleCondition.Normal:
SetTextVisible(true);
break;
case ModuleCondition.Damaged:
SetTextVisible(true);
if (isActiveAndEnabled)
{
_textFlickerCoroutine = StartCoroutine(FlickerText());
if (_sparkEmitters.Count > 0)
_sparkCoroutine = StartCoroutine(PlayIntermittentSparks());
}
break;
case ModuleCondition.PoweredOff:
SetTextVisible(false);
break;
}
}
private void StopRunningEffects()
{
if (_textFlickerCoroutine != null)
{
StopCoroutine(_textFlickerCoroutine);
_textFlickerCoroutine = null;
}
if (_sparkCoroutine != null)
{
StopCoroutine(_sparkCoroutine);
_sparkCoroutine = null;
}
foreach (var effect in _sparkEmitters)
{
if (effect == null) continue;
effect.Stop();
effect.gameObject.SetActive(false);
}
}
private IEnumerator FlickerText()
{
while (_condition == ModuleCondition.Damaged)
{
SetTextVisible(Random.value > 0.4f);
yield return new WaitForSeconds(Random.Range(
Mathf.Min(flickerMinInterval, flickerMaxInterval),
Mathf.Max(flickerMinInterval, flickerMaxInterval)));
}
}
private IEnumerator PlayIntermittentSparks()
{
yield return new WaitForSeconds(Random.Range(0.05f, 0.25f));
while (_condition == ModuleCondition.Damaged)
{
PlaySparkBurst();
yield return new WaitForSeconds(Random.Range(
Mathf.Min(sparkMinInterval, sparkMaxInterval),
Mathf.Max(sparkMinInterval, sparkMaxInterval)));
}
}
private void PlaySparkBurst()
{
if (_sparkEmitters.Count == 0) return;
var effect = _sparkEmitters[Random.Range(0, _sparkEmitters.Count)];
if (effect == null) return;
effect.gameObject.SetActive(true);
effect.Reinit();
effect.Play();
}
private void SetTextVisible(bool visible)
{
if (moduleTextRenderer == null) return;
moduleTextRenderer.color = _originalTextColor;
moduleTextRenderer.enabled = visible;
}
private void CreateSparkEmitters()
{
if (sparkAsset == null || modulePicRenderer == null)
return;
var bounds = modulePicRenderer.bounds;
var scaledExtents = new Vector2(
bounds.extents.x * Mathf.Max(0.01f, sparkBoundsScale.x),
bounds.extents.y * Mathf.Max(0.01f, sparkBoundsScale.y));
var sizeBase = Mathf.Max(0.01f, Mathf.Min(bounds.size.x, bounds.size.y));
foreach (var rawDirection in SparkDirections)
{
var direction = rawDirection.normalized;
var worldPosition = (Vector2)bounds.center + sparkCenterOffset + new Vector2(
rawDirection.x * scaledExtents.x,
rawDirection.y * scaledExtents.y) + direction * sparkBoundsPadding;
var emitterObject = new GameObject($"Damage Spark {rawDirection.x:0},{rawDirection.y:0}");
emitterObject.transform.SetParent(transform, false);
emitterObject.transform.localPosition = transform.InverseTransformPoint(worldPosition);
var effect = emitterObject.AddComponent();
effect.visualEffectAsset = sparkAsset;
ConfigureSparkEffect(effect, direction, sizeBase);
var renderer = emitterObject.GetComponent();
if (renderer != null)
{
renderer.sortingLayerID = modulePicRenderer.sortingLayerID;
renderer.sortingOrder = modulePicRenderer.sortingOrder - 1;
}
effect.Stop();
emitterObject.SetActive(false);
_sparkEmitters.Add(effect);
}
}
private void ConfigureSparkEffect(VisualEffect effect, Vector2 direction, float sizeBase)
{
if (effect.HasFloat("Spawn Rate"))
effect.SetFloat("Spawn Rate", 0f);
if (effect.HasVector2("Burst Count Min/Max"))
effect.SetVector2("Burst Count Min/Max", sparkBurstCount);
if (effect.HasVector2("Lifetime Min/Max"))
effect.SetVector2("Lifetime Min/Max", sparkLifetime);
if (effect.HasVector2("Size Min/Max"))
effect.SetVector2("Size Min/Max", sparkSizeFactor * sizeBase);
if (effect.HasFloat("Sparkle Width"))
effect.SetFloat("Sparkle Width", sparkWidth);
if (effect.HasVector3("Initial Velocity_vector"))
{
var speed = sizeBase * sparkVelocityFactor;
effect.SetVector3("Initial Velocity_vector", new Vector3(direction.x, direction.y, 0f) * speed);
}
}
}
}