Go to Top

Cet article est la traduction de l'article "Common Gotchas" paru sur ce même site en Anglais. L'informatique est un domaine international où l'Anglais domine (le langage, pas le gars bien sur...). De ce fait, certains mots seront laissés en Anglais, pas les plus compliqués, promis.

Il existe un certain nombre de "pièges" qui ont frappé presque tous les débutants de Unity. Parfois, cela est dû au fait que vous débutez la programmation en général mais c'est parfois dû au fait que Unity utilise une méthode légèrement inhabituelle d'attacher plusieurs scripts sur un objet.
Cet aperçu tente de vous guider à travers ceux que nous voyons le plus souvent sur le forum de réponses Unity. Ce tutoriel s'adresse donc aux nouveaux venus, les noobs.

Les Traquenards


Comment accéder à une variable d'un autre script ou d'un autre objet

Comment accéder à une variable d'un autre script ou d'un autre objet

Vous pouvez trouver une version plus détaillée sur ce sujet en C# ou en UnityScript (seulement en Anglais pour le moment...)

Donc, vous avez préparé deux scripts et vous voulez accéder à des variables ou des méthodes d' un script à partir de l'autre, mais obtenir une référence au script ou comment obtenir la variable reste un mystère. Appelons le script qui veut accéder à la variable le caller (appeleur) et le script qui contient la variable ou la méthode de la target (cible).

Vous pourriez être tenté de déclarer des variables comme static afin de pouvoir y accéder facilement sans avoir à réellement faire référence à l'objet sur lequel le script est placé. C'est une très mauvaise idée, sauf si vous savez ce que vous faites. Si vous êtes nouveau à la programmation, éviter le procédé static jusqu'à ce que vous le compreniez parfaitement. Les variables statiques sont des variables globales et ne devrait être utilisée que dans des scénarios rares et assez avancé. En fait, vous pouvez facilement programmer un jeu compliqué et ne jamais définir une variable comme statique.
Sur le forum de réponses Unity, on voit souvent des questions du genre - quand un de mes ennemi meurt, ils meurent tous. Ceci est normalement causée par les nouveaux développeurs (noobies) qui utilisent des variables statiques, car ils ne pouvaient se faire une idée sur comment accéder d'une autre manière à la donnée dont ils ont été besoin. Si cela s'applique à vous, lisez la suite!

Pour obtenir le script target, nous devons trouver  l'objet auquel il est attaché. Il existe cinq manières:

1. Le script target est attaché a même object que le script caller.

Dans ce cas, vous devez placer un appel à la fonction GetComponent, qui retourne une reference au script target attaché au même object.   GetComponent sans rien devant cherche dans l'objet.

Avec UnityScript, on utilise GetComponent de cette manière:

GetComponent(ScriptTarget).uneVariable = uneValeur;
GetComponent(ScriptTarget).UneMethode();

Avec C#:

GetComponent<ScriptTarget>().uneVariable = uneValeur;
GetComponent<ScriptTarget>().UneMethode();
Evitez d'utiliser GetComponent avec une string.  Avec un appel de GetComponent sans une string la valeur retournée est déjà du bon type et vous pouvez accéder à vos variables et méthodes. Si vous appelez GetComponent ("ScriptTarget"), vous obtiendrez votre script, mais d'un le processus est plus lent et de deux il se peut que certaines variables ne soit pas accessibles puisque le compilateur ne connait pas la nature exact du script. Sans plus d'indications, faîtes simples, pas de  guillemets. 

2. Vous voulez accéder un script target script attaché à un autre objet parce que vous venez d'entrer en collision avec ce dernier, ou invoqué une trigger sur le script caller.

Dans ce cas, votre méthode devrait déjà inclure une référence à l'autre objet soit en tant que Collider pour OnTriggerXXX ou en tant que Collision pour OnCollisionXXX.

Dans ce cas, il vous est seulement nécessaire d'appeler GetComponent sur la référence.

Avec UnityScript:

function OnTriggerEnter(reference : Collider) 
{ 
      reference.GetComponent(ScriptTarget).uneVariable = uneValeur; 
}

Avec C#

void OnTriggerEnter(Collider reference)
{
      reference.GetComponent<ScriptTarget>().uneVariable = une Valeur;
}

Dans une collision, vous avez juste besoin d'obtenir quelque chose qui représente l'autre objet et appeler GetComponent dessus:

function OnCollisionEnter(reference : Collision)
{
       reference.collider.GetComponent(ScriptTarget).uneVariable = uneValeur;
}

Avec C#:

Or in C#

void OnCollisionEnter(Collision reference)
{
      reference.GetComponent<ScriptTarget>().uneVariable = uneValeur;
}

If for some reason you want to set a variable on some child of the object you triggered or collided with then you can use GetComponentInChildren.

Si pour une quelconque raison, vous souhaitez modifer une variable se trouvant sur un child de l'objet vous venez d'entrer en collision, vous pouvez utiliser GetComponentInChildren.

GetComponentInChildren cherche aussi dans l'objet sur lequel l'appel est fait.

