Maîtriser les caméras 2D dans Unity : un tutoriel pour les développeurs de jeux

Publié: 2022-03-11

Pour un développeur, la caméra est l'une des pierres angulaires du processus de développement d'un jeu. Qu'il s'agisse de montrer votre vue de jeu dans une application d'échecs ou de diriger magistralement le mouvement de la caméra dans un jeu 3D AAA pour obtenir des effets cinématographiques, les caméras sont essentiellement utilisées dans n'importe quel jeu vidéo jamais créé, avant même d'être appelées "caméras".

Dans cet article, je vais expliquer comment concevoir un système de caméra pour les jeux 2D, et je vais également expliquer quelques points sur la façon de l'implémenter dans l'un des moteurs de jeu les plus populaires, Unity.

De la 2D à la 2.5D : un système de caméra extensible

Le système de caméras que nous allons concevoir ensemble est modulaire et extensible. Il a un noyau de base composé de plusieurs composants qui assureront la fonctionnalité de base, puis divers composants/effets qui peuvent être éventuellement utilisés, selon la situation.

Le système de caméra que nous construisons ici est destiné aux jeux de plate-forme 2D, mais peut facilement être étendu à d'autres types de jeux 2D, jeux 2.5D ou même jeux 3D.

Maîtriser la caméra 2D dans Unity : un didacticiel pour les développeurs de jeux

Maîtriser la caméra 2D dans Unity : un didacticiel pour les développeurs de jeux
Tweeter

Je vais diviser la fonctionnalité de la caméra en deux groupes principaux : le suivi de la caméra et les effets de la caméra.

Suivi

La plupart des mouvements de caméra que nous ferons ici seront basés sur le suivi. C'est la capacité d'un objet, dans ce cas la caméra, à suivre d'autres objets lorsqu'ils se déplacent dans la scène du jeu. Les types de suivi que nous allons implémenter vont résoudre certains scénarios courants rencontrés dans les jeux de plateforme 2D, mais ils peuvent être étendus avec de nouveaux types de suivi pour d'autres scénarios particuliers que vous pourriez avoir.

Effets

Nous allons implémenter des effets sympas comme le bougé de l'appareil photo, le zoom de l'appareil photo, le fondu de l'appareil photo et la superposition de couleurs.

Commencer

Créez un nouveau projet 2D dans Unity et importez des ressources standard, en particulier le personnage RobotBoy. Ensuite, créez une boîte au sol et ajoutez une instance de personnage. Vous devriez pouvoir marcher et sauter avec votre personnage dans votre scène actuelle. Assurez-vous que la caméra est réglée sur le mode Orthographique (elle est réglée sur Perspective par défaut).

Suivi d'une cible

Le script suivant ajoutera un comportement de suivi de base à notre caméra principale. Le script doit être attaché en tant que composant à la caméra principale de votre scène et il expose un champ pour attribuer un objet cible à suivre. Ensuite, le script s'assure que les coordonnées x et y de la caméra sont identiques à celles de l'objet qu'il suit. Tout ce traitement est effectué lors de l'étape de mise à jour.

 [SerializeField] protected Transform trackingTarget; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x, trackingTarget.position.y, transform.position.z); }

Faites glisser le personnage RobotBoy de la hiérarchie de votre scène sur le champ "Suivi de la cible" exposé par notre comportement suivant afin d'activer le suivi du personnage principal.

Ajout de décalage

Tout va bien, mais nous pouvons voir une limitation dès le départ : le personnage est toujours au centre de notre scène. Nous pouvons voir beaucoup de choses derrière le personnage, ce qui ne nous intéresse généralement pas, et nous voyons trop peu de ce qui est devant notre personnage, ce qui pourrait nuire au gameplay.

Pour résoudre ce problème, nous ajoutons de nouveaux champs au script qui permettront de positionner la caméra avec un décalage par rapport à sa cible.

 [SerializeField] float xOffset; [SerializeField] float yOffset; // ... void Update() { transform.position = new Vector3(trackingTarget.position.x + xOffset, trackingTarget.position.y + yOffset, transform.position.z); }

Ci-dessous, vous pouvez voir une configuration possible pour les deux nouveaux champs :

Lisser les choses

