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

Что такое жизненный цикл MonoBehaviour в Unity?

1.0 Junior🔥 211 комментариев
#Unity Core

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

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

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

# Жизненный цикл MonoBehaviour в Unity

Это один из самых важных концептов в Unity разработке. Неправильное понимание жизненного цикла приводит к багам, утечкам памяти и непредсказуемому поведению. Я объясню каждый этап с примерами.

Визуальное представление

┌─────────────────────────────────────────────────────────┐
│ INSTANTIATE GameObject                                   │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│ Awake() — вызывается ОДИН раз, сразу после создания    │
│ (вне зависимости от enabled состояния)                 │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│ OnEnable() — вызывается когда скрипт/объект включен   │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│ Start() — вызывается один раз перед первым Update()    │
│ (только если скрипт enabled)                           │
└────────────────────┬────────────────────────────────────┘
                     │
        ┌────────────┴────────────┐
        │                         │
        ▼                         ▼
  ┌──────────────┐      ┌─────────────────┐
  │ FixedUpdate()│ →    │ Update()        │
  │ (Physics)    │      │ (Input, Logic)  │
  └──────────────┘      └─────────────────┘
        │                         │
        │                         │
        └────────────┬────────────┘
                     │
                     ▼
        ┌─────────────────────────┐
        │ LateUpdate()            │
        │ (Камера, animation)     │
        └────────────┬────────────┘
                     │
        ┌────────────┴────────────┐
        │  Повторяется каждый     │
        │  frame пока enabled     │
        └────────────┬────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│ OnDisable() — вызывается когда скрипт/объект отключен │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│ OnDestroy() — вызывается перед удалением объекта      │
└─────────────────────────────────────────────────────────┘

Подробный разбор каждого этапа

1. Awake()

Когда вызывается: Сразу после создания объекта (instantiate), до включения скрипта

Используется для:

  • Инициализация references между скриптами
  • Настройка initial state
  • Dependency injection
public class Player : MonoBehaviour {
    private GameManager gameManager;
    
    private void Awake() {
        // Вызывается ДО Start(), даже если объект disabled
        gameManager = FindObjectOfType<GameManager>();
        
        // Инициализация связей между объектами
        var weaponSystem = GetComponent<WeaponSystem>();
        if (weaponSystem != null) {
            weaponSystem.InitializePlayer(this);
        }
    }
}

Важное: Если объект создан disabled, Awake() всё равно вызовется.

var obj = Instantiate(prefab);
obj.SetActive(false);
// Awake() уже вызвалась выше!

2. OnEnable()

Когда вызывается: Каждый раз, когда скрипт или gameObject становится активным

Вызывается:

  • При instantiate (если был active)
  • При SetActive(true)
  • При загрузке сцены
public class EventListener : MonoBehaviour {
    private void OnEnable() {
        // Подписываемся на события
        EventBus.OnPlayerDamaged += HandleDamage;
        GameManager.OnGamePaused += HandlePause;
        
        // Возобновляем логику
        isListening = true;
    }
    
    private void OnDisable() {
        // Отписываемся (ВАЖНО!)
        EventBus.OnPlayerDamaged -= HandleDamage;
        GameManager.OnGamePaused -= HandlePause;
        
        isListening = false;
    }
}

3. Start()

Когда вызывается: Перед первым вызовом Update(), но только если скрипт enabled

Отличие от Awake: Используется для логики, которая должна выполниться после того, как инициализировались другие скрипты

public class Enemy : MonoBehaviour {
    private Player player;
    private Rigidbody rb;
    
    private void Awake() {
        // Инициализация компонентов
        rb = GetComponent<Rigidbody>();
    }
    
    private void Start() {
        // Используем то, что инициализировалось в других Awake()
        player = FindObjectOfType<Player>();
        
        // Начальные вычисления
        var distanceToPlayer = Vector3.Distance(transform.position, player.transform.position);
        if (distanceToPlayer < attackRange) {
            StartAttack();
        }
    }
}

4. Update()

Когда вызывается: Один раз в frame (зависит от frameRate)

Используется для:

  • Input обработка
  • Movement логика
  • Animation обновления
  • Game logic
public class PlayerController : MonoBehaviour {
    private void Update() {
        // Чтение input
        float moveX = Input.GetAxis("Horizontal");
        float moveY = Input.GetAxis("Vertical");
        
        // Game logic
        var moveDirection = new Vector3(moveX, 0, moveY);
        transform.Translate(moveDirection * moveSpeed * Time.deltaTime);
        
        // Спец клавиши
        if (Input.GetKeyDown(KeyCode.Space)) {
            Jump();
        }
    }
}

Важно: Используй Time.deltaTime для frame rate independence

5. LateUpdate()

Когда вызывается: После всех Update() вызовов, но в том же frame

