Поради щодо оптимізації для Unity

Unity — це ігровий движок, популярний не лише серед інді-розробників, але й серед великих компаній.

Він має зручний інтерфейс, потужний конвеєр візуалізації, просту у використанні систему компонентів і, нарешті, підтримує широкий спектр платформ.

Але з простим у використанні інтерфейсом ще легше ускладнити вашу гру (наприклад, розмістивши багато непотрібних об’єктів тощо), тому важливо мати оптимізацію пам’ятати протягом усього процесу розвитку.

Ось важливі поради щодо 3 основних категорій (Відтворення, Сценарії, та Аудіо), які допоможуть вам покращити продуктивність гри:

Візуалізація

Порада 1: Не масштабуйте об’єкти з компонентом Renderer

Немасштабовані об’єкти – це ті, які мають масштаб (1, 1, 1). У цьому випадку Unity не потрібно виконувати додаткові обчислення для зміни масштабу об’єкта в кожному кадрі.

Приклад: Припустімо, у вас є модель будинку, яка завелика або замала для вашого рівня. Природно масштабувати це так:

Зміна розміру будівлі

Здебільшого допустимо масштабувати об’єкт у поданні сцени, але якщо ви плануєте мати багато дубльованих екземплярів цього об’єкта, бажано змінити масштаб у параметрах імпорту.

Перше, що вам потрібно зробити, це масштабувати модель, щоб вона відповідала вашим потребам (наприклад, масштаб будівлі вище було змінено від (1, 1, 1) до (0,49482, 0,49482, 0,49482)), а потім виберіть модель у проекті перегляду та в параметрах імпорту зверніть увагу на масштабний коефіцієнт (зазвичай це 1 або 0,1).

Встановіть нове значення, яке має дорівнювати стандартному масштабному коефіцієнту, помноженому на новий масштаб (у моєму випадку це 1 x 0,49482 = 0,49482), а потім натисніть «Застосувати». Тепер поверніться до моделі в режимі перегляду сцени та знову встановіть масштаб (1, 1, 1).

Налаштування коефіцієнта масштабування Unity 3D

Тепер об’єкт масштабується так, як вам потрібно, зі збереженням стандартного масштабу (1, 1, 1).

Ця порада особливо важлива для анімованих об’єктів, які використовують SkinnedMeshRenderer, оскільки візуалізація цього компонента дорожча, а масштаб (1, 1, 1) спрощує процес візуалізації.

Порада 2: використовуйте якомога менше світла

У Unity є 3 типи світла (спрямоване світло, точкове світло та прожектор). З точки зору продуктивності спрямоване світло є найдешевшим для візуалізації, потім точкове і, нарешті, прожектор.

Як правило, ви не хочете мати більше ніж 1 спрямоване світло на сцену, а для точкових і точкових джерел світла намагайтеся мати якомога менше (або взагалі не мати).

Що стосується тіней у реальному часі, хоча вони покращують візуальний аспект гри, вони мають високу продуктивність, тому, як правило, їх краще вимкнути або записати в lightmaps і light зонди.

Порада 3. Використовуйте прозорі шейдери з обережністю

Використовуйте прозорі або шейдери частинок лише на поверхнях, які мають бути прозорими (наприклад, огорожі, частки диму тощо)

Об’єкти з прозорістю вимагають додаткового проходу візуалізації, що може знизити продуктивність, особливо на платформах з обмеженими ресурсами, як-от Mobile або Web.

Сценарії

Порада 1: Завжди кешуйте посилання на компоненти

Ви завжди повинні кешувати посилання на компоненти, якщо плануєте отримувати до них доступ кожного оновлення.

Наприклад, перевірте сценарій нижче:

погано

using UnityEngine;

public class Script1 : MonoBehaviour
{
    float someValue = 0;

    // Update is called once per frame
    void Update()
    {
        someValue = GetComponent<Script2>().someValue2;
    }
}

Тут ми маємо Script1, який отримує змінну "someValue2" зі Script2 і призначає її локальній змінній.

Тепер виклик лише одного GetComponent кожного кадру не матиме жодного впливу на продуктивність, однак вам слід прийняти звичку кешувати компоненти, які використовуватимуться часто.