Le mouvement de la caméra est assez rigide et produira également des étourdissements chez certains joueurs à cause du mouvement constant perçu de l'environnement. Afin de résoudre ce problème, nous ajouterons un délai dans le suivi de la caméra à l'aide d'une interpolation linéaire, et un nouveau champ pour contrôler la vitesse à laquelle la caméra se met en place après que le personnage a commencé à changer de position.

 [SerializeField] protected float followSpeed; // ... protected override void Update() { float xTarget = trackingTarget.position.x + xOffset; float yTarget = trackingTarget.position.y + yOffset; float xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); float yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); transform.position = new Vector3(xNew, yNew, transform.position.z); } 

Arrêtez les vertiges : verrouillage de l'axe

Puisqu'il n'est pas agréable pour votre cerveau de regarder la caméra monter et descendre tout le temps avec le personnage, nous introduisons le verrouillage de l'axe. Cela signifie que nous pouvons limiter le suivi à un seul axe. Ensuite, nous séparerons notre code de suivi en un suivi indépendant des axes, et nous prendrons en compte les nouveaux drapeaux de verrouillage.

 [SerializeField] protected bool isXLocked = false; [SerializeField] protected bool isYLocked = false; // ... float xNew = transform.position.x; if (!isXLocked) { xNew = Mathf.Lerp(transform.position.x, xTarget, Time.deltaTime * followSpeed); } float yNew = transform.position.y; if (!isYLocked) { yNew = Mathf.Lerp(transform.position.y, yTarget, Time.deltaTime * followSpeed); } 

Système de voies

Maintenant que la caméra ne suit le joueur qu'horizontalement, nous sommes limités à la hauteur d'un écran. Si le personnage grimpe sur une échelle ou saute plus haut que cela, nous devons suivre. Pour ce faire, nous utilisons un système de voies.

Imaginez le scénario suivant :

Le personnage est initialement sur la voie inférieure. Tant que le personnage reste dans les limites de cette voie, la caméra se déplacera uniquement horizontalement sur le décalage de hauteur spécifique à la voie que nous pouvons définir.

Dès que le personnage entre dans une autre voie, la caméra passera à cette voie et continuera à se déplacer horizontalement à partir de là jusqu'au prochain changement de voie.

Des précautions doivent être prises lors de la conception des voies afin d'éviter un changement de voie rapide lors d'actions telles que des sauts, ce qui peut créer de la confusion pour le joueur. Une voie ne doit être modifiée que si le personnage du joueur doit y rester pendant un certain temps.

Les niveaux des voies peuvent changer tout au long du niveau du jeu en fonction des besoins spécifiques du concepteur, ou peuvent être complètement interrompus et un autre système de suivi de caméra peut prendre leur place. Par conséquent, nous avons besoin de certains limiteurs pour spécifier les zones de voie.

Mise en œuvre

Une implémentation possible consiste à ajouter des voies en tant qu'objets simples dans la scène. Nous utiliserons leur coordonnée de position Y associée au décalage Y dans le script de suivi ci-dessus pour implémenter le système. Par conséquent, leur positionnement sur les coordonnées X et Z n'a pas d'importance.

Ajoutez la classe LaneSystem à la caméra, ainsi que la classe de suivi, et affectez les objets de voie au tableau fourni. Attribuez également le personnage du joueur au champ Référence. Comme la référence est positionnée entre une voie et une autre voie, la plus basse des deux sera utilisée pour positionner la caméra.

Et la classe LaneSystem se charge de déplacer la caméra entre les voies, en fonction de la position de référence. Le followSpeed ​​est ici encore utilisé pour l'interpolation de position, afin d'éviter que le changement de voie ne soit trop brusque :

 [SerializeField] Transform reference; [SerializeField] List<Transform> lanes; [SerializeField] float followSpeed = 5f; // ... void Update() { float targetYCoord = transform.position.y; if (lanes.Count > 1) { int i = 0; for (i = 0; i < lanes.Count - 1; ++i) { if ((reference.position.y > lanes[i].position.y) && (reference.position.y <= lanes[i + 1].position.y)) { targetYCoord = lanes[i].position.y; break; } } if (i == lanes.Count - 1) targetYCoord = lanes[lanes.Count - 1].position.y; } else { targetYCoord = lanes[0].position.y; } float yCoord = Mathf.Lerp(transform.position.y, targetYCoord, Time.deltaTime * followSpeed); transform.position = new Vector3(transform.position.x, yCoord, transform.position.z); }

