Подробный список изменений: - Перенесены эффекты частиц из C#-кода в готовые префабы-ассеты (Vfx Prefabs) в папке Assets/Resources/Vfx/ с автоматической генерацией материалов в Editor. - Исправлены ошибки duration на играющих системах частиц и розовые текстуры в GameVfxService.cs. - Исправлена навигация и фокус кнопок на оверлеях ContinueGameOverlay и PrizeResultOverlay с автоматическим подсвечиванием кнопок лимонно-зеленым цветом при фокусе и проигрыванием звука. - Добавлена проверка настроек в GameplayHudController.cs для скрытия ContinueGameOverlay, если продолжение игры отключено. - Расширен сбор статистики: создана структура GameplaySessionRecord, добавлена история до 50 раундов в PrizeSessionStateService и JsonPrizeSessionStateRepository, выведен расчет средних показателей и список 5 последних раундов в сервисной статистике. - Повышена надежность COM-транспорта: добавлен sleep (80мс) в Dispose для graceful shutdown потоков, реализован цикл переподключения до 10 попыток при обрыве связи. - Интегрированы настройки громкости с микшером: изменен шаг изменения громкости на 5% (0.05) для удобства, добавлен статус-чек и логи в ApplyServiceVolumes, расширены юнит-тесты на GetFloat параметры. - Созданы новые архитектурные гайды по аппаратному COM-транспорту и призовой экономике автомата в папке GUIDES/architecture/. - Оптимизирован и обновлен RAG-индекс проекта.
4.5 KiB
Полное руководство по Addressables
Система Addressables (com.unity.addressables) — это замена устаревшей папке Resources и сложным AssetBundles. Она позволяет загружать контент по текстовому адресу из любой точки (локально или с удаленного сервера), не меняя код игры.
1. Загрузка одиночных ассетов и Интеграция с UniTask
В проекте мы используем Cysharp.Threading.Tasks для превращения неудобных AsyncOperationHandle в удобные UniTask.
using UnityEngine.AddressableAssets;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class AssetLoader
{
public async UniTask<GameObject> LoadPlayerPrefabAsync()
{
// "PlayerPrefab" — это адрес, заданный в окне Addressables Groups
GameObject prefab = await Addressables.LoadAssetAsync<GameObject>("PlayerPrefab").ToUniTask();
return prefab;
}
}
2. Инстанцирование (InstantiateAsync)
Если вам нужно загрузить префаб и сразу создать его на сцене, НЕ ИСПОЛЬЗУЙТЕ Object.Instantiate(await LoadAssetAsync()).
Правильный способ — использовать Addressables.InstantiateAsync.
public async UniTask<GameObject> SpawnEnemyAsync(Vector3 position)
{
// Загрузит префаб в память и сразу создаст его копию на сцене.
// Addressables сам отслеживает этот объект!
GameObject enemyInstance = await Addressables.InstantiateAsync("EnemyPrefab", position, Quaternion.identity).ToUniTask();
return enemyInstance;
}
3. Reference Counting и Управление Памятью (КРИТИЧНО)
Главная проблема Addressables — утечки памяти (Memory Leaks), если не освобождать ресурсы. Система использует счетчик ссылок (Reference Counting).
- Загрузили текстуру — счетчик = 1.
- Загрузили ее еще раз где-то еще — счетчик = 2.
- Вызвали
Release— счетчик = 1. - Вызвали
Releaseеще раз — счетчик = 0, текстура выгружена из оперативной памяти (RAM/VRAM).
Правила освобождения памяти:
Сценарий 1: Вы использовали LoadAssetAsync
Если вы загрузили ассет (например, ScriptableObject настроек, AudioClip или Texture), вы обязаны вызвать Addressables.Release().
private AudioClip backgroundMusic;
public async UniTask PlayMusicAsync()
{
backgroundMusic = await Addressables.LoadAssetAsync<AudioClip>("MenuMusic").ToUniTask();
audioSource.PlayOneShot(backgroundMusic);
}
public void OnDestroy()
{
// Если объект уничтожается, освобождаем память от музыки!
if (backgroundMusic != null)
{
Addressables.Release(backgroundMusic);
}
}
Сценарий 2: Вы использовали InstantiateAsync
Если вы создали GameObject через Addressables, вы не должны удалять его через Destroy(gameObject). Используйте Addressables.ReleaseInstance().
public void KillEnemy(GameObject enemyInstance)
{
// Это автоматически уничтожит GameObject на сцене И уменьшит счетчик префаба в памяти.
// Если это был последний враг такого типа, сам префаб выгрузится из RAM.
Addressables.ReleaseInstance(enemyInstance);
}
4. Загрузка по Меткам (Labels)
Addressables позволяет загружать списки ассетов. Например, вы можете пометить все префабы уровней меткой Level и загрузить их одним вызовом.
public async UniTask<IList<LevelData>> LoadAllLevelsAsync()
{
// Загружает все ассеты, у которых стоит Label "Level"
IList<LevelData> levels = await Addressables.LoadAssetsAsync<LevelData>("Level", null).ToUniTask();
return levels;
}