lundi 25 février 2019

Unity - Instance et position dans le monde

Partie 1

Bonjour à tous,

aujourd'hui, j'inspecte l'instanciation dans Unity afin de reproduire un projet que j'ai conçu manuellement sur 3DSMax. Souhaitant reproduire dans Unity, il s'agira de produire une ville procédurale qui sera façonnée automatiquement. Ceci permettra ainsi de se concentrer sur des effets de lumière. Je souhaite, au moment de la conception, avoir un environnement qui fait du sensé.


Mon plan pour la première partie :











Pour se repérer au départ j'ai installé un script dans mon Objet de GameConfig.
Il est nommé Building Spawn:


-Maison : placer l'objet avec collision qui sera Instancié. Dans ce cas-ci j'ai mis l'objet ci-haut.
- Surface : Terrain sur lequel les objets se trouveront.
Pour le moment, j'ai placé un Plane sur le monde et je l'ai mis dans ce paramètre.
- Maison Pos, Nbre Batiments, Nbre Batiments Actuel : Pourraient être privés, mais je les ai mis publics pour que vous ayez une idée de ce qui est analysé. Ces chiffres changeront à chaque update.
- Nbre Batiments Min, Nbre BatimentsMax : Comme les noms indiquent, ce sont les variations du nombres d'instances de l'objet dans Maison qui se retrouveront dans la scène.
- Collision : Automatiquement, à l'ouverture du jeu, le Box Collider* de l'objet dans le paramètre Maison sera attaché.

*Pas besoin d'un Mesh Collider puisque ce sont des objets très cubiques et l'analyse que Unity fait peut devenir un peu trop compliquée sur des collisions à géométrie complexe.

 1- Générer 1 objet ...

J'ai rapidement mis en place des bâtiments à la forme allongée qui doit avoir une boîte de collision (Box Collision) pour la suite.



C'est une étape qui se termine avec deux lignes de script :
    // Paramètre avec l'objet - Variable
    public GameObject _maison;

Pour l'instant, on peut mettre cette ligne dans la fonction On Start :
    //L'instanciation - FixedUpdate
    Instantiate(_maison);


2 et 3- ...sur la surface ET au hasard ...

Le produire SUR la surface s'avérera un étape supplémentaire qui n'a pas encore été abordée dans ce projet. Bien s'assurerque le centre de l'objet s'appuie au sol dans le logiciel de modélisation (au bas du BoxCollider). Pour tout le reste, il me faudra un article complet sur le sujet.

Autrement; nous passons à l'étape 3 : je désire générer l'objet au hasard, ailleurs qu'au centre du monde.

Vient l'utilité de Maison Pos :
         // créer les coordonnées - Variable
         public Vector3 _maisonPos;
         // dire que ce sont les mêmes coordonnées que l'objet dans Maison - On Start             
         _maison.transform.position = _maisonPos;


Pour l'instant, on peut mettre cette ligne dans la fonction On Start :
         // changer la position de l'objet maitre - Update
          _maisonPos.x = Random.Range(-5.0f, 5.0f);
          _maisonPos.z = Random.Range(-5.0f, 5.0f);
          _maison.transform.position = _maisonPos;

4-  ... et le multiplier...

Cette fois, nous auront toutes nos Variables - je les ai toutes mises publiques mais elle peuvent être privées (private) :

    public GameObject _maison;
    public GameObject _surface;
    public Vector3 _maisonPos;
    public int _nbreBatiments;
    public int _nbreBatimentsActuel = 0;
    public int _nbreBatimentsMin = 1;
    public int _nbreBatimentsMax = 10;
    public BoxCollider _collision;


   void Start()
    {
        //ajustés
        _nbreBatiments = Random.Range(_nbreBatimentsMin, _nbreBatimentsMax);
        _maison.transform.position = _maisonPos;
        _collision = _maison.GetComponent<BoxCollider> ();
       
        //créés
        var _maisonRot = _maison.transform.localRotation.eulerAngles;
       
    }

    void Update()
    {
       
        // Layer 10 et layer 9 ne se voient pas (collisions)
        _maison.layer = 10;
        _surface.layer = 10;
       
    }
    void FixedUpdate()
    {
        //Pour suivre le nombre de bâtiments (PEUT ÊTRE REMPLACÉ PAR UNE FOR LOOP)
        if ( _nbreBatimentsActuel < _nbreBatiments )
        {

                //isole et isole les collisions - layer 10 ne voit pas layer 9 - avant l'instanciation
                _maison.layer = 9;

                //L'instanciation
                Instantiate(_maison);
                

                //remet la layer - layer 9 voit la layer 9 - après l'instanciation
                _maison.layer = 10;
               
                //Ajout d'un token - Nombre de bâtiment (peut se faire en for loop)
                _nbreBatimentsActuel += 1;
               
               //changer la position de l'objet maitre
            _maisonPos.x = Random.Range(-5.0f, 5.0f);
            _maisonPos.z = Random.Range(-5.0f, 5.0f);
            _maison.transform.position = _maisonPos;
               
            //changer la rotation du même objet
            var _maisonRot = _maison.transform.rotation;
            _maison.transform.rotation = Quaternion.Euler(-90.0f, Random.Range(-90.0f, 90.0f), 0.0f);
           
        }
        else
        {
            print("Il y a " + _nbreBatiments + " bâtiments dont " + _nbreBatimentsActuel + " sont    apparus.");
        }
           
        //Éviter de laisser la maison à (0, 0, 0)   
        if (_maison.transform.position.x == 0.0f  & _maison.transform.position.z == 0.0f )
        {
            _maison.transform.position = _maisonPos;
        }
    }


