Продолжаем тему оптимизаций и сегодня поговорим о сборщике мусора.
Сборщик мусора (GC) - это система, которая освобождает память, когда объект больше не используется.
Проблема в том, что во время сборки мусора игра фризится - GC останавливает всё, чтобы убрать неиспользуемые объекты.
Поэтому, ваша цель - уменьшить количество мусора и управлять сборкой вручную, когда это безопасно.
1️⃣ Включите инкрементальный GC
Unity поддерживает Incremental GC. В таком случае сборка мусора выполняется не целиком, а частями - по кадрам.
Так фризы становятся почти незаметными.
Включается в Project Settings → Player → Other Settings → Configuration → Incremental GC.
2️⃣ Собирайте мусор вручную в удобные моменты
Вместо того, чтобы GC отрабатывал в важные моменты геймплея - можно самому вызвать сборку в визуально простые моменты игры, где фризы экрана будут незаметны.
Обычно это экраны пауз - часто они статичные, или имеют простую анимацию.
Вызов сборки мусора:
GC.Collect();
3️⃣ Не используйте конкатенацию строк в циклах или Update
Каждая конкатенация строки == новый объект и мусор для сборки. Если цикл состоит из 16 итераций - будет создано 16 строк, что окажет большую нагрузку на GC.
Плохо:
string ConcatExample(string[] stringArray) {
string result = "";
for (int i = 0; i < stringArray.Length; i++) {
result += stringArray[i]; // каждый раз - новый объект
}
return result;
}Хорошо:
private StringBuilder _sb = new StringBuilder(16);
string ConcatExample(string[] stringArray) {
_sb.Clear();
for (int i = 0; i < stringArray.Length; i++) {
_sb.Append(stringArray[i]); // используем StringBuilder - так создадим только один объект строки
}
return _sb.ToString();
}
4️⃣ Кешируйте строки
Типичная ошибка - обновление текста при любом обновлении счётчика или постоянно в Update.
Кешируйте текст и обновляйте только при изменении значения.
Плохо:
public Text scoreBoard;
public int score;
void Update() {
string scoreText = "Score: " + score.ToString(); // создаём новый объект каждый кадр
scoreBoard.text = scoreText;
}
Хорошо:
public Text scoreBoard;
public string scoreText;
public int score;
public int oldScore;
void Update() {
if (score != oldScore) {
scoreText = "Score: " + score.ToString(); // создаём новые объект, только если значение изменилось
scoreBoard.text = scoreText;
oldScore = score;
}
}
5️⃣ Используйте NonAlloc-версии методов
Многие API в Unity создают мусор: Physics.OverlapSphere, Physics.Raycast и т.д.
Иногда есть версии с суффиксом NonAlloc:
Physics.OverlapSphereNonAlloc(position, radius, colliders);
При написании своих методов - следуйте той же идее: не создавайте новые массивы, если можно переиспользовать старые.
💡 Вывод и дополнительные материалы
Оптимизация GC - это не сложно.
Главное - избегать лишних аллокаций и управлять сборкой в удобные моменты.
Чем меньше мусора вы создаёте, тем плавнее идёт игра.
Статья от Unity по оптимизации GC
Профайлинг GC от GameDevGuru
🚀 Пост Guru Unity: @Minerope