Danil Iarosh 7f14981d8b added dialogue system
- Добавил систему диалогов
- Создал классы Sequencer.cs и SequenceAction.cs для воссоздания последовательности действий
- Добавил иеханику последовательности действий в Message.cs и MessageController.cs
2025-12-10 08:32:15 +03:00

195 lines
8.2 KiB
C#
Raw Permalink 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;
using RR.Common.Utility;
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; // время жизни эффекта по умолчанию (сек)
[SerializeField] private SortingLayerPicker defaultSortingLayer;
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>();
// Устанавливаем Sorting Layer для рендерера VFX
ApplySortingLayer(vfxGameObject);
// Созданные объекты сразу не активны в пуле
vfxGameObject.SetActive(false);
vfxGameObject.transform.SetParent(transform);
_pool.Enqueue(vfxGameObject);
}
}
private void OnDestroy()
{
// Если уничтожается текущий активный экземпляр, сбрасываем ссылку
if (Instance == this)
{
Instance = null;
}
}
/// <summary>
/// Применяет Sorting Layer к рендереру VFX объекта
/// </summary>
private void ApplySortingLayer(GameObject vfxGameObject)
{
Renderer vfxRenderer = vfxGameObject.GetComponent<Renderer>();
if (vfxRenderer != null)
{
vfxRenderer.sortingLayerID = defaultSortingLayer.id;
}
}
/// <summary>
/// Получить VFX объект из пула (без автоматического запуска)
/// </summary>
/// <param name="vfxAsset">Ассет визуального эффекта для применения</param>
/// <returns>GameObject с компонентом VisualEffect или null, если не удалось получить</returns>
private 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;
// Устанавливаем Sorting Layer для рендерера VFX
ApplySortingLayer(vfxInstance);
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;
}
public GameObject Spawn(VisualEffectAsset vfxAsset, Transform newParentTransform, float? lifetime = null)
{
GameObject vfxInstance = Spawn(vfxAsset, newParentTransform.position, lifetime);
vfxInstance.transform.SetParent(newParentTransform);
return vfxInstance;
}
private IEnumerator ReturnAfterCoroutine(GameObject vfxInstance, float seconds)
{
// Ждём указанное время. Можно заменить на более точный механизм при наличии событий от VFXGraph.
yield return new WaitForSeconds(seconds);
// Перед возвратом явно останавливаем эффект
VisualEffect visualEffect = vfxInstance.GetComponent<VisualEffect>();
if (visualEffect) visualEffect.Stop();
// Возвращаем в пул
Return(vfxInstance);
}
/// <summary>
/// Вернуть VFX объект обратно в пул для повторного использования
/// </summary>
/// <param name="vfxInstance">VFX объект для возврата в пул</param>
private void Return(GameObject vfxInstance)
{
if (vfxInstance == null) return;
// Явно устанавливаем родителем пул, чтобы избежать захламления сцены
vfxInstance.transform.SetParent(this.transform);
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);
}
}
}
}