J'ai déjà mis la variable de "_collision" ici pour préparer l'étape suivante. Ce qui est le plus important ici c'est l'ordre dans lequel nous procédons.

Nous verrons ici des problème de "groupes de maison interpénétrant" l'une dans l'autre.



Également, nous aurons un problème de rotation. En faisant un troubleshoot, j'ai remarqué que le système n'avait gardé que les rotation entre 0.3 et 0.7 dans la traduction en Quaternion, alors je vais rechercher pour une solution plus appropriée. Certains confirment la complexité des Quaternions dans leur réponse comme l'une des pistes ici envoyée par "rutter" :
https://answers.unity.com/questions/790877/random-rotation-in-z-axis.html

5- ... sans qu'ils ne se touchent.

Ici, nous répondons au problème des groupes de maisons avec un système utilisant les physiques "Physics.OverlapBox". Étant donné que la physique procède par moment de façon erratique avec la fonction d'Update, je me vois suggéré d'utiliser la fonction de FixedUpdate, environnement contrôlé pour la physique.

Bref, voici ce dont nous avons besoin de nouveau (et ou le placer entre les commentaires) :

        // [...]créés
        var _enableMeshCollider = _surface.GetComponent<MeshCollider>().enabled ;

        //var _maisonRot = _maison.transform.localRotation.eulerAngles; [...]


 
         //[...]if ( _nbreBatimentsActuel < _nbreBatiments )
        //{


            //mise à jour de la position de la COLLISION BOX
            _collision = _maison.GetComponent<BoxCollider> ();
           
            //création de la boite de collision (nécessite une COLLISION BOX)
            //Démarrer en confirmant les collisions

            var _verifCollision = Physics.OverlapBox ( (_collision.center + _maisonPos), (_collision.size), (_maison.transform.rotation));
           
            //Continuer en vérifiant les contacts des collisions
            if (_verifCollision.Length == 1 || _verifCollision.Length == 0)
            {


                print ("ce qui est écrit " + _verifCollision.Length + "- ne touche rien");

               //troubleshooting coordonnées de la hitbox
                print ("non-hit ET le centre X : " + (_collision.center.x + _maisonPos.x) + " ET la taille X : " + _collision.size.x + " ET le centre Z : " + (_collision.center.z + _maisonPos.z) + " ET la taille Z : " + _collision.size.z + " ET la rotation : " + _maison.transform.rotation);


                //isole et ferme les collisions - layer 10 par rapport à la layer 9 - avant l'instanciation
                //_maison.layer = 9;
[...]




               // [...]Ajout d'un token
               //_nbreBatimentsActuel += 1;
               
           
}
            else
            {
               
                print ("ce qui est écrit " + _verifCollision.Length );
                
//troubleshooting coordonnées de la hitbox
                print("hit build"
ET le centre X : " + (_collision.center.x + _maisonPos.x) + " ET la taille X : " + _collision.size.x + " ET le centre Z : " + (_collision.center.z + _maisonPos.z) + " ET la taille Z : " + _collision.size.z + " ET la rotation : " + _maison.transform.rotation);

               
            }


            //changer la position de l'objet maître
            _maisonPos.x = Random.Range(-5.0f, 5.0f);
[...]





             //[...]_maison.transform.rotation = Quaternion.Euler(-90.0f, Random.Range(-90.0f, 90.0f), 0.0f);
            
            //repositionner la collision
          
 _collision = _maison.GetComponent<BoxCollider> ();

        //}
       //else
[...]


Ce sont des étapes cruciales ou l'objet "Maison", qui sera instancié, parcourt l'environnement à la recherche d'un endroit ou rien n'est placé (collision).  









Les maisons se tiennent plus à distance mais semblent garder relativement le même angle

---

Retour

Utilisation de la loop avec "for" : Je crois que je dois transférer à la "for loop" plutôt que de continuer directement sur l'Update. Cela va me permettre d'isoler mes problèmes suivants.Imprécision de la vérification des Collisions : Notez que j'ai mis " if (_verifCollision.Length == 1 || _verifCollision.Length == 0)" comme condition pour créer une instance, ce qui signifie qu'il peut y avoir 1 collision. _verifCollision est la OverlapBox (Zone qui détecte des superpositions de Collisions). Si je mets uniquement "_verifCollision.Length == 0", ce qui signifie que l'Array des Collisions serait vide, je me retrouve avec une boucle infinie ou la recherche d'un endroit sans collision ne se termine jamais. Je suppose que le layer mask ne s'active pas au bon moment ou pas du tout. Je crois qu'il me faut mentionner à la OverlapBox quelle layer elle doit analyser. Elle semble mettre l'objet maître dans les collisions.

Rotations d'objet et d'OverlapBox différentes : À quelques endroits, les objets s'interpénètrent . Selon mes recherches, il s'agirait encore d'un problème lié aux rotations Quaternion vs Euler. Il est certain que je vais regarder cela de près pour la suite.

Partie 2 à venir...

Je vous souhaite la meilleure journée,
À bientôt. 

Aucun commentaire:

Enregistrer un commentaire