>
>
>
V4008. Unity Engine. Avoid using memory…


V4008. Unity Engine. Avoid using memory allocation Physics APIs in performance-sensitive context.

Анализатор обнаружил в часто выполняемом коде использование аллоцирующих память методов Physics.RaycastAll, Physics2D.OverlapSphere или подобных им.

Такие методы создают массив при каждом вызове, что может снизить производительность. Вместо них стоит использовать неаллоцирующие вариации методов, в которые можно передать предварительно выделенный массив.

Рассмотрим пример:

public class HitScript : MonoBehaviour
{
  void Update()
  {
    AllocMethod();
  }

  void AllocMethod()
  {
    RaycastHit[] hitsTargets = Physics.RaycastAll(transform.position, 
                                                  transform.forward);

    foreach (RaycastHit hit in hitsTargets)
    {
      if (hit.collider.gameObject.name != "PlayerAdvanced")
      {
        ....
      }
    }
  }
}

Update будет каждый кадр вызывать AllocMethod, использующий аллоцирующий метод RaycastAll. Из-за постоянного создания новых массивов, которые содержат попадания луча, производительность ухудшится.

Данный код можно оптимизировать. Для этого нужно заменить метод RaycastAll на неаллоцирующий метод RaycastNonAlloc и использовать его вместе с предварительно выделенным массивом.

Вариант оптимальной реализации:

public class HitScript : MonoBehaviour
{
  void Update()
  {
    NonAllocMethod();
  }

  const int MAX_RAYCAST_HIT_COUNT = 10;
  RaycastHit[] _hitsTargets = new RaycastHit[MAX_RAYCAST_HIT_COUNT];

  void NonAllocMethod()
  {
    int countOfResults = Physics.RaycastNonAlloc(new Ray(transform.position,
                                                         transform.forward ),
                                                 _hitsTargets);

    for(int i = 0; i < countOfResults; i++)
    {
      if (_hitsTargets[i].collider.gameObject.name != "PlayerAdvanced")
      {
        ....
      }
    }
  }
}

Для использования RaycastNonAlloc заранее создан вспомогательный массив _hitsTargets. Максимальное количество попаданий луча ограничено размером этого массива. После вызова метода RaycastNonAlloc количество попаданий луча будет записано в переменную countOfResults, а сами попадания будут записаны в массив _hitsTargets. Таким образом, в каждом кадре будет использоваться массив _hitsTargets, под который память выделяется лишь один раз.

На момент написания документации к диагностике в классе Physics2D все методы с припиской "NonAlloc" помечены как устаревшие, им на замену можно использовать неаллоцирующие перегрузки обычных методов. Например, вместо Physics2D.BoxCastAll стоит использовать перегрузку Physics2D.BoxCast, принимающую массив для получения всех попаданий.