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▲
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.