Первый скрипт: MonoBehaviour как компонент
Пора оживить объекты кодом. В Unity скрипт — это тоже компонент, и он подчиняется тем же правилам, что и встроенные.
Суть: скрипт на C# становится компонентом, когда его класс наследует
MonoBehaviour. Имя файла должно совпадать с именем класса. Навешиваешь скрипт на объект — и Unity начинает вызывать его методы по своему расписанию.
До этого мы навешивали готовые компоненты. Теперь напишем свой. Создаёшь скрипт (Create -> C# Script), и Unity генерирует заготовку класса. Главное правило: класс наследует MonoBehaviour. Это базовый класс, который превращает обычный C#-класс в компонент Unity.
Второе правило, на котором спотыкаются все новички: имя файла = имя класса. Файл PlayerController.cs должен содержать class PlayerController. Если переименуешь файл, а класс нет — компонент не навесится.
Как работает под капотом
Когда ты добавляешь скрипт на GameObject, Unity создаёт экземпляр твоего класса и привязывает его к объекту. Дальше движок сам вызывает специальные методы в нужные моменты:
Ты создаёшь PlayerController.cs:
class PlayerController : MonoBehaviour
{
void Start() { ... } -> Unity вызовет 1 раз в начале
void Update() { ... } -> Unity вызовет КАЖДЫЙ кадр
}
Навесил на объект "Player" -> объект ожил.Простейший скрипт выглядит так (C#):
using UnityEngine;
public class HelloWorld : MonoBehaviour
{
void Start()
{
Debug.Log("Привет из игры! Объект: " + gameObject.name);
}
void Update()
{
// выполняется каждый кадр
}
}Debug.Log печатает сообщение в окно Console. Это твой главный инструмент отладки на старте: хочешь проверить, что код вообще выполняется — поставь Debug.Log.
Идею «движок дёргает мои методы» легко прочувствовать. Смоделируем мини-движок на Python, который вызывает методы «компонента»:
# Имитируем, как Unity вызывает Start один раз и Update каждый кадр
class PlayerController:
def start(self):
self.frame = 0
print("Start: герой появился")
def update(self):
self.frame += 1
print(f"Update: кадр {self.frame}")
# "движок" Unity
script = PlayerController()
script.start() # вызывается ОДИН раз
for _ in range(3):
script.update() # вызывается каждый кадрТа же логика на Python ▶ — это и есть суть MonoBehaviour: ты пишешь методы, а движок решает, когда их звать. Ты не вызываешь Update сам — это делает Unity 60 раз в секунду.
Частые ошибки
- Имя файла не совпадает с классом. Самая частая ошибка. Unity ругается «can't add script» — проверь, что имена идентичны.
- Забыть унаследовать MonoBehaviour. Без него класс — обычный C#, его нельзя навесить на объект как компонент.
- Вызывать Update вручную. Не надо. Update вызывает движок. Если зовёшь сам — логика выполнится дважды.
Best practices
- Называй классы по их роли:
PlayerMovement,EnemyAI,ScoreManager. - Удаляй пустые
Update()— даже пустой метод движок вызывает каждый кадр, и на тысячах объектов это заметно. - Используй
Debug.Logщедро, пока учишься, но убирай отладочные логи перед сборкой.
Итоги: скрипт становится компонентом, наследуя MonoBehaviour; имя файла обязано совпадать с именем класса. Ты пишешь методы (Start, Update), а движок сам вызывает их по расписанию. Debug.Log — твой главный друг при отладке.
Инверсия управления простыми словами
В привычных программах ты сам пишешь главный цикл и сам решаешь, что когда вызвать. В Unity всё наоборот: главный цикл — внутри движка, а ты лишь подкладываешь методы с правильными именами, и движок зовёт их в нужный момент. Это называется инверсией управления: не ты управляешь движком, а движок управляет твоим кодом. Поэтому методы Start и Update не нужно нигде «подключать» или регистрировать — достаточно правильно их назвать, и Unity сам их найдёт по имени и вызовет. Это поначалу непривычно, зато избавляет от тонн рутинного связующего кода: ты пишешь только саму логику поведения, а оркестрацию берёт на себя движок. Понимание этой инверсии — момент, после которого Unity перестаёт казаться магией.