289 lines
9.0 KiB
C#
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);
|
|
}
|
|
|