unity3d
оптимизация
Поиск…
замечания
- Если возможно, отключите скрипты на объектах, когда они не нужны. Например, если у вас есть скрипт на объекте противника, который ищет и стреляет в игрока, подумайте об отключении этого скрипта, когда противник слишком далеко, например, от игрока.
Быстрые и эффективные проверки
Избегайте ненужных операций и вызовов методов, где бы вы ни находились, особенно в методе, который вызывается много раз в секунду, например « Update
.
Проверка расстояния / диапазона
При сравнении расстояний используйте sqrMagnitude
вместо magnitude
. Это позволяет избежать ненужных операций sqrt
. Обратите внимание, что при использовании sqrMagnitude
правая часть также должна быть в квадрате.
if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))
Проверка границ
Пересечения объектов можно грубо проверить, проверив, пересекаются ли их границы Collider
/ Renderer
. Структура Bounds
также имеет удобный метод Intersects
который помогает определить, пересекаются ли две границы.
Bounds
также помогают нам рассчитать приблизительное приблизительное фактическое (от поверхности к поверхности) расстояние между объектами (см. Bounds.SqrDistance
).
Предостережения
Проверка границ работает очень хорошо для выпуклых объектов, но проверки границ вогнутых объектов могут приводить к гораздо более высоким неточностям в зависимости от формы объекта.
Использование Mesh.bounds
не рекомендуется, поскольку оно возвращает границы локального пространства. MeshRenderer.bounds
этого используйте MeshRenderer.bounds
.
Мощность Coroutine
использование
Если у вас длительная работа, основанная на небезопасном Unity API, используйте Coroutines, чтобы разделить ее на несколько фреймов и откликнуться на ваше приложение.
Coroutines также помогает выполнять дорогостоящие действия для каждого n-го кадра вместо того, чтобы запускать это действие в каждом кадре.
Разделение длинных очередей на несколько кадров
Coroutines помогают распределять длительные операции над несколькими кадрами, чтобы поддерживать скорость работы вашего приложения.
Процедуры, которые рисуют или генерируют рельеф в процедуре или создают шум, являются примерами, которые могут потребовать обработки Coroutine.
for (int y = 0; y < heightmap.Height; y++)
{
for (int x = 0; x < heightmap.Width; x++)
{
// Generate pixel at (x, y)
// Assign pixel at (x, y)
// Process only 32768 pixels each frame
if ((y * heightmap.Height + x) % 32 * 1024) == 0)
yield return null; // Wait for next frame
}
}
Приведенный выше код является простым для понимания примером. В производственном коде лучше избегать чек на пиксель , который проверяет , когда
yield return
(возможно делать это через каждые 2-3 ряда) и предварительно рассчитатьfor
длине петли заранее.
Выполнение дорогостоящих действий менее часто
Coroutines помогает вам выполнять дорогостоящие действия реже, так что это не так сильно, как это было бы при каждом кадре.
Приведя следующий пример непосредственно из Руководства :
private void ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
return true;
}
return false;
}
private IEnumerator ProximityCheckCoroutine()
{
while(true)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
Тесты близости можно еще более оптимизировать, используя API CullingGroup .
Общие проблемы
Разработчики общей ошибки делают доступ к результатам или побочным эффектам сопрограммы вне сопрограммы. Coroutines возвращает управление вызывающему абоненту, как только встретится оператор yield return
и результат или побочный эффект еще не могут быть выполнены. Чтобы обойти проблемы, когда вы должны использовать результат / побочный эффект вне сопрограммы, проверьте этот ответ .
Струны
Можно утверждать, что в Unity больше богов ресурсов, чем скромная строка, но это один из самых простых аспектов для исправления на ранней стадии.
Строковые операции создают мусор
Большинство операций с строкой создают крошечные суммы мусора, но если эти операции вызывают несколько раз в течение одного обновления, он складывается. Со временем это вызовет автоматическую сборку мусора, которая может привести к видимому всплеску процессора.
Кэш ваших строковых операций
Рассмотрим следующий пример.
string[] StringKeys = new string[] {
"Key0",
"Key1",
"Key2"
};
void Update()
{
for (var i = 0; i < 3; i++)
{
// Cached, no garbage generated
Debug.Log(StringKeys[i]);
}
for (var i = 0; i < 3; i++)
{
// Not cached, garbage every cycle
Debug.Log("Key" + i);
}
// The most memory-efficient way is to not create a cache at all and use literals or constants.
// However, it is not necessarily the most readable or beautiful way.
Debug.Log("Key0");
Debug.Log("Key1");
Debug.Log("Key2");
}
Это может выглядеть глупо и избыточно, но если вы работаете с шейдерами, вы можете столкнуться с такими ситуациями. Кэширование клавиш будет иметь значение.
Обратите внимание, что строковые литералы и константы не генерируют никакого мусора, так как они вставляются статически в пространство стека программ. Если вы генерируете строки во время выполнения и гарантированно генерируете одни и те же строки каждый раз, как в приведенном выше примере, кеширование определенно поможет.
В других случаях, когда генерируемая строка не является одинаковой каждый раз, нет другой альтернативы для генерации этих строк. Таким образом, всплеск памяти с ручным генерированием строк каждый раз обычно ничтожно, если только десятки тысяч строк не генерируются за раз.
Большинство строковых операций являются сообщениями Debug
Выполнение строковых операций для сообщений Debug, т.е. Debug.Log("Object Name: " + obj.name)
в порядке и не может быть предотвращено во время разработки. Тем не менее, важно обеспечить, чтобы нерелевантные отладочные сообщения не попадали в выпущенный продукт.
Один из способов - использовать атрибут Conditional в ваших отладочных вызовах. Это не только удаляет вызовы метода, но и все операции с строкой, входящие в него.
using UnityEngine;
using System.Collections;
public class ConditionalDebugExample: MonoBehaviour
{
IEnumerator Start()
{
while(true)
{
// This message will pop up in Editor but not in builds
Log("Elapsed: " + Time.timeSinceLevelLoad);
yield return new WaitForSeconds(1f);
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void Log(string Message)
{
Debug.Log(Message);
}
}
Это упрощенный пример. Возможно, вы захотите потратить некоторое время на разработку более полноценной рутинной процедуры регистрации.
Сравнение строк
Это небольшая оптимизация, но стоит упомянуть. Сравнение строк немного больше, чем можно было бы подумать. По умолчанию система будет пытаться учитывать культурные различия. Вместо этого вы можете использовать простое двоичное сравнение, которое выполняется быстрее.
// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}
// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}
// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
Ссылки на кеш
Ссылки на кеш, чтобы избежать дорогостоящих вызовов, особенно в функции обновления. Это можно сделать путем кэширования этих ссылок при запуске, если они доступны или когда они доступны, и проверки нулевого / bool flat, чтобы избежать повторной ссылки.
Примеры:
Ссылки на компоненты кэша
менять
void Update()
{
var renderer = GetComponent<Renderer>();
renderer.material.SetColor("_Color", Color.green);
}
в
private Renderer myRenderer;
void Start()
{
myRenderer = GetComponent<Renderer>();
}
void Update()
{
myRenderer.material.SetColor("_Color", Color.green);
}
Ссылки на объекты кэша
менять
void Update()
{
var enemy = GameObject.Find("enemy");
enemy.transform.LookAt(new Vector3(0,0,0));
}
в
private Transform enemy;
void Start()
{
this.enemy = GameObject.Find("enemy").transform;
}
void Update()
{
enemy.LookAt(new Vector3(0, 0, 0));
}
Кроме того, кешируйте дорогие вызовы, например, звонки в Mathf, где это возможно.
Избегайте вызывать методы, используя строки
Избегайте вызывать методы, используя строки, которые могут принимать методы. Этот подход будет использовать отражение, которое может замедлить вашу игру, особенно при использовании в функции обновления.
Примеры:
//Avoid StartCoroutine with method name
this.StartCoroutine("SampleCoroutine");
//Instead use the method directly
this.StartCoroutine(this.SampleCoroutine());
//Avoid send message
var enemy = GameObject.Find("enemy");
enemy.SendMessage("Die");
//Instead make direct call
var enemy = GameObject.Find("enemy") as Enemy;
enemy.Die();
Избегайте пустых методов единства
Избегайте пустых методов единства. Помимо плохого стиля программирования, во время исполнения сценариев очень мало накладных расходов. Во многих случаях это может создать и повлиять на производительность.
void Update
{
}
void FixedUpdate
{
}