3.  Vous avez une relation entre deux objets que vous pouvez créer en utilisant l'éditeur ou définir à l'aide de script lorsque la relation est créée

Donc dans ce cas vous avez une relation entre deux objets, non pas parce qu'ils sont en train d'interagir, mais plutôt parce qu'ils ont une autre relation à long terme. Par exemple, un ennemi peut courir après le joueur, un joueur peut avoir «verrouillé» une arme sur un ennemi, ou peut-être le joueur est actuellement dans ou sur  un certain véhicule.

Dans ce cas, vous voulez avoir une variable dans votre script caller qui fait référence à votre script target. Le plus simple est de définir cette variable comme étant de type NomScriptTarget,cela peut aussi être un autre type de variable référence comme GameObject ou Transform.

Soyez conscient que l'appel de GetComponent n'est pas «gratuit» en terme de performance. Si possible essayez de placer l'appel avant le début de la scène (Awake ou Start) . Par la suite la variable référence sera placé en cache ce qui optimize les appels vers les variables cibles.

You will assign the variable using the inspector if the relationship can be created at editing time - or if you assign it at runtime you will use either method 2 (above) or method 4 (below) to get the component and set up the variable.

Vous pouvez assigner la variable dans l'inspecteur si la relation peut être créé au moment de l'édition - ou si vous l'assignez pendant l'exécution, utilisez soit la méthode 2 (ci-dessus) ou la méthode 4 (ci-dessous) pour obtenir le composant et définir la variable.

Voici un exemple de création d'une relation à long terme à partir d'une trigger:

var targetScript : TargetScript;       //Le script est directement placé dans l'emplacement de l'inspecteur 

function Update(){
     //Si nous avons une target et elle est prête à être utilisée
     if(targetScript && targetScript.uneVariable > 100){
         //Action
     }else{
         //Une autre action
     }
}

function OnTriggerEnter(other : Collider){
     //Vérifions si l'objet avec lequel nous entrons en collision peut être une target
     var target = other.GetComponent(ScriptTarget);
     if(target){
           //Si possible 
           targetScript = target;
     }
}

4. Vous pouvez trouver l'objet qui contient la target  en cherchant par nom ou tag

C'est de loin le moyen le plus couteux en terme de performance. Il est hautement recommandé de faire la rechercher si possible dans Awake ou Start. Pensez que la fonction Find parcours l'ensemble de la Hierarchie, si votre scène inclut 1000 objets, cela peut prendre du temps.

Une fois l'objet trouvé, on retourne à la méthode 2 avec GetComponent.

Il est possible de trouver un objet de différentes manières - avec GameObject.FindWithTag  ou GameObject.Find pour chercher l'inégralité de la scène.  Ou bien transform.Find pour chercher seulement dans la hierarchie de l'objet en question et limiter la recherche à ses children. Peu importe le processus,  la methode 2 revient pour accéder au script dont vous avez besoin. Vous pouvex tout autant cacher le résultat en le storant dans une variable pour obtenir une relation à long terms et utiliser la methode 3 pour manipuler l'objet.

TargetScriptName targetScript;

void Start()
{
       targetScript = GameObject.Find("NomObjet").GetComponent<TargetScriptName>();
}

void Update()
{
      if(Vector3.Distance(targetScript.transform.position, transform.position) < 10)
      {
          targetScript.uneVariable -= uneValeur;
      }
}

5. Vous avez un autre composant sur l'objet mais ce n'est pas celui avec le script que vous voulez

Ceci arrive souvent quand vous traversez la hierarchie ou vous avez un autre script sur le même objet. En quelque sorte, on revient à la methode 2.

Par exemple, vous souhaitez accéder un script contenu dans chacun des children de votre caller:

for(var t : Transform in transform)
{
     target = t.GetComponent(TargetScriptName);
     target.DoSomething();
     target.someVariable = someValue;
}

en C#:

foreach (Transform t : in transform)
{
     target = t.GetComponent<TargetScriptName>();
     target.Action();
     target.uneVariable = uneValeur;
}

Vous pouvez regarder la vidéo démonstrative ci-dessous (en Anglais pour le moment)


Rigidbody, à quoi ça sert?

Rigidbody, à quoi ça sert?

Donc vous venez de passer les 3 derniers jours sans dormir à essayer de faire en sorte que votre objet ait un semblant de réalisme. Alors vous faites la tournée des sites de physique pour comprendre les principes de gravité, friction, collision et autres. Puis vous entendez parler du Rigidbody et sentez une montée de sueur. Et là vous vous dites, "Non vas-y quoi!!!".

Le Rigidbody est un composant qui vous permet d'appliquer les principes fondamentaux de physique à votre objet sans rien faire (ou presque).

Maintenant comment l'utiliser? Pour cela, je vous suggère de télécharger ce projet et vous pouvez trouver les explication sur cette vidéo en fr:


Comment s'assurer que les fonctions OnCollisionXXX et OnTriggerXXX sont appelées dans votre script.

Comment s'assurer que les fonctions OnCollisionXXX et OnTriggerXXX sont appelées dans votre script.

