Danil Iarosh 92bd2d36b9 VFX Pool, new VFX, small fixes etc
- Исправил мелкие баки в поведенчеких скриптах
- Добавил CreatureAnimationEvents.cs для унификации поведения анимационных ивентов игровых сущностей
- Добавил VFX для смерти врагов
- Добавил пул VFX, пока много багов, UNSTABLE
- Настроил исользование пула в анимационных скриптах
2025-12-05 08:37:00 +03:00

162 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;
namespace RR.Game2D.Behaviour
{
/// <summary>
/// Пул объектов визуальных эффектов для переиспользования VFX и оптимизации производительности.
/// </summary>
public class VFXPool : MonoBehaviour
{
public static VFXPool Instance { get; private set; }
[SerializeField] private int poolSize = 32;
[SerializeField] private float defaultLifetime = 2f; // время жизни эффекта по умолчанию (сек)
private Queue<GameObject> _pool;
private void Awake()
{
// ---- Singleton ----
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
// -------------------
// Инициализируем очередь для хранения свободных VFX объектов
_pool = new Queue<GameObject>();
// Предварительно создаем VFX объекты для пула
for (int i = 0; i < poolSize; i++)
{
GameObject vfxGameObject = new GameObject($"VFX_{i}");
vfxGameObject.AddComponent<VisualEffect>();
// Созданные объекты сразу не активны в пуле
vfxGameObject.SetActive(false);
vfxGameObject.transform.SetParent(transform);
_pool.Enqueue(vfxGameObject);
}
}
private void OnDestroy()
{
// Если уничтожается текущий активный экземпляр, сбрасываем ссылку
if (Instance == this)
{
Instance = null;
}
}
/// <summary>
/// Получить VFX объект из пула (без автоматического запуска)
/// </summary>
/// <param name="vfxAsset">Ассет визуального эффекта для применения</param>
/// <returns>GameObject с компонентом VisualEffect или null, если не удалось получить</returns>
public GameObject Get(VisualEffectAsset vfxAsset)
{
GameObject vfxInstance;
// Проверяем, есть ли свободные объекты в пуле
if (_pool.Count > 0)
{
vfxInstance = _pool.Dequeue();
vfxInstance.SetActive(true);
VisualEffect currentVFX = vfxInstance.GetComponent<VisualEffect>();
// Если текущий ассет не совпадает с запрашиваемым, заменяем его и реинициализируем
if (currentVFX.visualEffectAsset != vfxAsset)
{
currentVFX.Stop();
currentVFX.visualEffectAsset = vfxAsset;
currentVFX.Reinit();
}
}
else
{
// Пул пуст, создаем новый VFX объект
vfxInstance = new GameObject($"VFX_{_pool.Count}");
VisualEffect vfx = vfxInstance.AddComponent<VisualEffect>();
vfx.visualEffectAsset = vfxAsset;
vfxInstance.transform.SetParent(transform);
vfxInstance.SetActive(true);
}
// Сбрасываем поворот
if (vfxInstance)
{
vfxInstance.transform.rotation = Quaternion.identity;
}
return vfxInstance;
}
/// <summary>
/// Воспроизвести VFX из пула и автоматически вернуть его позже.
/// </summary>
/// <param name="vfxAsset">Ассет визуального эффекта</param>
/// <param name="position">Позиция для эффекта</param>
/// <param name="lifetime">Время жизни эффекта в секундах (если null — будет использовано defaultLifetime)</param>
public GameObject Spawn(VisualEffectAsset vfxAsset, Vector3 position, float? lifetime = null)
{
float life = lifetime ?? defaultLifetime;
GameObject vfxInstance = Get(vfxAsset);
if (vfxInstance == null) return null;
vfxInstance.transform.position = position;
VisualEffect visualEffect = vfxInstance.GetComponent<VisualEffect>();
// Удостоверимся, что эффект корректно инициализирован и запущен
visualEffect.Reinit();
visualEffect.Play();
// Запускаем корутину, которая вернёт объект в пул после окончания времени жизни
StartCoroutine(ReturnAfterCoroutine(vfxInstance, life));
return vfxInstance;
}
private IEnumerator ReturnAfterCoroutine(GameObject vfxInstance, float seconds)
{
// Ждём указанное время. Можно заменить на более точный механизм при наличии событий от VFXGraph.
yield return new WaitForSeconds(seconds);
// Перед возвратом уверенно останавливаем эффект
var ve = vfxInstance.GetComponent<VisualEffect>();
if (ve) ve.Stop();
// Возвращаем в пул
Return(vfxInstance);
}
/// <summary>
/// Вернуть VFX объект обратно в пул для повторного использования
/// </summary>
/// <param name="vfxInstance">VFX объект для возврата в пул</param>
public void Return(GameObject vfxInstance)
{
if (vfxInstance == null) return;
VisualEffect visualEffect = vfxInstance.GetComponent<VisualEffect>();
if (visualEffect) visualEffect.Stop();
// Если пул не переполнен, деактивируем и кладём в очередь
if (_pool.Count < poolSize)
{
vfxInstance.SetActive(false);
_pool.Enqueue(vfxInstance);
}
else
{
// Пул переполнен, уничтожаем объект
Debug.Log("VFX Pool is full, destroying returned VFX instance.");
Destroy(vfxInstance);
}
}
}
}