web-dev-qa-db-fra.com

Pourquoi Unity ignore-t-il la valeur initialisée d'un champ public non statique?

J'utilise InvokeRepeating() pour appeler une méthode dans un jeu. J'appelle InvokeRepeating() dans la méthode Start() d'une des classes GameObject. Pour définir le paramètre repeatRate pour InvokeRepeating(), je lui passe un champ public appelé secondsBetweenBombDrops.

Unity ignore la valeur que je spécifie pour secondsBetweenBombDrops dans le code et utilise à la place une valeur par défaut (c'est-à-dire 1) lorsque secondsBetweenBombDrops est déclaré sans modificateur statique:

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

Cependant, une fois que j'ajoute le modificateur static à secondsBetweenBombDrops, le code se comporte comme prévu et la valeur correcte de 10 est utilisée:

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

Pourquoi ce champ nécessite-t-il le modificateur static pour utiliser la valeur appropriée?

Dans l'inspecteur Unity, le composant de script montre que secondsBetweenBombDrops est 1. Cette valeur par défaut de 1 est présente, que j'instancie le préfabriqué au démarrage du jeu ou que je crée des instances préfabriquées pendant le jeu.

37
Yvette Colomb

L'épée à double tranchant de la sérialisation

Unity veut rendre les choses plus faciles pour tout le monde, y compris les personnes ayant une connaissance limitée du codage (débutants, concepteurs).

Pour les aider, Unity affiche les données dans l'inspecteur. Cela permet au codeur de coder et au concepteur de concevoir en ajustant les valeurs sans ouvrir MonoDevelop/un IDE.

Il existe deux façons d'afficher des valeurs dans l'inspecteur:

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

La seconde est meilleure car elle est conforme au principe d'encapsulation (les variables sont privées/protégées et modifiées via des méthodes ou des propriétés).

Lorsque vous affichez une variable dans l'éditeur, la valeur donnée dans le script n'est utilisée que lorsque vous faites glisser le script. Unity sérialise ensuite ces valeurs et ne se soucie plus d'aucune modification de script. Cela peut prêter à confusion si, par exemple, myVar est défini sur 20 dans le script après coup, il ne sera pas utilisé. La sérialisation est écrite dans le fichier de scène.

Les deux lignes de l'exemple fonctionnent exactement de la même manière.

Solutions possibles

Il est possible d'amener Unity à considérer de nouvelles valeurs dans un script en appuyant sur Réinitialiser sur la roue des paramètres du composant de script. Cela réinitialisera également toutes les autres variables du composant, donc ne faites cela que si cela est prévu.

Rendre la variable privée et omettre l'attribut [SerializeField] désactivera le processus de sérialisation, donc Unity ne cherchera plus dans le fichier de scène une valeur à afficher - à la place, la valeur sera créée au moment de l'exécution par le script.

Lors de l'ajout d'un composant à Unity, un nouvel objet du type du composant est créé. Les valeurs affichées sont les valeurs sérialisées de cet objet. Pour cette raison, seules les valeurs des membres peuvent être affichées et les variables statiques ne le sont pas, car elles ne sont pas sérialisables. (Il s'agit d'une spécification .NET, pas strictement spécifique à Unity.) Parce que nity ne sérialise pas les champs statiques , c'est pourquoi l'ajout du modificateur static semble résoudre le problème.

Expliquer l'OP

Dans le cas OP, sur la base des commentaires, votre champ public montrait une valeur de 1 dans l'éditeur. Vous pensiez que cette valeur était une valeur par défaut, alors qu'il s'agissait en fait de la valeur que vous avez probablement donnée au champ lors de sa déclaration initiale. Après avoir ajouté le script en tant que composant, vous avez fait la valeur 10 et pensé qu'il était bogué car il utilisait toujours la valeur de 1. Vous devez maintenant comprendre qu'il fonctionnait très bien, comme prévu.

Qu'est-ce que Unity sérialise?

Par défaut, Unity sérialise et affiche les types de valeurs (int, float, enum, etc.) ainsi que string, array, List et MonoBehaviour. (Il est possible de modifier leur apparence avec les scripts de l'éditeur, mais cela est hors sujet.)

Le suivant:

public class NonMonoBehaviourClass{
   public int myVar;
}

n'est pas sérialisé par défaut. Là encore, il s'agit de la spécification .NET. Unity sérialise MonoBehaviour par défaut dans le cadre des exigences du moteur (cela enregistrera le contenu dans le fichier de scène). Si vous souhaitez afficher une classe "classique" dans l'éditeur, dites-le simplement:

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

De toute évidence, vous ne pouvez pas l'ajouter à un objet de jeu, vous devez donc l'utiliser dans un MonoBehaviour:

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

cela affichera l'objet dans l'inspecteur et permettra des modifications à la variable myVar dans l'instance de NonMonoBehaviourClass. Et encore une fois, toute modification de myVar dans le script ne sera pas prise en compte une fois la valeur sérialisée et stockée dans la scène.

Conseils supplémentaires sur l'affichage des objets dans l'inspecteur

Pour finir, les interfaces ne sont pas non plus affichées dans l'inspecteur car elles ne contiennent aucune variable - juste des méthodes et des propriétés. En mode débogage, les propriétés ne sont pas affichées par défaut. Vous pouvez changer ce mode en utilisant le bouton avec trois lignes dans le coin supérieur droit de l'inspecteur. Les deux premiers paramètres sont Normal/Debug. Le premier est celui par défaut, le second affichera également la variable privée. Ceci est utile pour surveiller leurs valeurs mais ne peut pas être modifié depuis l'éditeur.

Donc, si vous avez besoin d'une interface pour être affichée, vous devrez considérer une classe abstraite car elle offre une fonctionnalité similaire (sauf pour l'héritage multiple) mais peut être un MonoBehaviour.

Les références:

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

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

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

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

38
Everts