Unity – Roguelike 2D

Déplacement des objets

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Le projet présenté dans cette série est tout aussi bien compatible avec Unity 4.6 qu'avec Unity 5.
Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.

II. Vidéo


Unity – Roguelike 2D – Déplacement des objets


III. Résumé

Dans ce chapitre, nous créons une nouvelle classe gérant aussi bien le déplacement du joueur que celui des ennemies.
L'astuce permettant de partager des fonctionnalités entre les deux types d'éléments est d'utiliser un héritage. Chaque type d'unités pourra définir le comportement approprié et ce comportement pourra être appelé à partir d'un code générique et commun.

III-A. MovingObjects.js

Pour l'héritage, les mots clés suivants indiquent :

  • abstract :

    • pour une classe : indique que la classe abstraite peut être définie comme incomplète. Les fonctions manquantes devront être définies dans les classes filles,
    • pour une fonction : indique que la fonction est actuellement incomplète et devra être complétée par les classes filles ;
  • virtual : permet à une fonction d'être surchargée par une fonction du même nom, dans la classe fille.

Les génériques sont utilisés afin d'utiliser une même fonction, que ce soit pour les ennemies ou pour le joueur. Ainsi, le code n'est pas dupliqué.

III-A-1. Script

 
Sélectionnez
using UnityEngine;
using System.Collections;

namespace Completed
{
    // Le mot clé abstract vous permet de créer des classes et des membres de classes qui sont incomplets et qui doivent être implémentés dans les classes filles.
    public abstract class MovingObject : MonoBehaviour
    {
        public float moveTime = 0.1f;           // Temps que cela prend de déplacer l'objet, en seconde.
        public LayerMask blockingLayer;         // Couche sur laquelle la collision doit être vérifiée.
        
        
        private BoxCollider2D boxCollider;      // Le composant BoxCollider2D attaché à cet objet.
        private Rigidbody2D rb2D;               // Le composant Rigidbody2D attaché à cet objet.
        private float inverseMoveTime;          // Utilisé pour rendre les mouvements plus efficaces.
        
        
        // Protected, les fonctions virtuelles peuvent être surchargées par les classes filles.
        protected virtual void Start ()
        {
            // Récupère une référence sur le composant BoxCollider2D de l'objet
            boxCollider = GetComponent <BoxCollider2D> ();
            
            // Récupère une référence sur le composant Rigidbody2D de l'objet
            rb2D = GetComponent <Rigidbody2D> ();
            
            // En stockant l'inverse du temps de mouvement, nous pouvons l'utiliser dans une multiplication au lieu d'une division, c'est plus efficace.
            inverseMoveTime = 1f / moveTime;
        }
        
        
        // La fonction Move retourne true si elle peut faire le déplacement ou false sinon. 
        // La fonction Move prend les paramètres x et y pour la direction et un RaycastHit2D pour vérifier la collision.
        protected bool Move (int xDir, int yDir, out RaycastHit2D hit)
        {
            // Stocke la position de départ depuis laquelle on se déplace, selon la position actuelle de l'objet.
            Vector2 start = transform.position;
            
            // Calcul la position de fin suivant les paramètres de direction.
            Vector2 end = start + new Vector2 (xDir, yDir);
            
            // Désactive le boxCollider afin que le lancer de rayon ne touche pas la boîte englobante pour cet objet.
            boxCollider.enabled = false;
            
            // Envoie une ligne du point de départ vers la fin en vérifiant les collisions de la couche blockingLayer.
            hit = Physics2D.Linecast (start, end, blockingLayer);
            
            // Réactive le boxCollider après le lancer de rayon.
            boxCollider.enabled = true;
            
            // Vérifie si quelque chose a été touché.
            if(hit.transform == null)
            {
                // Si rien n'a été touché, démarre la coroutine SmoothMovement en passant la fin comme destination.
                StartCoroutine (SmoothMovement (end));
                
                // Retourne true pour indiquer que le mouvement a été un succès.
                return true;
            }
            
            // Si quelque chose a été touché, retourne false, le mouvement n'est pas possible.
            return false;
        }
        
        
        // Coroutine pour déplacer les éléments d'une case à l'autre, en prenant la destination comme paramètre.
        protected IEnumerator SmoothMovement (Vector3 end)
        {
            // Calcul la distance restante à effectuer en se basant sur le carré de la différence entre la position actuelle et la destination. 
            // Le carré est utilisé à la place de la magnitude, car c'est moins coûteux en calcul.
            float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
            
            // Tant que la distance est plus grande qu'un très petit nombre (Epsilon, presque zéro) :
            while(sqrRemainingDistance > float.Epsilon)
            {
                // Trouve une nouvelle position proportionnelle à la destination et basée sur moveTime.
                Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
                
                // Appelle MovePosition sur le Rigidbody2D attaché et le déplace à la position calculée.
                rb2D.MovePosition (newPostion);
                
                // Recalcule la distance restante après le mouvement.
                sqrRemainingDistance = (transform.position - end).sqrMagnitude;
                
                // Retourne et boucle jusqu'à avoir un sqrRemainingDistance assez proche de zéro pour terminer la fonction.
                yield return null;
            }
        }
        
        
        // Le mot clé virtual signifie que la fonction AttemptMove peut être surchargée avec le mot clé override et en héritant de la classe.
        // La fonction AttemptMove prend un paramètre générique T pour spécifier le type de composant avec lequel l'unité va interagir si elle est bloquée (le joueur pour les ennemies, les murs pour le joueur).
        protected virtual void AttemptMove <T> (int xDir, int yDir)
            where T : Component
        {
            // La variable hit conservera l'objet bloquant le rayon lorsque la fonction Move est appelée.
            RaycastHit2D hit;
            
            // Définit canMove à true si la fonction Move a réussi, false sinon.
            bool canMove = Move (xDir, yDir, out hit);
            
            // Vérifie si le lancer de rayon n'a rien touché.
            if(hit.transform == null)
                // Si rien n'a été touché, retourne et n'exécute rien de plus.
                return;
            
            // Récupère une référence sur le composant de type T attaché à l'objet qui a été touché.
            T hitComponent = hit.transform.GetComponent <T> ();
            
            // Si la variable canMove est false et que hitComponent n'est pas null alors MovingObject est bloqué et a touché quelque chose avec lequel il peut interagir.
            if(!canMove && hitComponent != null)
                
                // Appelle la fonction OnCantMove et lui passe hitComponent comme paramètre.
                OnCantMove (hitComponent);
        }
        
        
        // Le mot clé abstract indique que l'objet en cours de modification a une implémentation incomplète.
        // La fonction OnCantMove va être surchargée par les fonctions de la classe fille.
        protected abstract void OnCantMove <T> (T component)
            where T : Component;
    }
}

IV. Ressources

Vous pouvez télécharger les ressources pour ce projet sur l'Asset Store de Unity.

V. Commenter

Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2015 Unity Technologies. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.