using UnityEngine;
///
/// Анимированный курсор: центральный кружок + пульсирующие кольца.
/// Free — зелёный, синхронная пульсация всех объектов.
/// Blocked — красный, сжатие.
/// Shot — жёлтая вспышка, дальнее кольцо расширяется сильнее.
///
public class CursorAnimator : MonoBehaviour
{
[Header("Дочерние объекты")]
public Renderer dot;
public Renderer[] rings;
[Header("Цвета")]
public Color freeColor = new Color(0.1f, 1f, 0.1f, 0.85f);
public Color blockedColor = new Color(1f, 0.1f, 0.1f, 0.85f);
public Color shotColor = new Color(1f, 0.9f, 0.0f, 1.00f);
[Header("Пульсация (Free)")]
[Tooltip("Синхронная пульсация всех объектов / со сдвигом фазы между кольцами")]
public bool syncPulse = false;
[Tooltip("Сдвиг фазы между кольцами (сек) при syncPulse = false")]
public float phaseOffset = 0.18f;
public float dotMinScale = 0.7f;
public float dotMaxScale = 1.0f;
public float ringMinScale = 0.5f;
public float ringMaxScale = 1.1f;
public float pulseSpeed = 1.4f;
[Header("Blocked: сжатие")]
public float blockedDotScale = 0.35f;
public float blockedRingScale = 0.2f;
[Header("Shot: расширение")]
[Tooltip("Во сколько раз кольцо 0 расширяется при выстреле")]
public float shotRingBaseExpand = 1.5f;
[Tooltip("Доп. множитель на каждое следующее кольцо")]
public float shotRingExpandStep = 0.5f;
[Tooltip("Длительность анимации выстрела (сек)")]
public float shotDuration = 0.4f;
[Header("Переход между состояниями")]
public float transitionSpeed = 8f;
// ── внутреннее ─────────────────────────────────────────────────────────
private enum CursorState { Free, Blocked }
private CursorState curState = CursorState.Free;
private float blendT;
private float shotT;
private bool shotActive;
private Vector3 dotBaseScale;
private Vector3[] ringBaseScales;
private static readonly int ColorProp = Shader.PropertyToID("_Color");
// ────────────────────────────────────────────────────────────────────────
void Awake()
{
if (dot == null)
{
var t = transform.Find("Dot");
if (t != null) dot = t.GetComponent();
}
if (rings == null || rings.Length == 0)
{
var found = new System.Collections.Generic.List();
for (int i = 0; i < transform.childCount; i++)
{
var ch = transform.GetChild(i);
if (ch.name.StartsWith("Ring"))
{
var r = ch.GetComponent();
if (r != null) found.Add(r);
}
}
rings = found.ToArray();
}
dotBaseScale = dot != null ? dot.transform.localScale : Vector3.one;
ringBaseScales = new Vector3[rings != null ? rings.Length : 0];
if (rings != null)
for (int i = 0; i < rings.Length; i++)
ringBaseScales[i] = rings[i] != null ? rings[i].transform.localScale : Vector3.one;
blendT = 1f;
}
void Update()
{
// прогресс анимации выстрела
if (shotActive)
{
shotT += Time.deltaTime / shotDuration;
if (shotT >= 1f) { shotT = 1f; shotActive = false; }
}
// blendT: 1 = Free, 0 = Blocked
float blendTarget = (curState == CursorState.Free) ? 1f : 0f;
blendT = Mathf.MoveTowards(blendT, blendTarget, transitionSpeed * Time.deltaTime);
// фаза пульсации
float basePhase = Time.time * pulseSpeed * Mathf.PI * 2f;
float shotArch = Mathf.Sin(shotT * Mathf.PI);
Color baseColor = Color.Lerp(blockedColor, freeColor, blendT);
Color curColor = Color.Lerp(baseColor, shotColor, shotArch);
// dot
if (dot != null)
{
float sinVal = (Mathf.Sin(basePhase) + 1f) * 0.5f;
float freeS = Mathf.Lerp(dotMinScale, dotMaxScale, sinVal);
float finalS = Mathf.Lerp(blockedDotScale, freeS, blendT);
dot.transform.localScale = dotBaseScale * finalS;
SetRendererColor(dot, curColor);
}
// rings
if (rings != null)
{
for (int i = 0; i < rings.Length; i++)
{
if (rings[i] == null) continue;
float phase = syncPulse ? basePhase : basePhase + i * phaseOffset * Mathf.PI * 2f;
float sinVal = (Mathf.Sin(phase) + 1f) * 0.5f;
float freeS = Mathf.Lerp(ringMinScale, ringMaxScale, sinVal);
float baseS = Mathf.Lerp(blockedRingScale, freeS, blendT);
float expandFactor = 1f + (shotRingBaseExpand - 1f + i * shotRingExpandStep) * shotArch;
rings[i].transform.localScale = ringBaseScales[i] * (baseS * expandFactor);
Color rc = curColor;
rc.a *= Mathf.Lerp(0.25f, 0.75f, blendT);
SetRendererColor(rings[i], rc);
}
}
}
// ────────────────────────────────────────────────────────────────────────
/// Мгновенно выставить состояние (без плавного перехода). Вызывать при появлении курсора.
public void RefreshState(bool cellBlocked)
{
curState = cellBlocked ? CursorState.Blocked : CursorState.Free;
blendT = cellBlocked ? 0f : 1f;
shotT = 0f;
shotActive = false;
}
public void SetStateFree() { curState = CursorState.Free; }
public void SetStateBlocked() { curState = CursorState.Blocked; }
/// Жёлтая вспышка + расширение колец. willBeBlocked — состояние после анимации.
public void PlayShot(bool willBeBlocked)
{
curState = willBeBlocked ? CursorState.Blocked : CursorState.Free;
shotT = 0f;
shotActive = true;
}
public void SetColors(Color free, Color blocked)
{
freeColor = free;
blockedColor = blocked;
}
// ────────────────────────────────────────────────────────────────────────
private static void SetRendererColor(Renderer r, Color c)
{
if (r is SpriteRenderer sr)
{
sr.color = c;
}
else
{
var block = new MaterialPropertyBlock();
r.GetPropertyBlock(block);
block.SetColor(ColorProp, c);
r.SetPropertyBlock(block);
}
}
}