Использование пулов скрытия для юнитов на сцене для увеличения производительности
Тест без движения юнитов. С движением кол-во юнитов в сцене будет меньше, но его нужно оптимизировать отдельно.
Задача оказалась достаточно интересной. Сначала было сделано обычное отключение по расстоянию от главного персонажа. Получил где-то 10.000 юнитов нормальной работы. Потом стал думать дальше, что можно еще сделать. Очевидным было увеличение времени проверки при удалении от персонажа. Но на деле оказалось, что это не очень выгодный вариант для не большой карты, так как ресурсы стали тратиться на вычисление времени плюс дополнительное деление (можно бы было от него избавится, но подумал, что это все-таки не лучший вариант). Тогда решил использовать пулы. Главная идея - сделать один update для всех юнитов на одном расстоянии от персонажа. Создал пул с временем включения всех дочерних объектов через определенное время и стал кидать туда полностью выключенных юнитов. Это помогло, тогда стал дорабатывать этот функционал, сделал массив пулов с увеличивающимся расстоянием от персонажа и увеличивающимся временем включения. Юниты на определенной дистанции кидаются в один пул (всего тестировал до 2000 пулов с шагом 1 тайл и временем 0.5f секунд). В итоге получил где-то 300.000 юнитов на карте при 50fps, если стоять не в гуще мобов, и 30 fps, если зайти в "линейку" юнитов (на видео fps меньше, так как снятие видео отбирало ресурсы). Тут, конечно, еще важно, что юниты не кучкуются на одном месте, а разбросаны по уровню. И они без движения, - это надо дорабатывать отдельно другим функционалом.
Ps. Также хочу заметить, юниты не просто спрайты, на них есть коллайдеры, анимация, проверка на конец платформы и последующим разворотом. Отключено взаимодействие с персонажем, чтобы можно было спокойно среди них гулять.
По видео пара комментов. В основном хотел показать, как обрезается активность юнитов, как работают пулы. Уточнение по fps, - важно, что просадка это в основном из-за постоянного создания новых юнитов, там есть моменты, где я отключаю создание и это очень сильно влияет на fps. Также просадка при передвижении среди юнитов. Тут важно, что кроме теста не вижу такой ситуации, чтобы было столько же юнитов в одном кадре, а чуть отойдя и разрядив кол-во мобов fps возвращается на приемлемый уровень. Также можно еще играться с параметрами кол-ва пулов (тут их 700) времени отката пула (тут оно всего 0.5 сек, так как прохожу напрямую сквозь монстров, в игре такого не будет и можно смело поднимать в несколько раз), шага между ними. Также можно заметить, как подскакивает fps при открытии меню, - тогда отключается физика юнитов.
Дальше для оптимизации можно сделать общий менеджер-апдейтер и пул для удаленных объектов(юнитов, снарядов)
Минусы: обновление пулов нужно рассчитывать с максимальным ускорением персонажа. Пулы должны рассчитываться на всю карту, так как если их будет меньше на последнем пуле соберется слишком много юнитов и их включение будет слишком заметно. Также такой функционал весьма ограничивает использование точек респауна или телепортов или придется обновлять все пулы перед этим.
Как это было сделано:
в синглтоне LinksManager создал публичный массив пулов таким образом:
public static float hideDistance = 8; // Меньше этого значения расстояния до объекта активно
GameObject poolDistancePref; // Префаб пула с скриптом на нем
public static int poolStep=3; // После hideDistance с этим шагом юниты будут попадать в пул
public static float poolTime = 0.5f; // Шаг включения дочерних объектов пулов
public static GameObject[] poolDistance=new GameObject[700]; // Это пулы
Настраиваем пулы в методе этого синглтона:
poolDistancePref= Resources.Load<GameObject>("Prefabs/items/poolDistance");
for (var i = 0; i < poolDistance.Length;i++)
{
poolDistance[i] = Instantiate(poolDistancePref);
Distance distance= poolDistance[i].GetComponent<Distance>();
distance.reloadTime = i * poolTime+1;
}
На префабе пула висит скрипт Distance
using UnityEngine;
public class Distance : MonoBehaviour {
public float reloadTime=1.0f;
[SerializeField]
private int childs;
private float timer = 0;
void Update()
{
timer += Time.deltaTime;
if (timer > reloadTime)
{
timer = 0;
childs = transform.childCount;
for (var i = 0; i < childs; i++)
{
transform.GetChild(i).gameObject.SetActive(true);
}
}
}
}
Схема каждого юнита:
Пустой объект, на котором висит только скрипт HideByDistance
> Пустой объект с названием child, на котором висит вся логика юнита (скрипт действий, коллайдеры, физика и т.д.). Этот объект будет при небольшом удалении отключаться без помещения в пул.
> Все остальное (Спрайт)
Скрипт HideByDistance:
Любой объект на сцене с HideByDistance будет добавляться в пулы.
using UnityEngine; public class HideByDistance : MonoBehaviour { public float distance = 0.0f; [SerializeField] private int removalRate = 8; private Transform child; private float timer = 0; void Start() { child = transform.Find("child"); } void Update() { CheckDistance(); } private void CheckDistance() { var heading = transform.position - LinksManager.player.transform.position; distance = heading.magnitude; if (distance < LinksManager.hideDistance) { child.gameObject.SetActive(true); } else { for (var i = LinksManager.poolDistance.Length-1; i >= 0; i--) { if (distance >LinksManager.hideDistance+ LinksManager.poolStep * i) { gameObject.transform.parent = LinksManager.poolDistance[i].transform; gameObject.SetActive(false); break; } child.gameObject.SetActive(false); } } } }

Комментарии
Отправить комментарий