using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// Точка маршрута для UI-элемента
///
[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;
}
///
/// Менеджер для управления движущимися UI-персонажами
/// Создаёт экземпляры и запускает их по маршрутам
///
public class UIAnimatedMover : MonoBehaviour
{
[Header("Конфигурации персонажей")]
[SerializeField] private UICharacterPathConfig[] characterConfigs; // Массив конфигураций разных персонажей
[Header("Настройки")]
[SerializeField] private Transform spawnParent; // Родитель для создаваемых экземпляров
private List activeInstances = new List();
private void Awake()
{
if (spawnParent == null)
spawnParent = transform;
}
///
/// Инициализировать и запустить персонажа по маршруту
///
/// Конфигурация персонажа с маршрутами
/// Тип маршрута (Default по умолчанию)
/// Созданный экземпляр персонажа
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();
if (rectTransform == null)
{
Debug.LogError($"UIAnimatedMover: characterPrefab must have RectTransform!");
Destroy(instanceObj);
return null;
}
// Получаем или добавляем UIAnimationStateSwitcher
UIAnimationStateSwitcher animationSwitcher = instanceObj.GetComponent();
if (animationSwitcher == null)
{
animationSwitcher = instanceObj.AddComponent();
}
// Устанавливаем 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;
}
///
/// Инициализировать персонажа по индексу из массива конфигураций
///
/// Индекс конфигурации в массиве (0, 1, 2...)
/// Тип маршрута (Default по умолчанию)
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);
}
///
/// Инициализировать первого персонажа из массива (индекс 0)
///
public UICharacterInstance InitializeDefault()
{
return InitializeByIndex(0, PathType.Default);
}
///
/// Получить конфигурацию персонажа по индексу
///
public UICharacterPathConfig GetConfig(int index)
{
if (characterConfigs == null || index < 0 || index >= characterConfigs.Length)
return null;
return characterConfigs[index];
}
///
/// Количество доступных конфигураций персонажей
///
public int GetConfigCount()
{
return characterConfigs != null ? characterConfigs.Length : 0;
}
///
/// Остановить и удалить экземпляр персонажа
///
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);
}
}
///
/// Удалить все активные экземпляры
///
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();
}
}
///
/// Экземпляр движущегося персонажа
///
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 OnReachPoint;
// Внутренние методы для вызова событий
internal void InvokePathComplete() => OnPathComplete?.Invoke();
internal void InvokeReachPoint(int pointIndex) => OnReachPoint?.Invoke(pointIndex);
}