Поля в Inspector и публичные переменные

Магия Unity: ты пишешь переменную в коде, а настраиваешь её мышкой в Inspector. Никакой перекомпиляции — меняй на ходу.

Суть: поля скрипта, помеченные public или атрибутом [SerializeField], появляются в Inspector. Их можно менять прямо в редакторе, в том числе во время игры, без правки кода. Так настройки отделяют от логики.

Допустим, ты задал скорость героя числом 5 прямо в коде. Захотел 7 — лезь в код, меняй, жди компиляции. Неудобно. Unity предлагает лучше: объяви public float speed = 5f; — и поле speed появится в Inspector. Теперь правишь скорость мышкой, даже на ходу, и сразу видишь результат.

Это разделяет роли: программист пишет логику, дизайнер крутит цифры. В команде это бесценно. Даже если ты один — подбирать баланс мышкой в разы быстрее.

Как работает под капотом

Unity сериализует такие поля — сохраняет их значения вместе с объектом или префабом. Поэтому значение из Inspector переживает перезапуск.

Скрипт:                       Inspector (Player):
  public float speed = 5;  ->  Speed     [ 5 ]
  public int   hp    = 100;->  Hp        [ 100 ]
  [SerializeField]
  private int  ammo  = 30; ->  Ammo      [ 30 ]   (приватное, но видно)

  private int  secret = 1; ->  (НЕ показывается в Inspector)

Важная тонкость: делать поле public только ради Inspector — плохо, потому что тогда любой другой скрипт может его менять. Правильнее [SerializeField] private — поле видно в Inspector, но снаружи защищено. Это лучшая практика 2024-2025.

Полезные атрибуты: [Header("Движение")] добавляет заголовок-группу, [Range(0, 10)] делает ползунок, [Tooltip("...")] — подсказку.

using UnityEngine;

public class PlayerStats : MonoBehaviour
{
    [Header("Движение")]
    [SerializeField] private float speed = 5f;

    [Header("Здоровье")]
    [Range(1, 200)]
    [SerializeField] private int maxHp = 100;

    void Start()
    {
        Debug.Log($"speed={speed}, maxHp={maxHp}");
    }
}

Идею «настройка отдельно от логики» смоделируем на Python — конфиг и поведение разделены:

# Настройки (как поля в Inspector) отдельно от логики
config = {"speed": 5.0, "max_hp": 100}

def move(x, config):
    return x + config["speed"]

x = 0.0
for _ in range(3):
    x = move(x, config)
print("При speed=5 прошли:", x)

# Дизайнер поменял "в инспекторе" скорость — логику не трогали
config["speed"] = 8.0
x = 0.0
for _ in range(3):
    x = move(x, config)
print("При speed=8 прошли:", x)

Та же логика на Python ▶ — меняя только конфиг, мы поменяли поведение, не трогая код движения. Это и есть смысл полей в Inspector.

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

  • Всё подряд делать public. Это ломает инкапсуляцию: любой скрипт может испортить твои значения. Используй [SerializeField] private.
  • Менять значение и в коде, и в Inspector. Значение из кода-инициализатора перезаписывается тем, что в Inspector. Сюрприз: «почему мои 5 стали 12?».
  • Думать, что правки в Play сохранятся. Покрутил поле во время игры — после Stop откатится. Запиши понравившееся значение.

Best practices

  • По умолчанию — [SerializeField] private; public только когда поле действительно нужно другим скриптам.
  • Группируй поля через [Header] и ограничивай ползунками [Range] — Inspector станет аккуратным и защищённым от глупых значений.
  • Ссылки на другие компоненты тоже делай полями и перетаскивай в Inspector — быстрее, чем искать в коде.

Итоги: публичные и [SerializeField]-поля видны в Inspector и настраиваются мышкой без правки кода. Это отделяет настройки от логики. Предпочитай [SerializeField] private ради безопасности, группируй поля атрибутами и помни: правки в режиме Play не сохраняются.

ScriptableObject: настройки, живущие отдельно от сцены

Поля в Inspector — это первый шаг к разделению данных и логики. Следующий уровень той же идеи — ScriptableObject: ассет-контейнер настроек, который лежит в проекте отдельным файлом, а не висит на объекте сцены. В него удобно складывать, например, характеристики всех типов врагов или баланс оружия: один файл на тип, и любой объект ссылается на него. Тогда правка баланса — это правка одного ассета, а не охота за значениями по десяткам префабов. Пока тебе хватит обычных сериализованных полей, но держи в голове, что Unity предлагает целую лесенку инструментов для отделения настроек от кода — от public-поля до ScriptableObject. Это та самая зрелая архитектура, к которой приходят все, кто делает игру чуть больше одного экрана.

Проверьте себя
1. Какой способ показать приватное поле в Inspector считается лучшей практикой?
AСделать поле public
BИспользовать [SerializeField] private
CСделать поле static
DНикак, приватные поля не показать
2. Что нужно делать с переменными движения в Inspector?
AОни автоматически меняются по FPS
BМожно настраивать мышкой без правки кода, даже на ходу
CИх нельзя менять после компиляции
DОни видны только при сборке