Tout d'abord pour détecter une collision, les deux objets doivent avoir un composant collider attaché. Pour que OnCollisioXXX fonctionne, isTrigger ne doit pas être sélectionné, en contrepartie pour que OnTriggerXXX fonctionne, il vous faut sélectionner isTrigger. Vous devez aussi suivre les règles suivantes:

L'objet qui contient le script avec OnCollisionXXX, OnTriggerXXX doit avoir un composant rigidbody attaché.

Avec Unity, la détection des collisions fait partie du système de physique.  Si vous ne souhaitez pas appliquer de physique sur votre objet, vous pouvez sélectionner isKinematic. Ainsi, Unity détectera la collision mais n'appliquera aucune résolution ou interaction. 

Au moins un des partis doit avoir un rigidbody qui ne soit pas endormi

Dans un souci d'optimisation, Unity place automatiquement en sommeil les rigidbodys qui ne bougent pas. Une collision avec un autre rigidbody le réveillera, considérant que ce rigidbody n'est pas lui même endormi. Dans la cas où chaque parti est endormi, la collision est simplement ignorée.

Attachez un rigidbody à chaque collider vous créez ou bougez si votre objet n'en contient pas déjà un.

D'autres importantes considérations

Un Mesh Collider peut entrer en collision avec  un autre mesh collider si ils sont tous les deux définis comme convex. POur une collision avec un mesh concave, alors l'autre parti doit être un  collider primitif (box collider, sphere collider,...). Voir ci-dessous.
Les mesh colliders n'ont qu'un seul coté, ils entrent en collision seulement du coté comportant la normal pointant vers l'exterieur.
Les mesh colliders sont sujets au back face culling. Chaque polygone de votre jeu comporte une normal sur une seule face. Par souci d'optimisation, l'autre face est ignorée. Il en va de même avec les mesh colliders. Une seule face peut détecter une collision.

Vous pouvez construire des colliders à partor de colliders primitifs. Il est ainsi possible de reproduire des objets complexes sans pour autant puiser dans les resources. Il vous suffit d'attacher des game objects sur l'objet et attacher les colliders sur ces objets. Le composant rigidbody est attaché au parent (root). Un voiture qui devrait comporter des centaines voire des milliers de polygones peut être réduite à quatre colliders pour les roues et deux larges colliders pour les parties hautes et basses.


Détecter l'Input convenablement

Détecter l'Input convenablement

Les débutants finissent souvent devant leur programme à se demander pourquoi la pression d'une touche est parfois non détectée. Ils ont tout bien fait en suivant la documentation à la lettre (du moins c'est ce qu'ils croient) mais ils ont le sentiment que certaines fois rien ne se passe. La plupart du temps, ce genre de problème survient lors du dévelopement de la partie physique du jeu via l'Input. Puisque la physique se passe dans la FixedUpdate, il serait logique que l'INput en rapport avec la physique y soit aussi. Beh non...

La fonction Update est dépendante de la machine sur laquelle le programme fonctionne. Cela signifie donc que chaque ordinateur peut avoir un FPS (Frame Per Second, combien de fois chaque script est pris en charge par seconde) différent en fonction du processeur et des différents programmes qui tournent.

La fonction FixedUpdate est définie par l'utilisateur. Il vous est possible de modifer sa valeur via le Physics Manager sur le menu, Edit->Project Settings->Physics. La valeur par défaut est 0.02 qui représente 50fps. Une valeur plus petite demandera au système de tourner plus vite. Notez que la valeur est une requête mais peut ne pas être garantis si vous demandez une valeur que le processeur ne peut fournir.

Update et FixedUpdate tourne indépendamment, cela signifie que pour une Update, on peut avoir une, plusieurs ou aucune FixedUpdate. Si votre ordinateur tourne à 100fps mais FixedUpdate est définie à 50fps, pour deux Updates, on a seulement une FixedUpdate.

InputFixed

Voici ce qu'il ne fait pas faire:

using UnityEngine;

public class Test:MonoBehaviour{
    void FixedUpdate(){
        if(Input.GetKeyDown(KeyCode.Space)){
           //Action
        }
    }
}

Ce que vous devez faire:

using UnityEngine;

public class Test:MonoBehaviour{
    bool action = false;
    void Update(){
         if(Input.GetKeyDown(KeyCode.Space)){
             action = true;
         }
    }
    void FixedUpdate(){
       if(action){
           //Action
           action = false;
        }
    }
}

L'Input dans l'Update est détectée chaque frame ce qui garantit qu'aucune Input ne peut être ignorée. Une variable globale est modifiée pour indiquer à la FixedUpdate qu'une Input a été détectée. FixedUpdate utilise cette variable et la définit à nouveau comme false de manière à ce que l'action ne se répète pas indéfiniment.


Action dans le futur

Action dans le futur

Les coroutines sont un sujet particulièrement pointilleux pour faire que quelque chose arrive plus tard. Il est préférable de les oublier pour le moment surtout si vous débutez avec Unity. Voyons plutôt ce que nous pouvons faire

Juste pour le principe, voici un minuteur (timer) par code:

float timer;
int attente;

void Update(){
    timer += Time.deltaTime;
    if(timer > attente){
        //Action
       timer = 0;
    }
}