Є два способи кешування компонента в сценарії: або створити загальнодоступну змінну та призначити її в режимі перегляду інспектора, або створити приватну змінну та призначити її за допомогою Start або Awake. Перевірте приклад нижче:

добре

using UnityEngine;

public class Script1 : MonoBehaviour
{

    float someValue = 0;

    Script2 script2Cached;

    // Use this for initialization
    void Start()
    {
        script2Cached = GetComponent<Script2>();
    }

    // Update is called once per frame
    void Update()
    {
        someValue = script2Cached.someValue2;
    }
}

Набагато краще те, що Script2 тепер доступний під час кожного оновлення без накладних витрат на продуктивність.

Зробіть те саме для вбудованих компонентів, таких як BoxCollider, Rigidbody тощо (окрім Transform і GameObject, вони вже кешуються за замовчуванням, тому ви можете отримати до них доступ одразу).

Порада 2. Використовуйте SendMessage з обережністю

SendMessage дозволяє викликати певну функцію (якщо існує) для кожної MonoBehaviour, яка прикріплена до об’єкта гри.

Наприклад, коли ви стріляєте зі зброї в грі, ви можете швидко завдати шкоди, коли куля потрапляє у ворога, без необхідності використовувати GetComponent та інші додаткові речі.

Однак цей метод не слід викликати надто часто, оскільки він досить інтенсивний.

Порада 3: використовуйте GameObject.Find і GameObject.FindWithTag з обережністю

GameObject.Find, GameObject.FindWithTag і GameObject.FindGameObjectsWithTag дозволяють швидко шукати об’єкти в сцені. Ці методи набагато повільніші, ніж GetComponent, і їх слід використовувати лише під час ініціалізації.

Порада 4: уникайте використання OnGUI

Історично OnGUI був єдиним способом створити меню в Unity. Але з того часу була додана альтернатива під назвою UI Canvas, яка є набагато кращою з точки зору продуктивності та пропонує набагато більше функціональних можливостей.

Незважаючи на це, OnGUI все ще залишається життєздатним способом створення інтерфейсу користувача в Unity, і якщо вам конче потрібно його використовувати, майте на увазі, що OnGUI викликається принаймні двічі за кадр (удвічі частіше, ніж Update і LateUpdate), тому не використовуйте його для будь-яких обчислень, крім інтерфейсу користувача.

Одне, що ви можете зробити, це мати окремий сценарій, який містить лише OnGUI, і вмикати/вимкнути його за потреби.

Наприклад:

UIScript.cs

using UnityEngine;

public class UIScript : MonoBehaviour {

    void OnGUI()
    {
        if(GUI.Button(new Rect(5, 5, 125, 25), "Button 1"))
        {
            //Button 1 was pressed, Do Something
        }
        if (GUI.Button(new Rect(140, 5, 125, 25), "Button 2"))
        {
            //Button 2 was pressed, Do Something
        }
    }
}

Script1.cs

using UnityEngine;

public class Script1 : MonoBehaviour
{

    UIScript uiScript;

    // Use this for initialization
    void Start()
    {
        uiScript = GetComponent<UIScript>();
        uiScript.enabled = false;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            //toggle UIScript when Tab is pressed
            uiScript.enabled = !uiScript.enabled;
        }
    }
}

І UIScript, і Script1 приєднані до одного GameObject. Сценарій1 контролює, коли показувати меню.

Коли гравець натискає Tab, UIScript вмикається, показуючи кнопки. Повторне натискання Tab деактивує його, приховуючи кнопки.

Поки UIScript дезактивовано, метод OnGUI не викликається, що, у свою чергу, покращує продуктивність.

Порада 5: використовуйте Profiler

Profiler є одним із найважливіших інструментів, коли справа доходить до виявлення вузьких місць і падіння частоти кадрів, що полегшує пошук точної причини низької продуктивності.

Аудіо

Аудіокліпи можна оптимізувати, переконавшись, що їхні налаштування імпорту правильні.

Оптимальні Параметри імпорту аудіо залежатимуть від тривалості аудіо, частоти відтворення та цільової платформи.