188 lines
7.5 KiB
C#
188 lines
7.5 KiB
C#
using UnityEngine;
|
||
|
||
/// <summary>
|
||
/// Анимированный курсор: центральный кружок + пульсирующие кольца.
|
||
/// Free — зелёный, синхронная пульсация всех объектов.
|
||
/// Blocked — красный, сжатие.
|
||
/// Shot — жёлтая вспышка, дальнее кольцо расширяется сильнее.
|
||
/// </summary>
|
||
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<Renderer>();
|
||
}
|
||
|
||
if (rings == null || rings.Length == 0)
|
||
{
|
||
var found = new System.Collections.Generic.List<Renderer>();
|
||
for (int i = 0; i < transform.childCount; i++)
|
||
{
|
||
var ch = transform.GetChild(i);
|
||
if (ch.name.StartsWith("Ring"))
|
||
{
|
||
var r = ch.GetComponent<Renderer>();
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
// ────────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>Мгновенно выставить состояние (без плавного перехода). Вызывать при появлении курсора.</summary>
|
||
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; }
|
||
|
||
/// <summary>Жёлтая вспышка + расширение колец. willBeBlocked — состояние после анимации.</summary>
|
||
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);
|
||
}
|
||
}
|
||
}
|