La variable timer croît chaque frame en ajoutant deltaTime (le temps écoulé depuis le dernier frame). La variable est comparée cahque frame avec attente. Quand timer devient plus grand que attente, la déclaration est vraie, l'action est lancée et timer est remis à zéro. Cerner le timer avec un boolean permet de lancer le timer sous une certaine condition:

using UnityEngine;
using System.Collections;
public class Test:MonoBehaviour{
    float timer;       
    int attente;
    bool inside;

    void Start(){
        timer = 0.0;
        attente = 2;
        inside = false;
    }

    void Update(){
        if(inside){
            timer += Time.deltaTime;
            if(timer > attente){
                //Action
               timer = 0;
           }
       }
    }

    void OnTriggerEnter(Collider other){
        if (other.gameObject.tag=="Joueur")inside = true;
    }
   void OnTriggerExit(Collider other){
        if (other.gameObject.tag =="Joueur"){
            inside=false;
            timer = 0;
        }
   }
}

L'exemple ci-dessus montre comment créer un minuteur pour lancer une action tant que le joueur reste dans la zone trigger. Par exemple, si le joueur marche sur du feu, si nous blessons le joueur à chaque frame, il va vite se retrouver à 0. Avec un minuteur, on peut le blesser chaque x secondes.

Cependant, Unity offre de meilleures solutio.

Limiter le temps de vie d'un object

Si vous souhaitez qu'un object n'apparaisse que pour une période déterminée de quelques secondes, vous pouvez le detruire dans la fonction Start. Destroy prend un second paramètre qui correspond à la durée de vie avant la mort définitive (...).

void Start()
{
       //Détruire l'objet après 5 secondes
       Destroy(gameObject, 5);
}

Il est ainsi possible de garder un objet pour une durée déterminée avant de la faire disparaître:

int vie = 10;

void Update(){
       if(vie <= 0)
            //Détruire l'objet après 2 secondes
            Destroy(gameObject, 2);
}

Une fois l'objet mort par le manque de vie (oh la la...), il meurt une deuxième fois après 2 secondes. Ce procédé est souvent utilisé, un personnage est tué mais son corps reste sur le sol pour une durée.

Retarder une action

Vous voulez qu'une action se fasse et une autre action d'attendre un certain temps avant de se faire, la première étant décisive. UnityScript et C# offrent des méthodes différentes pour le même résultat. dans les examples ci-dessous, la variable check est nécessaire pour entrer dans la commande. En entrant, check devient false, après 2 secondes elle redevient true.

UnityScript:

#pragma strict
var check :boolean =true;
var i:int =0;

function Update () {
    if(Input.GetKeyDown(KeyCode.A)&&check){
        check = false;
        print("Dedans" + i++);
        WaitForIt();
    }
}
function WaitForIt(){
    yield WaitForSeconds(2.0f);
    check=true;
}

C#:

using UnityEngine;
using System.Collections;

public class Wait : MonoBehaviour {
    public bool check =true;
    int i =0;

    void Update () {
        if(Input.GetKeyDown(KeyCode.A)&&check){
            check = false;
            print("Inside" + i++);
            StartCoroutine(WaitForIt());
        }
    }
    IEnumerator WaitForIt(){
        yield return new WaitForSeconds(2.0f);
        check=true;
    }
}

Notez le type que renvoie la fonction, IEnumerator Ce type est utilisé pour toutes les fonctions qui sont mise en attente par le système. dans notre cas WaitForIt est lancée une première fois, yield return WaitForSeconds(2.0f); est retournée. Le système met la fonction dans une liste. Au frame suivant, le système soustarait le temps écoulé depuis le dernier frame et verifie si le temps correspondant au paramètre s'est écoulé depuis le premier appel. Et ainsi de suite.Notez aussi la manière d'appeler une coroutine en C#, Unity nécessite l'utilisation de StartCoroutine(Fonction());.

Si vous lancez ces deux scripts, vous verrez que vous pouvez appuyer sur A et la console imprime, il vous faudra attendre 2 secondes avant que l'action ne puisse se répéter.

Faire quelque chose dans quelques secondes

Si vous voulez que quelque chose se fasse quelques secondes après une autre, vous pouvez simplement utiliser Invoke. Vous déclarez la fonction vous souhaitez utiliser et vous l'invoquez ainsi Invoke("NomFonction",retardEnSeconde); Dans l'exemple ci-dessous, la fonction TurnMeBlue est appelée après 2 secondes.

UnityScript:

function Start(){
      //Change l'object en bleu dans 2 sec
      Invoke("TurnMeBlue", 2);
}

void TurnMeBlue(){
       renderer.material.color = Color.blue;
}

Vous pouvez même faire plusieurs invocations:

var startPosition : Vector3;
var health = 100.0;
var respawning = false;

function Start(){
    startPosition = transform.position;
}

//Repositione l'objet
function Respawn(){
      transform.position = startPosition;
      health = 100;
      renderer.enabled = true;
      respawning = false;
}

//Cacher le personnage
function Hide(){
     renderer.enabled = false;
     transform.position = new Vector3(1000,1000,1000);
}

