Поля в 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. Это та самая зрелая архитектура, к которой приходят все, кто делает игру чуть больше одного экрана.