Как правильно передавать данные между сценами в Unity?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Основные подходы к передаче данных между сценами в Unity
В Unity отсутствует встроенная система глобального управления состоянием, поэтому разработчики используют несколько паттернов для передачи данных между сценами. Выбор метода зависит от типа данных, архитектуры проекта и требований к производительности.
1. Статические классы и синглтоны (наиболее распространенный подход)
Статический класс-менеджер — самый простой способ сохранения данных на протяжении всей сессии приложения. Данные сохраняются в памяти между загрузками сцен.
// GameDataManager.cs
public static class GameDataManager
{
public static int PlayerScore { get; set; }
public static string PlayerName { get; set; }
public static int SelectedLevel { get; set; }
public static void ResetData()
{
PlayerScore = 0;
PlayerName = "Player";
SelectedLevel = 1;
}
}
// Использование в другой сцене
public class ScoreDisplay : MonoBehaviour
{
void Start()
{
Debug.Log($"Player: {GameDataManager.PlayerName}, Score: {GameDataManager.PlayerScore}");
GameDataManager.PlayerScore += 100; // Модификация данных
}
}
Паттерн синглтон для MonoBehaviour объектов, которые должны существовать в единственном экземпляре:
// GameSession.cs
public class GameSession : MonoBehaviour
{
public static GameSession Instance { get; private set; }
public PlayerData PlayerData { get; set; }
public GameSettings Settings { get; set; }
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject); // Объект не уничтожается при загрузке новой сцены
}
else
{
Destroy(gameObject);
}
}
}
2. DontDestroyOnLoad для объектов-переносчиков
Метод DontDestroyOnLoad() позволяет сохранить GameObject и все его компоненты при переходе между сценами:
public class DataCarrier : MonoBehaviour
{
public Inventory playerInventory;
public int currentHealth;
void Start()
{
DontDestroyOnLoad(gameObject);
// Инициализация данных
playerInventory = new Inventory();
currentHealth = 100;
}
// Метод для доступа к данным из любой сцены
public static DataCarrier GetInstance()
{
return FindObjectOfType<DataCarrier>();
}
}
3. ScriptableObjects как хранилища данных
ScriptableObjects — мощный инструмент Unity для создания переиспользуемых наборов данных, которые существуют независимо от сцен:
// GameDataSO.cs
[CreateAssetMenu(fileName = "GameData", menuName = "Data/GameData")]
public class GameDataSO : ScriptableObject
{
[SerializeField] private int highScore;
[SerializeField] private List<string> unlockedLevels;
[SerializeField] private PlayerStats playerStats;
public int HighScore
{
get => highScore;
set => highScore = value;
}
public void UnlockLevel(string levelId)
{
if (!unlockedLevels.Contains(levelId))
unlockedLevels.Add(levelId);
}
}
// Использование
public class LevelManager : MonoBehaviour
{
[SerializeField] private GameDataSO gameData;
public void CompleteLevel(int score)
{
if (score > gameData.HighScore)
gameData.HighScore = score;
SaveSystem.SaveGameData(gameData);
}
}
4. Система событий (Event System)
Использование делегатов и событий для уведомления систем о необходимости передачи данных:
// EventManager.cs
public static class EventManager
{
public static event Action<PlayerData> OnPlayerDataChanged;
public static event Action<int> OnSceneChangeRequested;
public static void TriggerPlayerDataChanged(PlayerData data)
{
OnPlayerDataChanged?.Invoke(data);
}
public static void RequestSceneChange(int sceneIndex)
{
OnSceneChangeRequested?.Invoke(sceneIndex);
}
}
// Подписчик в другой сцене
public class UIManager : MonoBehaviour
{
void OnEnable()
{
EventManager.OnPlayerDataChanged += HandlePlayerDataChanged;
}
void OnDisable()
{
EventManager.OnPlayerDataChanged -= HandlePlayerDataChanged;
}
private void HandlePlayerDataChanged(PlayerData data)
{
UpdateUI(data);
}
}
5. Сериализация и файлы сохранений
Для постоянного хранения данных между игровыми сессиями используется сериализация:
// SaveSystem.cs
public static class SaveSystem
{
private static string savePath = Application.persistentDataPath + "/save.dat";
public static void SaveGame(GameData data)
{
string jsonData = JsonUtility.ToJson(data, true);
System.IO.File.WriteAllText(savePath, jsonData);
// Или использование BinaryFormatter (устаревший) или современные альтернативы
}
public static GameData LoadGame()
{
if (System.IO.File.Exists(savePath))
{
string jsonData = System.IO.File.ReadAllText(savePath);
return JsonUtility.FromJson<GameData>(jsonData);
}
return new GameData(); // Возвращаем данные по умолчанию
}
}
6. Архитектурные подходы и Dependency Injection
Для сложных проектов рекомендуются паттерны проектирования:
- Service Locator — глобальный реестр сервисов
- Dependency Injection через фреймворки (Zenject/VContainer)
- Модель MVVM с разделением данных и представления
Рекомендации по выбору метода
- Для простых проектов: статические классы или синглтоны
- Для данных конфигурации: ScriptableObjects
- Для постоянных данных: сериализация + файловая система
- Для реактивности: система событий
- Для сложных архитектур: DI-фреймворки
Ключевые принципы:
- Инкапсуляция данных — ограничение прямого доступа
- Сепарация concerns — разделение логики данных и представления
- Тестируемость — возможность мокать данные для тестов
- Производительность — минимизация накладных расходов
- Сохранение состояния при перезапусках приложения
Выбор подхода зависит от масштаба проекта: для мобильных игр часто достаточно статических классов, тогда как для крупных PC-проектов требуется полноценная архитектура с DI и State Management.