Подробный список изменений: - Перенесены эффекты частиц из 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-индекс проекта.
97 lines
4.5 KiB
Markdown
97 lines
4.5 KiB
Markdown
# Полное руководство по Addressables
|
||
|
||
Система **Addressables** (`com.unity.addressables`) — это замена устаревшей папке `Resources` и сложным `AssetBundles`. Она позволяет загружать контент по текстовому адресу из любой точки (локально или с удаленного сервера), не меняя код игры.
|
||
|
||
## 1. Загрузка одиночных ассетов и Интеграция с UniTask
|
||
|
||
В проекте мы используем `Cysharp.Threading.Tasks` для превращения неудобных `AsyncOperationHandle` в удобные `UniTask`.
|
||
|
||
```csharp
|
||
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`.
|
||
|
||
```csharp
|
||
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()`.
|
||
|
||
```csharp
|
||
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()`.
|
||
|
||
```csharp
|
||
public void KillEnemy(GameObject enemyInstance)
|
||
{
|
||
// Это автоматически уничтожит GameObject на сцене И уменьшит счетчик префаба в памяти.
|
||
// Если это был последний враг такого типа, сам префаб выгрузится из RAM.
|
||
Addressables.ReleaseInstance(enemyInstance);
|
||
}
|
||
```
|
||
|
||
## 4. Загрузка по Меткам (Labels)
|
||
|
||
Addressables позволяет загружать списки ассетов. Например, вы можете пометить все префабы уровней меткой `Level` и загрузить их одним вызовом.
|
||
|
||
```csharp
|
||
public async UniTask<IList<LevelData>> LoadAllLevelsAsync()
|
||
{
|
||
// Загружает все ассеты, у которых стоит Label "Level"
|
||
IList<LevelData> levels = await Addressables.LoadAssetsAsync<LevelData>("Level", null).ToUniTask();
|
||
return levels;
|
||
}
|
||
```
|