cazh-CNendeeliwhiiditjakoptrues

     

 

  

 

Programmation graphique

Chapitre 6 : Les Sprites : Animation et accélération

 

Tutoriel présenté par : Robert Gillard (Gondulzak)
Publication : 01 février 2014
Dernière mise à jour : 22 novembre 2015

 

      Concept d'Accélération appliqué à l'Animation

   Dès que vous pressez les touches gauche/droite de votre clavier/gamepad, vous voyez notre motocycliste avancer dans la direction que vous choisissez mais ce, à une vitesse constante dès l'affichage de la première frame ! surprise

   Si un avion ou un mobile quelconque traverse votre écran d'un côté à l'autre et disparaît vers le côté opposé de celui duquel il était apparu, cela n'a pas grande importance. Mais si nous démarrons une animation à l'intérieur de la fenêtre de jeu, celà ne semblera pas naturel. Un bon exemple est de vous demander d'essayer de courir un 100 mètres en ayant atteint votre vitesse maximum dès le départ... indecision Et bien cela est la même chose sur l'écran lorsque vous démarrez une animation dont la vitesse est constante et dans certains cas, nous devrons effectuer une accélération / décélération sur notre animation. wink

   Mais je vous rassure, point n'est besoin ici d'appliquer des formules de cinématique relatives aux mouvements uniformément accélérés ou retardés. Alors, comment procéder ? angel

   Nous allons voir le code dans quelques instants. Nous pourrions intégrer celui-ci dans la fonction UpdateHero() de notre projet précedent mais ne tenant pas à trop modifier nos classes actuelles en vue de les réutiliser dans notre jeu à venir, nous allons créer un nouveau projet, très court comme vous allez le voir, mais qui nous permettra d'imprimer une réelle accélération à notre sprite ainsi qu'une décélération remarquée dès que nous relâcherons la touche de direction. 

 

 

   Ne voulant pas vous faire attendre plus longtemps, je vous demanderai de bien vouloir créer un nouveau projet que vous nommerez « DriveIn2 ». Nous reprendrons bien entendu le background ainsi que le sprite du projet DriveIn (que vous retrouverez ci-dessous par commodité wink) :

 

   Dans la classe Game1, juste en dessous de SpriteBatch spriteBatch;, ajoutez les variables suivantes :

 

        
        //Données source
        private Texture2D background;
        private Texture2D animationTexture;
        private Rectangle currentFrameLocation;

        //Données de destination
        private Vector2 spritePosition;
        private Vector2 spriteVelocity;
        private Vector2 maxSpriteVelocity;


        //Données pour l'animation
        private int currentFrame;
        private int numberOfFrames;

        private int timeUntilNextFrame;	    // toujours en milisecondes
        private int millisecondsPerFrame;   // toujours en milisecondes

        private SpriteEffects spriteEffet;

 

   Certaines de ces variables demandent une explication wink :


   Nous déclarons une variable spriteVelocity qui gérera l'accélération ainsi que maxSpriteVelocity qui sera la vitesse maximale que pourra atteindre notre motocycliste, timeUntilNextFrame étant le temps restant jusqu'à l'affichage de la frame suivante.

   Inutile de toucher à notre constructeur, occupons-nous de la fonction Initialize(). Supprimez celle-ci et remplacez-la par la suivante: 

 

        
        //Fonction d'initialisation   
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            numberOfFrames = 4;
            currentFrame = 0;

            millisecondsPerFrame = 20;
            timeUntilNextFrame = millisecondsPerFrame;

            currentFrameLocation.X = 0;
            currentFrameLocation.Y = 0;
            currentFrameLocation.Width = 80;	//largeur du sprite
            currentFrameLocation.Height = 55;	//hauteur du sprite

            spritePosition = new Vector2(150, 390);
            spriteVelocity = new Vector2(0, 0);
            maxSpriteVelocity = new Vector2(8, 0);

            spriteEffet = SpriteEffects.FlipHorizontally;

            base.Initialize();
        }

 

   Dans cette fonction, je vous ferai juste remarquer la variable représentant l'accélération de type Vector2 qui bien entendu a une valeur nulle au départ (0, 0). Nous donnons de même une valeur à la vitesse maximale du sprite maxSpriteVelocity (8, 0), représentée elle aussi par une variable de type Vector2. Et pour terminer nous donnons un effet renversé horizontal à notre variable spriteEffect.

  Dans la fonction LoadContent, vous ajouterez simplements nos assets nécessaires à l'animation et à l'affichage. Entrez les deux lignes suivantes en dessous de spriteBatch = new SpriteBatch(GraphicsDevice);

 

  animationTexture = Content.Load<Texture2D>("moto");
  background = Content.Load<Texture2D>("Background22bis");

 

   Et comme vous vous en doutez, toute la logique de cette animation va se faire dans la fonction Update(). Supprimez cette fonction et remplacez-la par celle-ci

 

protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
       this.Exit();
     timeUntilNextFrame -= gameTime.ElapsedGameTime.Milliseconds;
 
     if (Keyboard.GetState().IsKeyDown(Keys.Left))
     {
         if (spriteVelocity.X > -maxSpriteVelocity.X)
         {
             spriteVelocity.X -= 0.03f;
             spriteEffet = SpriteEffects.None;
         }
     }
     else if (Keyboard.GetState().IsKeyDown(Keys.Right))
     {
         if (spriteVelocity.X < maxSpriteVelocity.X)
         {
             spriteVelocity.X += 0.03f;
             spriteEffet = SpriteEffects.FlipHorizontally;
          }
     }
     else
     {
          spriteVelocity *= 0.97f;
     }
     spritePosition += spriteVelocity;
    float relativeVelocity = Math.Abs(spriteVelocity.X / maxSpriteVelocity.X);
    if (relativeVelocity > 0.03f)
    {
         if (timeUntilNextFrame <= 0)
         {
             currentFrame++;
             timeUntilNextFrame = (int)(millisecondsPerFrame * (2.0f -
                                                        relativeVelocity));
         }
     }
     if (currentFrame >= numberOfFrames)
         currentFrame = 0;
     currentFrameLocation.X = currentFrameLocation.Width * currentFrame;
     base.Update(gameTime);
}

 

   Voyons maintenant tout ce que ceci signifie. smiley

  Nous avons besoin de faire un lien entre millisecondsPerFrame et la vitesse du sprite. Nous allons donc supposer que la plus petite valeur de timeUntilNextFrame est la valeur définie auparavant dans millisecondsPerFrame et que la plus grande valeur de timeUntilNextFrame est disons, deux fois cette valeur.

    Nous devons donc modifier la valeur que nous avons assignée à timeUntilNextFrame basée sur la vitesse relative.

    Mais qu'entendons-nous par vitesse relative ? devil

   La vitesse relative est déterminée comme étant la valeur absolue du quotient de la vitesse actuelle / la vitesse maximum et qui détermine le nombre de millisecondes jusqu'à la prochaine frame.


   TimeUntilNextFrame = millisecondsPerFrame x (1.0 + ( 1.0 – (vitesse actuelle/vitesse maximum)))

 

   Ceci est valable tant que le sprite est en mouvement, mais dès l'instant que vous relâchez la touche directionnelle, vous voudriez que l'animation s'arrête également. A cet effet nous calculons la vitesse relative un peu plus tôt (spriteVelocity *= 0.97f ) en utilisant une instruction conditionnelle.

   Il ne nous reste plus qu'à réecrire la fonction Draw() qui n'a plus de secrets pour vous maintenant (cf. chapitre précédent)... wink
   Supprimez cette fonction et remplacez-la par celle-ci

 

        
        protected override void Draw(GameTime gameTime)
        {
            //GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Ajoutez le code du dessin ici
            spriteBatch.Begin();

            spriteBatch.Draw(background, Vector2.Zero, Color.White);

            spriteBatch.Draw(animationTexture,
                            spritePosition,
                            currentFrameLocation,
                            Color.White,
                            0.0f,           //Rotation
                            Vector2.Zero,   //Origine
                            1.0f,           //Echelle
                            spriteEffet,    //Effet
                            1.0f);          //Profondeur
            spriteBatch.End();

            base.Draw(gameTime);
        }

 

   Il m'est bien entendu impossible de vous montrer cette animation à l'intérieur de ce tutoriel mais je vous laisse regarder ce bel effet d'accélération et de décélération sur votre propre écran. A ce sujet je vous invite à faire quelques modifications sur les valeurs des variables concernant la mise à jour de la fonction Update() afin de trouver l'animation qui vous convient le mieux pour un projet éventuel. wink

 

   Afin de voir l'importance de cette animation, je vous suggère de faire défiler le sprite animé d'un côté ou l'autre de la fenêtre de votre écran, après avoir doublé sa vitesse par exemple, (16,0) et en le laissant sortir pendant quelques secondes de cette même fenêtre. Faites-le alors revenir en maintenant la touche opposée jusqu'à ce que vous aperceviez le sprite apparaitre à nouveau. Lâchez aussitôt la touche directionnelle sur laquelle vous êtes en train de presser et vous pourrez alors voir le sprite animé s'arrêter après avoir encore parcouru pratiquement toute la largeur de la fenêtre et ce, avec un ralentissement significatif.

   En outre, en guise d'exercice, vous pourriez essayer de modifier la fonction UpdateHero() de la classe Game1 du précédent projet DriveIn afin d' y intégrer ce concept d'accélération. cool

 


   Les différents effets d'accélération/décélération que vous pourriez implémenter dans vos propres projets sont relativement nombreux. Je pense bien entendu au démarrage et à l'arrêt d'une voiture ou d'une moto mais aussi d'un personnage qui court. D'autres effets très intéressants pourraient être également le décollage ou l'atterrissage d'un hélicoptère ainsi que la chute d'un personnage en parachute, etc...


   Le prochain chapître portera sur une conception un peu plus élaborée des effets de profondeur, sur la mise en place d'une simple caméra et sur les effets de zoom.

   Je vous demanderai de bien vouloir me présenter vos remarques éventuelles ainsi que toutes autres considérations sur le forum Programmation C#/Xna, dans le sujet des «Tutos Xna en français»

   Je vous souhaite une bonne lecture et à bientôt pour de nouveaux tutoriels Xna. wink

 

 

 

Connexion