Подтвердить что ты не робот

Почему Unity игнорирует инициализированное значение нестатического публичного поля?

Я использую InvokeRepeating(), чтобы вызвать метод в игре. Я вызываю InvokeRepeating() в методе Start() одного из классов GameObject. Чтобы установить параметр repeatRate для InvokeRepeating(), я передаю ему общедоступное поле под названием secondsBetweenBombDrops.

Unity игнорирует значение, указанное мной для secondsBetweenBombDrops в коде, и вместо этого использует некоторое значение по умолчанию (т.е. 1), когда secondsBetweenBombDrops объявляется без статического модификатора:

public float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

Однако, как только я добавлю модификатор static к secondsBetweenBombDrops, код будет вести себя так, как ожидалось, и используется правильное значение 10:

public static float secondsBetweenBombDrops = 10f;
void Start() {
    InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}

Почему для этого поля требуется модификатор static для использования соответствующего значения?

В инспекторе Unity компонент script показывает, что secondsBetweenBombDrops равно 1. Это значение по умолчанию 1 присутствует независимо от того, создаю ли я экземпляр prefab при запуске игры или создаю экземпляры prefab во время игры.

4b9b3361

Ответ 1

Овальный меч сериализации

Unity хочет облегчить жизнь всем, включая людей с ограниченными знаниями в области кодирования (новички, дизайнеры).

Чтобы помочь им, Unity отображает данные в инспекторе. Это позволяет кодеру кодировать и проектировать дизайн путем настройки значений без открытия MonoDevelop/IDE.

В инспекторе есть два способа отображения значений:

public int myVar = 10;
[SerializeField] private int myOtherVar = 0; // Can also be protected

Второй вариант лучше, поскольку он соответствует принципу инкапсуляции (переменные являются частными/защищенными и модифицируются с помощью методов или свойств).

Когда вы показываете переменную в редакторе, значение, указанное в script, используется только при перетаскивании script. Unity затем сериализует эти значения и не заботится о каких-либо модификациях script. Это может привести к путанице, если, например, myVar будет установлено на 20 внутри script после факта, он не будет использоваться. Сериализация записывается в файл сцены.

Две строки в примере работают точно так же.

Возможные решения

Можно получить Unity для рассмотрения новых значений в script, нажав Reset на колесе настроек компонента script. Это также будет Reset все остальные переменные компонента, поэтому сделайте это только в том случае, если это предназначено.

Приведение переменной private и исключение атрибута [SerializeField] приведет к отключению процесса сериализации, поэтому Unity больше не будет смотреть в файле сцены для отображаемого значения - вместо этого значение будет создаваться во время выполнения с помощью script,

При добавлении компонента в Unity создается новый объект типа компонента. Отображаемые значения представляют собой сериализованные значения этого объекта. По этой причине могут отображаться только значения элементов, а статические переменные - нет, поскольку они не являются сериализуемыми. (Это спецификация .NET, не строго специфичная для Unity.) Поскольку Unity не сериализует статические поля, поэтому добавление static модификатор, казалось, решил проблему.

Объяснение OP

В случае OP, на основе комментариев, ваше публичное поле показывало значение 1 в редакторе. Вы считали, что это значение было по умолчанию, когда оно было фактически значением, которое вы, скорее всего, дали поле, когда оно было первоначально объявлено. После того как вы добавили script в качестве компонента, вы сделали значение 10 и считали, что оно было ошибкой, поскольку оно все еще использует значение 1. Теперь вы должны понимать, что он работает нормально, как было разработано.

Что делает сериал Unity?

По умолчанию Unity будет сериализовывать и отображать типы значений (int, float, enum и т.д.), а также строки, массива, списка и MonoBehaviour. (Можно изменить их внешний вид с помощью сценариев редактора, но это не по теме.)

Следующее:

public class NonMonoBehaviourClass{
   public int myVar;
}

по умолчанию не сериализуется. Здесь снова это спецификация .NET. Unity сериализует MonoBehaviour по умолчанию как часть требования к движку (это сохранит содержимое в файле сцены). Если вы хотите отобразить "классический" класс в редакторе, просто скажите так:

[System.Serializable]
public class NonMonoBehaviourClass{
   public int myVar = 10;
}

Очевидно, вы не можете добавить его в игровой объект, поэтому вам нужно использовать его в MonoBehaviour:

public class MyScript:MonoBehaviour{
     public NonMonoBehaviourClass obj = new NonMonoBehaviourClass();
}

это отображает объект в инспекторе и допускает модификации переменной myVar в экземпляре NonMonoBehaviourClass. И снова любые изменения в myVar в script не будут рассмотрены после того, как значение будет сериализовано и сохранено в сцене.

Дополнительные советы по отображению вещей в инспекторе

Чтобы закончить, интерфейсы не отображаются в инспекторе, так как они не содержат никаких переменных - просто методы и свойства. В режиме отладки свойства не отображаются по умолчанию. Вы можете изменить этот режим, используя кнопку с тремя строками в верхнем правом углу Инспектора. Первые две настройки: Normal/Debug. Первая по умолчанию - вторая, вторая также отображает закрытую переменную. Это полезно для просмотра их значений, но не может быть изменено из редактора.

Итак, если вам нужен интерфейс для отображения, вам нужно будет рассмотреть абстрактный класс, поскольку он предлагает аналогичную функциональность (за исключением множественного наследования), но может быть MonoBehaviour.

Ссылки:

http://docs.unity3d.com/ScriptReference/SerializeField.html

http://docs.unity3d.com/Manual/script-Serialization.html

https://www.youtube.com/watch?v=9gscwiS3xsU

https://www.youtube.com/watch?v=MmUT0ljrHNc