function Update(){
    if(health < 0 && !respawning){
         respawning = true;
         animation.Play("die");
         Invoke("Hide", 3);
         Invoke("Respawn", 10);
    }
}

Ce script utilise Invoke pour appeler une animatin de mort et ensuite replace le personnage. Quand la vie descend en dessous de 0, l'animation "die" est jouée. Le modèle est caché (et retiré du champs) 3 secondes aprés et réapparait après 10 secondes.

Faire une action régulièrement

Au lieu de déclarer une fonction et la placer dans une boucle avec un minuteur, on peut simplement utiliser la focntion InvokeRepeating("NomFonction", retardPremierAppel,frequenceAppelSuivant);

Il est possible d'annuler la répétition avec CancelInvoke("NomeFonction"); pour annuler un appel en particulier ou sans paramètre pour annuler tous les appels.

float sante = 100f;

void Start()
{
    InvokeRepeating("Soigne", 2, 2);
}

void Soigne()
{
    sante = Mathf.Clamp(health + 1, 0, 100);
}
Au moment de l'écriture de cet article, InvokeRepeating comporte un bug mineur. Il est tentant de placer 0 comme second paramètre pour lancer le premier appel desuite. Or, cette situation lancera deux appels de la même fonction lors du premier appel. Pour y remédier, il vous suffit de passer une valeur minime.

InvokeRepeating("Fonction",0,001f,2.0f);

Quand utiliser une coroutine

Une coroutine peut s'avérer utile lorsque vous avez besoin de lancer une action qui devrait être placée dans l'Update mais vous ne le voulez pas pour simplifier la chose.

Soyez conscient que lancer plusieurs coroutines depuis l'Update peut altérer la même variable depuis différentes sources. Debugging devient alors fastidieux...

Par example, il se pourrait que vous vouliez changer la rotation d'un objet dans une fonction, dela peut se faire avec une coroutine. Dans l'example suuivant, l'appel de TourneToi change la rotation de l'objet avec un angle sur une période de temps.

function OnMouseUp()
{
     TournToi(Vector3(0,90,0),2);
}

function TourneToi(angle : Vector3, time : float)
{
     var maRotation = transform.rotation;
     var targetAngle = currentRotation.eulerAngles + angle;
     var targetRotation = Quaternion.Euler(targetAngle);
     var t = 0;
     while(t < 1)
     {
          transform.rotation = Quaternion.Slerp(maRotation, targetRotation, t);
          t += Time.deltaTime / time;
          yield null;
     } 
     transform.rotation = targetRotation;
}

Avec UnityScript, une fonction devient une coroutine en ajoutant le mot-clé yield. Malheureux, jamais ôh grand jamais de faire ça dans l'Update.

La vidéo ci-dessous montre ce qui vient d'être expliqué par l'exemple. Malheureusement, c'est encore en Anglais.


Comment utiliser Vector3.Lerp

Comment utiliser Vector3.Lerp

Vous avez défini votre ligne de code mais pour une quelconque rien ne se passe ou tout se passe mais pas comme vous le dîtes (à voix haute) à l'ordinateur. Lerp qui signifie Linear Interpolation ou interpolation linéaire. On change une valeur entre une valuer de départ et une valeur cible par un ratio.

transform.position = Vector3.Lerp(depart, cible, ratio);

Le ratio représente le pourcentage de déplacement entre les deux valeurs.
Si le ratio est 0, alors rien ne bouge, si 1 depart devient cible.

Si vous souhaitez déplacer unobjet entre deux points en t secondes, il vous faut enregistrer la position de départ et croitre le ratio (généralement par un facteur Time.deltaTime/nombreDeSecondes) et l'objet atteindra la destination quand ratio est égal à 1.

Vector3 _depart;
Vector3 _target;
float _t;

void Update()
{
     transform.position = Vector3.Lerp(_depart, _target, _t);
     _t += Time.deltaTime/2; //Take 2 seconds
}

public void SetTargetPosition(Vector3 newTargetPosition)
{
    _depart = transform.position;
    _target = newTargetPosition;
    _t = 0;
}

Si vous souhaitez un effet plus délicat (car vous l'êtes) depuis le point où se trouve l'objet vers la cible, pour ce cas vous passez la position actuelle à Lerp.

Vous prenez depart (probablement transform.position) et vous regardez vers target. Puis vous bougez l'objet par le ratio. Avec ratio = 1, le mouvement est 100% de la distance. Avec ratio = 0.5 le mouvement représente 50% de la distance entre les deux points.

void Update(){
    transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime);
    }

Si 10 m sépare transform(0) et target(10) et deltaTime est 0.2 (20%) alors (deltaTime a peu de chance d'être 0.2 mais pour l'exemple il le sera):

  1. 20% de target-transform (10-0) = 2m donc transform = 2
  2. 20% de target-transform (10-2) = 1.6m donc transform = 3.6
  3. 20% de target-transform (10-3.6) = 1.28m donc transform = 4.88
  4. Et ainsi de suite