Используется для:

  • Следящая камера (camera follow за player'ом)
  • Animation обновления (после physics)
  • Очистка временных данных
public class CameraFollow : MonoBehaviour {
    private Player player;
    
    private void LateUpdate() {
        // LateUpdate вызывается ПОСЛЕ того, как player переместился в Update()
        // Поэтому камера будет смотреть на актуальную позицию
        transform.position = new Vector3(
            player.transform.position.x,
            player.transform.position.y + 2,
            player.transform.position.z - 5
        );
    }
}

6. FixedUpdate()

Когда вызывается: С фиксированным временным интервалом (обычно 0.02 сек = 50x в сек)

Используется для:

  • Physics расчёты (работа с Rigidbody)
  • Может вызваться несколько раз в frame или ни разу, если frameRate низкий
public class PhysicsController : MonoBehaviour {
    private Rigidbody rb;
    
    private void FixedUpdate() {
        // Physics обновления всегда идут с одной скоростью
        float horizontalInput = Input.GetAxis("Horizontal");
        
        // Применяем силу
        var force = new Vector3(horizontalInput, 0, 0) * moveForce;
        rb.AddForce(force, ForceMode.Force);
    }
}

Критическая ошибка:

// ❌ НЕПРАВИЛЬНО
private void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        rb.velocity = new Vector3(0, jumpForce, 0); // Physics в Update()
    }
}

// ✅ ПРАВИЛЬНО
private void FixedUpdate() {
    if (shouldJump) {
        rb.velocity = new Vector3(0, jumpForce, 0); // Physics в FixedUpdate()
    }
}

private void Update() {
    if (Input.GetKeyDown(KeyCode.Space)) {
        shouldJump = true; // Флаг, используемый в FixedUpdate()
    }
}

7. OnDisable()

Когда вызывается: Когда скрипт/объект деактивируется или удаляется

Используется для:

  • Очистка подписок на события (ОБЯЗАТЕЛЬНО!)
  • Отмена корутин
  • Сохранение состояния
private void OnDisable() {
    // ❌ Утечка памяти если не отписать
    EventBus.OnEvent -= Handler;
    
    // Отмена корутин
    StopAllCoroutines();
    
    // Очистка
    if (audioSource != null) {
        audioSource.Stop();
    }
}

8. OnDestroy()

Когда вызывается: Непосредственно перед удалением объекта

private void OnDestroy() {
    // Очистка ресурсов
    if (spawnedObject != null) {
        Destroy(spawnedObject);
    }
    
    // Логирование для отладки
    Debug.Log($"Enemy destroyed at {Time.time}");
}

Практический пример с полным жизненным циклом

public class Enemy : MonoBehaviour {
    private Health health;
    private Rigidbody rb;
    private bool isAlive = true;
    
    private void Awake() {
        Debug.Log("1. Awake - Инициализация компонентов");
        health = GetComponent<Health>();
        rb = GetComponent<Rigidbody>();
    }
    
    private void OnEnable() {
        Debug.Log("2. OnEnable - Подписка на события");
        health.OnDeath += HandleDeath;
    }
    
    private void Start() {
        Debug.Log("3. Start - Начало логики");
    }
    
    private void FixedUpdate() {
        if (isAlive) {
            // Physics логика
        }
    }
    
    private void Update() {
        if (isAlive) {
            Debug.Log("4. Update - Главное обновление логики");
        }
    }
    
    private void LateUpdate() {
        Debug.Log("5. LateUpdate - Финальные обновления");
    }
    
    private void OnDisable() {
        Debug.Log("6. OnDisable - Отписка");
        health.OnDeath -= HandleDeath;
    }
    
    private void OnDestroy() {
        Debug.Log("7. OnDestroy - Финальная очистка");
    }
    
    private void HandleDeath() {
        isAlive = false;
        Destroy(gameObject);
    }
}

Частые ошибки

Ошибка 1: Утечки памяти от событий

// ❌ УТЕЧКА
private void OnEnable() {
    EventBus.OnEvent += Handler; // Подписка
}
// OnDisable() не вызывается = утечка

// ✅ ПРАВИЛЬНО
private void OnEnable() {
    EventBus.OnEvent += Handler;
}

private void OnDisable() {
    EventBus.OnEvent -= Handler; // ВСЕГДА отписывай
}

Ошибка 2: Physics в Update() вместо FixedUpdate()

// ❌ НЕПРАВИЛЬНО
private void Update() {
    rb.AddForce(force); // Unpredictable physics
}

// ✅ ПРАВИЛЬНО
private void FixedUpdate() {
    rb.AddForce(force); // Consistent physics
}

Ошибка 3: FindObjectOfType в Update()

// ❌ МЕДЛЕННО
private void Update() {
    var player = FindObjectOfType<Player>(); // Дорого
    MoveTowards(player.position);
}

// ✅ ПРАВИЛЬНО
private void Start() {
    player = FindObjectOfType<Player>(); // Один раз в Start()
}

private void Update() {
    MoveTowards(player.position);
}

Итоговая диаграмма вызовов

Инстанцирование:
├─ Awake() → [один раз]
├─ OnEnable()
├─ Start() → [один раз, перед первым Update]
└─ Начинается loop:
   ├─ FixedUpdate() × N (physics iterations)
   ├─ Update()
   ├─ LateUpdate()
   └─ Повтор из loop

Деактивация:
└─ OnDisable()

Удаление:
└─ OnDestroy()

Понимание этого жизненного цикла — это основа правильной разработки на Unity. Неправильное использование приводит к ошибкам, которые сложно отловить.

Что такое жизненный цикл MonoBehaviour в Unity? | PrepBro