Conception d'une caméra 2D.

Publié le par Jérémy JANISZEWSKI

INTRODUCTION

Aujourd'hui, nous allons apprendre à créer une caméra 2D. Ceci sera interessant car elle nous permettra d'utiliser la plus importante des méthodes Begin de la classe SpriteBatch (voir ici pour de plus amples informations). Nous pourrons ainsi, effectuer des homothéties, des rotations, des translations beaucoup plus aisément que de tout gérer à la main.

CONCEPTION

Notre caméra va donc pouvoir se déplacer, tourner sur elle même et effectuer des zooms. Les zooms seront uniforme sur les 2 axes (X et Y). Au niveau des variables, nous aurons donc ceci :

  /// <summary> /// Represents the position of the camera. /// </summary> private Vector2 cameraPosition; /// <summary> /// Represents the zoom of the camera. /// </summary> private float zoom; /// <summary> /// Represents the rotation of the camera. /// </summary> private float rotation; 

Cependant, il nous faut aussi connaître la transformation (est-ce que la caméra a zommée, est-ce qu'elle s'est déplacé) que la caméra devra appliquer à notre monde. Cette transformation sera stocké dans un objet Matrix.

  /// <summary> /// Represents the transformation matrix. /// </summary> private Matrix transformationMatrix; 

Cette objet peut créer des translations, des rotations et des homothéties. Seulement, ces transformations sont définies comme des Vector3 or comme nous travaillons dans un monde 2D, nous, nous utiliserons des Vector2. Cependant pour convertir un Vector2 en Vector3, il suffira de laisser la composante Z du Vector3 à 0.

Il nous faudra donc :
- un objet de type Vector3 pour contenir la position de la caméra.
- un objet de type Vector3 pour contenir le facteur de zoom (homothétie) de la caméra.
- un objet de type Viewport pour contenir la "taille" du champ de vision de la caméra.