Notez que l'objet n'atteind jamais sa cible mais s'en approche sans jamais la toucher similaire à la limite en algèbre.  Il est possible de stopper la focntion en ajoutant une condition. Lerp est utilisé quand vous souhaitez déplacer un objet progressivement, cela peut être une porte qui glisse lentement pour s'ouvrir ou encore la caméra sur un 3rdPersonController utilise ce procédé pour ne pas bouger de manière top abrupte.

Certains exemples de la documentation (s'ils n'ont pas été changés depuis) utilisent Time.time comme ratio. C'est en quelque sorte un peu faux. L'interpolation n'arriverait que dans la première seconde de la scène. Le ration doit avoir comme valeur 0 <  ratio< 1.


Stocker une liste d'objets qui peut grandir

Stocker une liste d'objets qui peut grandir

N'utilisez pas Array, ArrayList et HashTable - toujours utiliser une des collections génériques de .NET ou les tableaux "Built-in". Les collections vieilles versions inpliquent beaucoup plus de codes pour obtenir l'objet souhaité. Les collections génériques retirent ce problème mais peuvent aussi contenir toutes sortes d'objets.
Les tableaux Built-in comme int[100], sont les collections les plus rapides et les plus efficaces, cependant leur taille est fixe. Ils sont parfaits si la taille du tableau n'a pas besoin d'être modifiée.

En C# il vous faut ajouter en haut de page:

using System.Collections.Generic;

UnityScript n'a besoin de rien.

Au lieu d'utliser Array ou ArraList, utiliser les List génériques. Avec les Lists, vous pouvez ajouter ou retirer des objets pendant l'execution, Lists continennent de nombreuses fonctions pour faciliter leur utilisation, telles que Sort pour arranger l'ordre. Elles peuvent aussi être changée en tableau si nécessaire.

Vous pouvez vous réferer à la documentation .NET pour List et Dictionary sur MSDN.  Quelques exmples en C# et UnityScript sont démontrés ci-dessous

Travailler avec une liste:

//Definir une liste avec UniyScript
var myList = new List.<int>();
var anotherList = new List.<SomeClass>();

//Definir une liste avec C#
List<int> myList = new List<int>();
List<SomeClass> anotherList = new List<SomeClass>();

//Ajouter un élément à une liste
myList.Add(someValue);

//Ajouter de multiples éléments à la liste
myList.AddRange(someListOrArrayOfValues);

//Vider la liste
myList.Clear();

//Insérer dasn une liste
myList.Insert(1, someValue);

//Insérer de multiples éléments
myList.InsertRange(1, someListOrArrayOfValues);

//Retirer un élément en particulier
myList.Remove(someValue);

//Retirer à un index
myList.RemoveAt(1);

//Trouver l'index d'un élément
var index = myList.IndexOf(someValue);

//Trouver l'index d'un éleément avec une fonction en UnityScript
var index = anotherList.FindIndex(function(entry) entry.someValue == something);

//Faire un tableau avec une liste
var myArray = myList.ToArray();

//Trouver l'index d'un objet avec une fonction en C#
var index = anotherList.FindIndex((entry) => entry.someValue == something)

//Taille de la liste (nombre d'éléments)
var itemCount = myList.Count

Les dictionnaires sont les tableaux associatifs génériques de .NET:

//Définir un dictionnaire de string pour integer en UnityScript
var myDic = new Dictionary.<String, int>();

//Définir un dictionnaire de GameObject pour classe in UnityScript
var anotherDic = new Dictionary.<GameObject, SomeClass>();

//String pour int den C#
Dictionary<string, int> myDic = new Dictionary<string, int>();

//GameObejct pour classe en C#
Dictionary<GameObject, SomeClass> anotherDic = new Dictionary<GameObject, SomeClass>();

//Ajouter un élément
myDic["Something"] = someIntValue;

//Obtenir une valeur
var someValue = myDic["Something"];

//Obtenir une valeur complexe et modifier ses attributs
anotherDic[gameObject].someProperty = someValue;

//Vérifier si une valeur existe
if(myDic.ContainsKey("Something")) { }

