← Назад к вопросам

Как правильно передавать данные между сценами в Unity?

2.2 Middle🔥 231 комментариев
#C# и ООП#Unity Core#Паттерны проектирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основные подходы к передаче данных между сценами в 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-фреймворки

Ключевые принципы:

  1. Инкапсуляция данных — ограничение прямого доступа
  2. Сепарация concerns — разделение логики данных и представления
  3. Тестируемость — возможность мокать данные для тестов
  4. Производительность — минимизация накладных расходов
  5. Сохранение состояния при перезапусках приложения

Выбор подхода зависит от масштаба проекта: для мобильных игр часто достаточно статических классов, тогда как для крупных PC-проектов требуется полноценная архитектура с DI и State Management.

Как правильно передавать данные между сценами в Unity? | PrepBro