Dans un jeu à un seul joueur, le viewport vaut la zone de rendu de jeu (par exemple, si sur XBOX360, vous avez défini 720p comme style d'affichage, alors le viewport aura une taille de 1280x720 pixels). Mais si vous jouez à un jeu à 4 joueurs, il y'aura 4 viewports dans cette zone d'affichage.

Les 4 viewports auront la position et la taille suivante : 
- Viewport1 : positionné à (0,0) et de taille (640, 360)
- Viewport2 : positionné à (640, 0) et de taille (640, 360)
- Viewport3 : positionné à (0, 360) et de taille (640, 360)
- Viewport4 : positionné à (640, 360) et de taille (640, 360)

Vous voyez bien que les 4 viewports doivent évolués indépendamment. En effet, les 4 joueurs ne sont pas obligés d'avoir suivit le même chemin, il faut donc que la caméra sache quel viewport doit être mis à jour.

 /// <summary> /// Represents the position of the camera vector. /// </summary> private Vector3 cameraPositionVector; /// <summary> /// Represents the zoom of the camera in a vector. /// </summary> private Vector3 scaleVector; /// <summary> /// Represents the viewport vector. /// </summary> private Vector3 viewportVector; /// <summary> /// Viewport where the camera is set. /// </summary> private Viewport viewport; 

Au niveau des propriétés, nous devons en exposer quelques-unes :
- La matrice de transformation.
- L'angle de rotation.
- Le facteur de zoom.

 
  #region Properties /// <summary> /// Gets the transformation matrix. /// </summary> public Matrix TransformationMatrix { get { return transformationMatrix; } } /// <summary> /// Gets or sets the position of the camera. /// </summary> public Vector2 Position { get { return cameraPosition; } set { if (cameraPosition != value) { cameraPosition = value; UpdateTransformationMatrix(); } } } /// <summary> /// Gets or sets the angle of rotation in degrees. /// </summary> public float Rotation { get { return rotation; } set { if (rotation != value) { rotation = value; rotation %= 360; UpdateTransformationMatrix(); } } } /// <summary> /// Gets or sets the zoom of the camera. /// </summary> public float Zoom { get { return zoom; } set { if (zoom != value) { zoom = value; UpdateTransformationMatrix(); } } } /// <summary> /// Gets or sets the viewport of the camera. /// </summary> public Viewport Viewport { get { return viewport; } set { if(viewport.X != value.X || viewport.Y != value.Y || viewport.Width != value.Width || viewport.Height != value.Height) { viewport = value; UpdateTransformationMatrix(); } } }  #endregion 

Remarquez que la matrice de transformation est en lecture seule.
Ensuite pour éviter de perpétuellement rafraîchir la matrice de transformation, nous regardons si les nouvelles valeurs sont différentes ou non des valeurs précédentes. Si c'est le cas, nous mettons à jour les valeurs et la matrice de transformation.

Maintenant, il va nous falloir déplacer la caméra. Nous pourrons effectuer les déplacements suivants :
- à gauche
- à droite
- en haut
- en bas
- en diagonale haut/gauche
- en diagonale haut/droite
- en diagonale bas/gauche
- en diagonale bas/droite

Bien sûr les déplacements peuvent être plus ou moins prononcés. Donc il faudra tenir compte de ce paramètre lors de la création de nos méthodes. Lorsqu'un mouvement est effectué, il nous faudra mettre à jour notre matrice de transformation.

Les 4 premiers déplacements sont très simples; en effet nous ne faisons que nous déplacer sur l'axe X ou l'axe Y, indépendamment l'une de l'autre.

 /// <summary> /// Moves the camera to the left by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveLeft(float amount) { cameraPosition.X -= amount; UpdateTransformationMatrix(); } /// <summary> /// Moves the camera to the right by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveRight(float amount) { cameraPosition.X += amount; UpdateTransformationMatrix(); } /// <summary> /// Moves the camera to the up by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveUp(float amount) { cameraPosition.Y -= amount; UpdateTransformationMatrix(); } /// <summary> /// Moves the camera to the down by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveDown(float amount) { cameraPosition.Y += amount; UpdateTransformationMatrix(); } 

Ne pas oublier que la position (0,0) est le coin haut-gauche de l'écran de jeu.

Ensuite pour le déplacement sur les deux axes, nous avons deux possibilités :
- soit le déplacement est uniforme sur les deux axes.
- soit le déplacement est indépendant sur les deux axes.

Pour le déplacement haut/gauche cela donnerait ceci :

  /// <summary> /// Moves the camera to the up/left by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveUpLeft(float amount) { MoveUpLeft(amount, amount); } /// <summary> /// Moves the camera to the up by the specified  /// <paramref name="yAmount"/> and to the left by the specified  /// <paramref name="xAmount"/>. /// </summary> /// <param name="xAmount">Amount of displacement to the left.</param> /// <param name="yAmount">Amount of displacement to the up.</param> public void MoveUpLeft(float xAmount, float yAmount) { cameraPosition.X -= xAmount; cameraPosition.Y -= yAmount; UpdateTransformationMatrix(); } 

Grâce à ces deux méthodes, nous pouvons créer un déplacement uniforme de la caméra ou un déplacement indépendants. Ces méthodes nous permettent d'extraire très facilement les autres.

 /// <summary> /// Moves the camera to the up/right by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveUpRight(float amount) { MoveUpRight(amount, amount); } /// <summary> /// Moves the camera to the up by the specified  /// <paramref name="yAmount"/> and to the right by the specified  /// <paramref name="xAmount"/>. /// </summary> /// <param name="xAmount">Amount of displacement to the right.</param> /// <param name="yAmount">Amount of displacement to the up.</param> public void MoveUpRight(float xAmount, float yAmount) { cameraPosition.X += xAmount; cameraPosition.Y -= yAmount; UpdateTransformationMatrix(); } /// <summary> /// Moves the camera to the down/left by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveDownLeft(float amount) { MoveDownLeft(amount, amount); } /// <summary> /// Moves the camera to the down by the specified  /// <paramref name="yAmount"/> and to the left by the specified  /// <paramref name="xAmount"/>. /// </summary> /// <param name="xAmount">Amount of displacement to the left.</param> /// <param name="yAmount">Amount of displacement to the up.</param> public void MoveDownLeft(float xAmount, float yAmount) { cameraPosition.X -= xAmount; cameraPosition.Y += yAmount; UpdateTransformationMatrix(); } /// <summary> /// Moves the camera to the down/right by the specified  /// <paramref name="amount"/>. /// </summary> /// <param name="amount">Amount of displacement of the camera.</param> public void MoveDownRight(float amount) { MoveDownRight(amount, amount); } /// <summary> /// Moves the camera to the down by the specified  /// <paramref name="yAmount"/> and to the right by the specified  /// <paramref name="xAmount"/>. /// </summary> /// <param name="xAmount">Amount of displacement to the right.</param> /// <param name="yAmount">Amount of displacement to the up.</param> public void MoveDownRight(float xAmount, float yAmount) { cameraPosition.X += xAmount; cameraPosition.Y += yAmount; UpdateTransformationMatrix(); } 

Maintenant il ne nous reste plus qu'à écrire notre matrice de transformations. Si vous avez suivit le cours sur les matrices, ce qui va être écrit sera simple, sinon lisez l'article.

La matrice de transformation est donc écrite ainsi :

 /// <summary> /// Update the transformation matrix. /// </summary> private void UpdateTransformationMatrix() { cameraPositionVector.X = -cameraPosition.X; cameraPositionVector.Y = -cameraPosition.Y; scaleVector.X = zoom; scaleVector.Y = zoom; viewportVector.X = (viewport.Width >> 1) + viewport.X; viewportVector.Y = (viewport.Height >> 1) + viewport.Y; // Translation matrix. Matrix trans = Matrix.CreateTranslation(cameraPositionVector); // Rotation matrix. Matrix ro = Matrix.CreateRotationZ(MathHelper.ToRadians(rotation)); // Scale matrix. Matrix sc = Matrix.CreateScale(scaleVector); // Viewport matrix. Matrix vpm = Matrix.CreateTranslation(viewportVector); transformationMatrix = trans * sc *
ro * vpm; }

En premier lieu, nous translatons notre caméra à l'origine du monde, puis nous effectuons l'homothétie, suivit de la rotation et enfin nous la translatons vers la position désirée.

Et voilà notre caméra 2D est prête il ne nous restera plus qu'à créer quelques exemples pour la voir à l'oeuvre.

Stay tuned

@bientôt sur ce blog



Publié dans XNA

Pour être informé des derniers articles, inscrivez vous :
Commenter cet article