Когда Unity Developer немножко виноват.
Да, я говорил, что 90% оптимизации - забота технического артиста. Но оставшиеся 10%... они умеют больно кусать за FPS. Причём, когда артист всё уже выжал, а игра всё равно тормозит.
Недавно получил кейс: игра лоу-поли, графика - картошка, но в эдиторе выше 100 FPS не поднимается. Провёл первичную магию (запёк свет, настроил батчинг) — получил 140 вызовов на проце, 260 статик-батчей, на видео просто отличные 58 вызово… и тормоза. Не принтер, но 150 фпс в билде для такого ПК проекта...
Открыл профайлер. И там… космос. Около 30% времени в главном потоке движок просто ЖДАЛ. Ничего не делал. Стоял и ждал.
Не люблю лезть в чужой код, но пришлось. И тут началось веселье.
Шаг первый. Асинхронность — не просто модное слово Проект был идеально SOLID, MVP и прочим, но… монопоток. Всё в линейку.
Асинхронность и многопоточность — это ключ. Их не было. Добавил async/UniTask/Job для тяжёлых расчётов, вынес физику отдельно и главный поток выдохнул.
Monobehaviour — вынужденное зло. Даже если у вас нет Update/FixedUpdate/LateUpdate, он всё равно встаёт в очередь на обработку каждым кадром. В сцене 400 объектов, на каждом по 3 монобеха. 1200 сущностей трижды в кадр ждали своей очереди. Просто отключил ненужные после инициализации - +20 FPS. Запомните: отработал - отключай.
Шаг второй. Смертельные грехи Update Прогнал по коду — нашёл классику.
FindGameObject или GetComponent в Update? Можно смело бить в лицо. Это функции перебора. Заменил на кэширование в Start - убрал микрофризы.
Сравнение дистанций. Если не нужно показывать расстояние на экране - используйте квадраты. Vector3.Distance внутри - теорема Пифагора с корнем. А корень - это функция подбора, самая затратная. Заменил на sqrMagnitude - и сотни вызовов перестали жрать кадр.
Циклы и вычитание. Тут просто рекомендация,т.к. прирост просто микроскопический, воспринимать как занятный факт. Самая быстрая операция - вычитание. Поэтому for (int i = 100; i > 0; i--) быстрее, чем с инкрементом. А временные отчёты лучше делать вычитанием i -= Time.deltaTime, а не сложением.
Dictionary вместо списков. Да, он жрёт на старте, но поиск - по хешу, а не перебором. Была система состояний с FirstOrDefault - заменил на Dictionary<Transform, State>. При хит-коллайдере - прямое обращение. На порядок быстрее.
Шаг третий. Тяжёлая артиллерия
ComputeShader - для гиперкрутых. Это неграфические расчёты на GPU. GPU делает простейшие операции в тысячи раз быстрее CPU. Но это уровень «договориться с техартистом», самому можно сварганить, но ты не знаешь как техартист будет кушать GPU, решения могут устроить бойню в GPU.
Самое важное — границы ответственности Были кейсы, когда я выставлял определённые настройки производительности (пакетное окклюдирование, LOD-группы, билд-сетап), а разработчик их менял, потому что «его код не вписывался». Результат плачевный. Договаривайтесь на старте. Оптимизация - командная игра. Техартист не враг, он хочет тебе помочь.
Итоги 10% кода решают 50% тормозов. Асинхронность, кэширование, квадраты, словари и выключенные монобехи — вот что спасает, когда артист уже выжал всё из текстур и света.
Всем 60 FPS и никакой магии - только системный подход. И да, FindGameObject /GetComponent в Update/FixedUpdate/LateUpdate - это уголовщина.
· 12.06
Ничего в этом не понимаю, но это было круто как снять кота с дерева при помощи обезьяны. Браво!
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
коммент удалён
· 12.06
Только не обезьяна. А стоят 1200 человек, из них 1199 чуваков просто подходят к дереву и отходят без работы, и только один сначала взял кота, потом все еще раз просто подходят еще раз и отходят, и только потом вы его отпускаете…
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
ответ удалён
· 12.06
А потом все радостно хлопают со словами МЫ ЭТО СДЕЛАЛИ! МЫ СМОГЛИ! 😂
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
ответ удалён