Cette implémentation n'est pas WYSIWYG, et est laissée telle quelle en exercice au lecteur.

Système de nœud de verrouillage

Faire bouger la caméra sur les couloirs est génial, mais parfois nous avons besoin que la caméra soit verrouillée sur quelque chose, un point d'intérêt (POI) dans la scène du jeu.

Ceci peut être réalisé en configurant ces POI dans la scène et en leur attachant un déclencheur de collision. Chaque fois que le personnage entre dans ce collisionneur déclencheur, nous déplaçons la caméra et restons sur le POI. Au fur et à mesure que le personnage se déplace puis quitte le collisionneur déclencheur du POI, nous revenons à un autre type de suivi, généralement le comportement de suivi standard.

La commutation du suivi de la caméra vers un nœud de verrouillage et inversement peut être effectuée soit par un simple commutateur, soit par un système de pile, sur lequel les modes de suivi sont poussés et sautés.

Mise en œuvre

Afin de configurer un nœud de verrouillage, créez simplement un objet (peut être vide ou comme dans la capture d'écran ci-dessous, un sprite) et attachez-y un grand composant Circle Collider 2D afin qu'il marque la zone dans laquelle le joueur se trouvera lorsque la caméra concentrer le nœud. Vous pouvez choisir n'importe quel type de collisionneur, je choisis Circle comme exemple ici. Créez également une balise que vous pouvez facilement vérifier, comme "CameraNode" et attribuez-la à cet objet.

Ajoutez la propriété suivante au script de suivi sur votre caméra :

 public Transform TrackingTarget { get { return trackingTarget; } set { trackingTarget = value; } }

Ensuite, attachez le script suivant au lecteur, ce qui lui permettra de basculer temporairement la cible de la caméra vers le nœud de verrouillage que vous avez défini. Le script se souviendra également de sa cible précédente afin de pouvoir y revenir lorsque le joueur est hors de la zone de déclenchement. Vous pouvez continuer et transformer cela en une pile complète si vous en avez besoin, mais pour notre objectif, puisque nous ne chevauchons pas plusieurs nœuds de verrouillage, cela suffira. Sachez également que vous pouvez modifier la position du Circle Collider 2D, ou encore ajouter tout autre type de collisionneur pour déclencher le verrouillage de la caméra, ce n'est qu'un simple exemple.

 public class LockBehavior : MonoBehaviour { #region Public Fields [SerializeField] Camera camera; [SerializeField] string tag; #endregion #region Private private Transform previousTarget; private TrackingBehavior trackingBehavior; private bool isLocked = false; #endregion // Use this for initialization void Start() { trackingBehavior = camera.GetComponent<TrackingBehavior>(); } void OnTriggerEnter2D(Collider2D other) { if (other.tag == tag && !isLocked) { isLocked = true; PushTarget(other.transform); } } void OnTriggerExit2D(Collider2D other) { if (other.tag == tag && isLocked) { isLocked = false; PopTarget(); } } private void PushTarget(Transform newTarget) { previousTarget = trackingBehavior.TrackingTarget; trackingBehavior.TrackingTarget = newTarget; } private void PopTarget() { trackingBehavior.TrackingTarget = previousTarget; } } 

Zoom de la caméra

Le zoom de la caméra peut être exécuté soit sur entrée de l'utilisateur, soit sous forme d'animation lorsque nous voulons nous concentrer sur quelque chose comme un POI ou une zone plus étroite dans un niveau.

Le zoom de la caméra 2D dans Unity 3D peut être obtenu en manipulant la taille orthographique de la caméra. Attacher le script suivant en tant que composant à une caméra et utiliser la méthode SetZoom pour modifier le facteur de zoom produira l'effet souhaité. 1,0 signifie aucun zoom, 0,5 signifie un zoom avant deux fois, 2 signifie un zoom arrière deux fois, et ainsi de suite.

 [SerializeField] float zoomFactor = 1.0f; [SerializeField] float zoomSpeed = 5.0f; private float originalSize = 0f; private Camera thisCamera; // Use this for initialization void Start() { thisCamera = GetComponent<Camera>(); originalSize = thisCamera.orthographicSize; } // Update is called once per frame void Update() { float targetSize = originalSize * zoomFactor; if (targetSize != thisCamera.orthographicSize) { thisCamera.orthographicSize = Mathf.Lerp(thisCamera.orthographicSize, targetSize, Time.deltaTime * zoomSpeed); } } void SetZoom(float zoomFactor) { this.zoomFactor = zoomFactor; }

Bougé d'écran

Chaque fois que nous avons besoin de montrer un tremblement de terre, une explosion ou tout autre effet dans notre jeu, un effet de bougé de caméra est très pratique.

Un exemple d'implémentation de la façon de procéder est disponible sur GitHub : gist.github.com/ftvs/5822103. La mise en œuvre est assez simple. Contrairement aux autres effets que nous avons couverts jusqu'à présent, il repose sur un peu d'aléatoire.

Fondu et superposition

Lorsque notre niveau commence ou se termine, un effet de fondu d'entrée ou de sortie est agréable. Nous pouvons implémenter cela en ajoutant une texture d'interface utilisateur non interactive dans un panneau s'étendant sur tout notre écran. Initialement transparent, nous pouvons le remplir avec n'importe quelle couleur et opacité, ou l'animer pour obtenir l'effet souhaité.

Voici un exemple de cette configuration, veuillez noter que l'objet Panneau d'interface utilisateur est attribué à l'enfant "Superposition de caméra" de l'objet principal de la caméra. Camera Overlay expose un script appelé Overlay qui présente les éléments suivants :

 [SerializeField] Image overlay; // ... public void SetOverlayColor(Color color) { overlay.color = color; } 

Afin d'avoir un effet de fondu, modifiez votre script Overlay en ajoutant une interpolation à une couleur cible que vous définissez avec SetOverlayColor comme dans le script suivant, et définissez la couleur initiale de notre panneau sur Noir (ou Blanc) et la couleur cible à la couleur finale de votre superposition. Vous pouvez modifier le fadeSpeed ​​en fonction de vos besoins, je pense que 0,8 est un bon choix pour les débutants. La valeur de fadeSpeed ​​fonctionne comme un modificateur de temps. 1.0 signifie que cela se produira sur plusieurs images, mais dans un délai d'une seconde. 0,8 signifie qu'il faudra en fait 1/0,8 = 1,25 secondes pour terminer.

 public class Overlay : MonoBehaviour { #region Fields [SerializeField] Image overlay; [SerializeField] float fadeSpeed = 5f; [SerializeField] Color targetColor; #endregion void Update() { if (overlay.color != targetColor) { overlay.color = Color.Lerp(overlay.color, targetColor, Time.deltaTime * fadeSpeed); } } #region Public public void SetOverlayColor(Color color) { targetColor = color; } #endregion }

Emballer

Dans cet article, j'ai essayé de démontrer les composants de base nécessaires pour mettre en place un système de caméra 2D modulaire pour votre jeu, ainsi que l'état d'esprit requis pour le concevoir. Naturellement, tous les jeux ont leurs besoins particuliers, mais avec le suivi de base et les effets simples décrits ici, vous pouvez parcourir un long chemin et également avoir un plan pour implémenter vos propres effets. Ensuite, vous pouvez aller encore plus loin et emballer le tout dans un package Unity 3D réutilisable que vous pouvez également transférer vers d'autres projets.

Les systèmes de caméras sont très importants pour transmettre la bonne atmosphère à vos joueurs. Une bonne comparaison que j'aime utiliser est lorsque vous pensez à la différence entre le théâtre classique et les films. Les caméras et les films eux-mêmes ont apporté tellement de possibilités à la scène qu'elle a finalement évolué pour devenir un art à part entière, donc si vous ne prévoyez pas de mettre en œuvre un autre jeu "Pong", les caméras avancées devraient être votre outil de choix dans tout projet de jeu que vous 'll entreprendre à partir de maintenant.

Connexes : Unity avec MVC : comment améliorer le développement de votre jeu