//Retirer un élément
myDic.Remove("Something');

//Iteration de chaque clé en UnityScript
for(var key : String in myDic.Keys) { }

//Iteration de chaque valeur en C#
foreach(int value in myDic.Values) { }

//Vider le dictionnaire
myDic.Clear();

//Nombre de valeurs
var count = myDic.Count;


Comment bouger un rigidbody

La physique est votre ami, elle vous fera beaucoup de bien alors il vous faut bien en prendre soin!

Si vous utilisez le systême de physique de Unity, il est recommandé d'utiliser AddForce ou les fonctions similaires pour déplacer le rigidbody si il ne sont pas désignés comme isKinematic
Si vous déplacez les rigidbody vous même, vous devez le faire dans la FixedUpdate, cependant il vous tout de mëme vous attendre á des surprises tel que des forces étonnament grandes ou petites appliqués à des objets. Vous ne pouvez pas utiliser Input dans la FixedUpdate. Jetez un oeil à la partie sur Détecter l'Input convenablement sur cette même page.
Ne changez pas la gravité ou timeScale, si un objet semble bouger trop vite outrop lentement, c'est surement un problème de taille ou de distance par rapport à la caméra. Si vous essayez de modifier la gravité de gérer le problème, vous ferez surement face à d'autres problèmes ulterieurement. Si vous débutez, changez plutot la taille (scale) et les proportions
Utilisez une taille réaliste pour vos objets, pensez que 1 unités dans Unity représente 1m. Ce n'est pas un règle juste une convention mais respectez la.  Une personne fait 1.7 m, une voiture fait  3.5 m de long etc etc. Chaque objet avec la bonne taille, la caméra placée convenablement et vous serez surement déjà à moitié fixé.  Si vous avez un doute, il vous suffit de vous réferer au monde réel.

Si votre balle semble tomber trop vite, a-t-elle la taille d'un berlon (ou une bille)? Essaeyez de faire tomber un berlon sur votre table, vous verrez que ça va vite. Pour un effet plus "flottant", il vous faut une balle bien plus grosse et lus loin de la caméra.


Le Character Controller, à quoi donc que ça sert?

Le Character Controller, à quoi donc que ça sert?

Le Character Controller peut être utilisé pour le joueur et les NPC (personnages IA). Le bon point c'est que le CC prend en charge des mesures et des logiques qui seraient compliquées de coder soi-même. Par exemple, bouger sur une plate-forme ou entrer en collision avec l'environement et les autres personnages.

Oubliez le CC si vous voulez appliquer des contraintes physiques à votre personnage. Privilégiez un script personnalisé. avec un rigidbody.

De nombreaux jeux, n'utilisent pas le CC, alors assurez-vous que ce soit bien ce dont vous avez besoin et si vous d´cidez de l'utiliser, réservez le pour les objets qui en ont besoin (Pas de CC pour une voiture par exemple).

Le CC peut interagir avec les éléments de l'environement, comme les pousser hors de votre chemin. Il vous faudra faire cela par script et le résultat n'est pas garantit de paraître réalistique puisque il y a "conflit" avec le système de Physique. Voyez cet exemple.
CC n'a pas de rigidbody par défaut et donc OnCollisionXXXX et OnTriggerXXXX ne fonctioneront pas par défaut. Il vous faut ajouter un rigidbody kinematic ou utliser OnControllerColliderHit.
Toujours utiliser les fonctions  SimpleMove ou Move pour déplacer un objet avec un CC. Ne pas changer la position directement.


Vous pensez programmez en Java ou Javascript

Vous pensez programmez en Java ou Javascript

Dans le but de légèrement faire oublier la confusion, le terme UnityScript a été utilisé sur cette page.

Tout d'abord, Unity dit utiliser Javascript. Javascript n'est pas Java et ne partage que peu ou pas de fonctionalités.Au départ, Javascript (la langage browser) se nommait LiveScript. Java était (et est encore) populaire à cette époque, alors il fut renommé JavaScript 

Si vous êtes familier avec Java, C# partage plus de fonctionalités que UnityScript. Il existe de nombreux tutoriels pour transiter de Java vers C#, C# sur Unity est C#. C'est donc tout à fait applicable au Unity C#.
Unity JavaScript n'est pas JavaScript pour internet. De nombreaux utlisateurs (nous)  le nomme Unity Script pour marquer la différence.  JavaScript/UnityScript sur Unityest un langage .NETplus proche d' Action Script que JavaScript.  Certaines particularités de JavaScript peuvent se retrouver sur UnityScript, mais de nombreuses manquent. Par exemple, UnityScript utilise l'héritage classique alors que Javascript utilise l'héritage prototype, il n'est pas possible d'ajouter des fonctions à un objet après compilation avec UnityScript et ça continue comme ça pour un moment.
Quoi qu'il advienne, jamais ôh grand jamais un tutoriel de Javascript internet vous permettra de programmer sur Unity. Cela ne ferait qu'amplifier la confusion. Faîtes comme moi, utilisez le terme UnityScript pour que les nouveaux venus ne fassent pas l'erreur.


La coroutine ne semble pas s'arrêter après le yield ou wait

La coroutine ne semble pas s'arrêter après le yield ou wait

Cela arrive souvent lorsque vous desactiver le script qui contient la coroutine ou détruit l'objet qui contenait le script qui contenait la coroutine. SOuvent, lorsque un objet meurt, on lance une coroutine ou appelle Invoke pour que quelque chose se fasse après l'animation de mort, une mise à jour des scores ou quelconque autre processus de retard.

Une coroutine executera toujours la partie se trouvant avant le yield. Si le script est désactivé ou détruit alors le reste de la fonciton ne sera jamais executée.
Si vous utilisez WaitForSeconds, Time.timeScale ne peut pas être 0 sinon rien ne bouge. WaitForSeconds utilise l'horloge du jeu qui est régit par timeScale. Si timeScale est 0, tout s'arrête.
Les coroutines s'executent sur l'objet qui contient l'appel de  StartCoroutine (pour C#). Donc si vous faites l'appel depuis un autre objet, il est logique de faire l'appel de StartCoroutine sur cet objet. Un exemple va clarifier: otherScript.StartCoroutine(otherScript.SomeFunction());

Pas bien:

void Update() {
    if(health < 0) {
           StartCoroutine(Die());
           Destroy(gameObject); //ou enabled = false;
    }
}

IEnumerator Die() {
       animation.Play("wobble");
       yield return new WaitForSeconds(3);
       //Cela n'arrivera jamais
       animation.Play("meurir");
}

Bien:

bool estMort;
void Update() {
     if(estMort) return;
     if(health < 0) {
         StartCoroutine(Meurt());
     }
}

IEnumerator Meurt() {
      estMort = true;
      animation.Play("wobble");
      yield return new WaitForSeconds(3);
      animation.Play("meurir");
      yield return new WaitForSeconds(3);
      Destroy(gameObject);
}


Accéder un script en C# avec UnityScript et inverse

Accéder un script en C# avec UnityScript et inverse

L'accession ne peut se faire que dans un seul sens depuis l'un vers l'autre mais pas dans l'autre sens (l'autre ne peut pas voir l'un). Deux classes écrites en différents langages ne pourront jamais se voir dans les deux sens.
Choisissez un langage pour votre programme. SI vous utilisez un script d'un tierce parti, le lagage a peu d'importance tant que vous faites le nécessaire. Il vous suffit de placer le script dans un fichier qui est compilé en premier - Standard Asset, Pro Standard Asset ou Plugins, alors il vous est possible d'accéder à ces fichiers à partir de classes stockées dans un fichier compilé ulterieurement.

UnityScript et C# ne sont pas compilés avec le même assembleur, c'est pourquoi il n'est pas possible de simplement accéder un fichier via un autre puisque les deux langages ne se comprennent pas. Un script placé dans Plugins, Standard Asset ou Pro Standard Asset est compilé en premier en IL (Intermediate Language). IL est le langage assembleur de .NET. De nombreux langages peuvent utiliser .NET et se mêler parce qu'en bout de chaîne, tout le monde se retrouve convertit en un langage commun. Une fois les scripts compilés, les autre scripts placés dans d'autres fichiers peuvent y accéder.

Ainsi, un script en C# peut accéder un script en UnityScript si ce dernier est placé dans un des fichiers Standard Asset, Pro Standard Asset ou Plugins (ou n'importe quel fichier sous leur hiérarchie).  Et ce la fonctionne aussi dans l'autre sens.

Gardez tête qu'un script en UnityScript placé dans un de ces fichiers spéciaux ne pourra voir un script en C# où qu'il soit et vice-versa. La hiérarchie fait que si un script est placé dans un fichier spécial, il peut voir les autres scripts de même langage placés en dessous (compilés ultérieurement) mais ne peut voir aucun script de l'autre langage. Par logique, on retombe dans le même problême de départ. C'est pour cela qu'il vous faut éviter autant que faire se peut (rooh le langage!!) de mélanger les langages et essayer de tout faire en un seul (autant que faire se peut).

Unity propose une bonne documentation sur l'ordre de compilation des scripts ici.


Modifier la rotation d'un objet avec Quaternion

Modifier la rotation d'un objet avec Quaternion

Les paramètres x, y, z et w d'un quaternion n'ont rien à voir avec les valeurs qui sont affichées dans l'inspecteur pour la rotation de l'objet. Un quaternion n'est pas stocké en degrés mais en sinus et cosinus d'angles. La rotation en degrés est stockée dans la variable .eulerAngles de la rotation.

Évitez (ne faites pas) de modifer x, y, z, et w d'un quaternion sauf dans le cas peu probable où vous savez exactement ce que vous faites. Si vous souhaitez modifer une rotation par degrés, utilisez .eulerAngles.

Si vous souhaitez en savoir plus sur les quaternions, tout d'abord bonne chance, c'est un sujet lourd qui laisse nombre de programmeurs sur le carreau. Vous pouvez toujours en savoir un peu plus en lisant ce tutoriel.

Voici comment définir une rotation avec un ensemble d'angles:

transform.rotation.eulerAngles = new Vector3(100,0,100);

, , , , , , , , , , , , ,

6 Responses to "Les traquenards courants avec Unity"

  • modulo
    November 24, 2012 - 4:24 pm Reply

    Enfin un bon tutoriel en francais merci beaucoup

  • Rei_k
    November 25, 2012 - 2:59 pm Reply

    Roooh, merci beaucoup pour la traduction !
    Bravo a toute l’équipe pour le travail de qualité effectué sur ce site.

  • Xstahef
    November 30, 2012 - 8:20 am Reply

    Bravo pour le travail et merci.

  • Amzer Zo
    December 21, 2012 - 5:30 am Reply

    Je viens juste vous dire Merci pour les tutoriaux gratuit et surtout, très bien expliqué en français.
    J’espère que ça durera avec d’autres tutoriels.

    Merci et bonne continuation !

  • CocaDrill
    January 29, 2013 - 3:45 pm Reply

    Hop favoris!

    Merci beaucoup!

  • Alban
    September 2, 2013 - 2:43 pm Reply

    Génial !!!
    Ca m’a sauver la vie !
    Merci beaucoup , ce tuto est juste indispensable !

Leave a Reply