sea-battle/Assets/Scripts/UI/Animation/UIAnimatedMover.cs

289 lines
9.0 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Òî÷êà ìàðøðóòà äëÿ UI-ýëåìåíòà
/// </summary>
[Serializable]
public class UIPathPoint
{
public Vector2 position;
public float duration = 1f;
public string animationStateName = ""; // Èìÿ ñîñòîÿíèÿ àíèìàöèè (walkk, stop, Idle, victory, defeat)
[Range(0f, 5f)]
public float delayAtPoint = 0f;
}
/// <summary>
/// Ìåíåäæåð äëÿ óïðàâëåíèÿ äâèæóùèìèñÿ UI-ïåðñîíàæàìè
/// Ñîçäà¸ò ýêçåìïëÿðû è çàïóñêàåò èõ ïî ìàðøðóòàì
/// </summary>
public class UIAnimatedMover : MonoBehaviour
{
[Header("Êîíôèãóðàöèè ïåðñîíàæåé")]
[SerializeField] private UICharacterPathConfig[] characterConfigs; // Ìàññèâ êîíôèãóðàöèé ðàçíûõ ïåðñîíàæåé
[Header("Íàñòðîéêè")]
[SerializeField] private Transform spawnParent; // Ðîäèòåëü äëÿ ñîçäàâàåìûõ ýêçåìïëÿðîâ
private List<UICharacterInstance> activeInstances = new List<UICharacterInstance>();
private void Awake()
{
if (spawnParent == null)
spawnParent = transform;
}
/// <summary>
/// Èíèöèàëèçèðîâàòü è çàïóñòèòü ïåðñîíàæà ïî ìàðøðóòó
/// </summary>
/// <param name="config">Êîíôèãóðàöèÿ ïåðñîíàæà ñ ìàðøðóòàìè</param>
/// <param name="pathType">Òèï ìàðøðóòà (Default ïî óìîë÷àíèþ)</param>
/// <returns>Ñîçäàííûé ýêçåìïëÿð ïåðñîíàæà</returns>
public UICharacterInstance InitializeCharacter(UICharacterPathConfig config, PathType pathType = PathType.Default)
{
if (config == null)
{
Debug.LogError("UIAnimatedMover: config is null!");
return null;
}
if (config.characterPrefab == null)
{
Debug.LogError($"UIAnimatedMover: characterPrefab is null in config {config.name}!");
return null;
}
// Ïîëó÷àåì ìàðøðóò ïî êëþ÷ó
PathData pathData = config.GetPath(pathType);
if (pathData == null || pathData.points.Count == 0)
{
Debug.LogError($"UIAnimatedMover: path not found for type {pathType} in config {config.name}!");
return null;
}
// Ñîçäà¸ì ýêçåìïëÿð ïåðñîíàæà
GameObject instanceObj = Instantiate(config.characterPrefab, spawnParent);
RectTransform rectTransform = instanceObj.GetComponent<RectTransform>();
if (rectTransform == null)
{
Debug.LogError($"UIAnimatedMover: characterPrefab must have RectTransform!");
Destroy(instanceObj);
return null;
}
// Ïîëó÷àåì èëè äîáàâëÿåì UIAnimationStateSwitcher
UIAnimationStateSwitcher animationSwitcher = instanceObj.GetComponent<UIAnimationStateSwitcher>();
if (animationSwitcher == null)
{
animationSwitcher = instanceObj.AddComponent<UIAnimationStateSwitcher>();
}
// Óñòàíàâëèâàåì Override Controller
if (config.overrideController != null)
{
animationSwitcher.SetOverrideController(config.overrideController);
}
// Óñòàíàâëèâàåì â ïåðâóþ òî÷êó ìàðøðóòà
rectTransform.anchoredPosition = pathData.points[0].position;
// Ñîçäà¸ì ýêçåìïëÿð óïðàâëåíèÿ
UICharacterInstance instance = new UICharacterInstance
{
gameObject = instanceObj,
rectTransform = rectTransform,
animationSwitcher = animationSwitcher,
pathData = pathData,
config = config
};
activeInstances.Add(instance);
// Çàïóñêàåì äâèæåíèå
StartCoroutine(MoveAlongPath(instance));
return instance;
}
/// <summary>
/// Èíèöèàëèçèðîâàòü ïåðñîíàæà ïî èíäåêñó èç ìàññèâà êîíôèãóðàöèé
/// </summary>
/// <param name="configIndex">Èíäåêñ êîíôèãóðàöèè â ìàññèâå (0, 1, 2...)</param>
/// <param name="pathType">Òèï ìàðøðóòà (Default ïî óìîë÷àíèþ)</param>
public UICharacterInstance InitializeByIndex(int configIndex, PathType pathType = PathType.Default)
{
if (characterConfigs == null || characterConfigs.Length == 0)
{
Debug.LogError("UIAnimatedMover: characterConfigs ìàññèâ ïóñò!");
return null;
}
if (configIndex < 0 || configIndex >= characterConfigs.Length)
{
Debug.LogError($"UIAnimatedMover: èíäåêñ {configIndex} âíå äèàïàçîíà ìàññèâà (0-{characterConfigs.Length - 1})");
return null;
}
return InitializeCharacter(characterConfigs[configIndex], pathType);
}
/// <summary>
/// Èíèöèàëèçèðîâàòü ïåðâîãî ïåðñîíàæà èç ìàññèâà (èíäåêñ 0)
/// </summary>
public UICharacterInstance InitializeDefault()
{
return InitializeByIndex(0, PathType.Default);
}
/// <summary>
/// Ïîëó÷èòü êîíôèãóðàöèþ ïåðñîíàæà ïî èíäåêñó
/// </summary>
public UICharacterPathConfig GetConfig(int index)
{
if (characterConfigs == null || index < 0 || index >= characterConfigs.Length)
return null;
return characterConfigs[index];
}
/// <summary>
/// Êîëè÷åñòâî äîñòóïíûõ êîíôèãóðàöèé ïåðñîíàæåé
/// </summary>
public int GetConfigCount()
{
return characterConfigs != null ? characterConfigs.Length : 0;
}
/// <summary>
/// Îñòàíîâèòü è óäàëèòü ýêçåìïëÿð ïåðñîíàæà
/// </summary>
public void RemoveCharacter(UICharacterInstance instance)
{
if (instance == null) return;
if (instance.moveCoroutine != null)
{
StopCoroutine(instance.moveCoroutine);
}
activeInstances.Remove(instance);
if (instance.gameObject != null)
{
Destroy(instance.gameObject);
}
}
/// <summary>
/// Óäàëèòü âñå àêòèâíûå ýêçåìïëÿðû
/// </summary>
public void RemoveAllCharacters()
{
for (int i = activeInstances.Count - 1; i >= 0; i--)
{
RemoveCharacter(activeInstances[i]);
}
activeInstances.Clear();
}
private IEnumerator MoveAlongPath(UICharacterInstance instance)
{
instance.isMoving = true;
PathData pathData = instance.pathData;
do
{
for (int i = 0; i < pathData.points.Count; i++)
{
UIPathPoint point = pathData.points[i];
// Ïåðåêëþ÷àåì àíèìàöèþ åñëè óêàçàíà
if (!string.IsNullOrEmpty(point.animationStateName) && instance.animationSwitcher != null)
{
instance.animationSwitcher.SetState(point.animationStateName);
}
// Äâèæåíèå ê òî÷êå (ïðîïóñêàåì ïåðâóþ òî÷êó åñëè ýòî íà÷àëî)
if (i > 0 || instance.hasStartedMoving)
{
yield return MoveToTargetPoint(instance, point);
}
else
{
instance.hasStartedMoving = true;
}
// Äîñòèãëè òî÷êè
instance.InvokeReachPoint(i);
// Çàäåðæêà â òî÷êå
if (point.delayAtPoint > 0)
yield return new WaitForSeconds(point.delayAtPoint);
}
} while (pathData.loop && instance.gameObject != null);
instance.isMoving = false;
instance.InvokePathComplete();
}
private IEnumerator MoveToTargetPoint(UICharacterInstance instance, UIPathPoint point)
{
Vector2 startPos = instance.rectTransform.anchoredPosition;
Vector2 targetPos = point.position;
float elapsed = 0f;
while (elapsed < point.duration)
{
if (instance.gameObject == null) yield break;
float t = elapsed / point.duration;
instance.rectTransform.anchoredPosition = Vector2.Lerp(startPos, targetPos, t);
elapsed += Time.deltaTime;
yield return null;
}
// Ôèíàëüíàÿ ïîçèöèÿ
if (instance.rectTransform != null)
{
instance.rectTransform.anchoredPosition = targetPos;
}
}
private void OnDestroy()
{
RemoveAllCharacters();
}
}
/// <summary>
/// Ýêçåìïëÿð äâèæóùåãîñÿ ïåðñîíàæà
/// </summary>
public class UICharacterInstance
{
public GameObject gameObject;
public RectTransform rectTransform;
public UIAnimationStateSwitcher animationSwitcher;
public PathData pathData;
public UICharacterPathConfig config;
public bool isMoving;
public bool hasStartedMoving;
public Coroutine moveCoroutine;
// Ñîáûòèÿ
public event Action OnPathComplete;
public event Action<int> OnReachPoint;
// Âíóòðåííèå ìåòîäû äëÿ âûçîâà ñîáûòèé
internal void InvokePathComplete() => OnPathComplete?.Invoke();
internal void InvokeReachPoint(int pointIndex) => OnReachPoint?.Invoke(pointIndex);
}