2 votes

Comment lire l'entrée sans mettre en pause le jeu du serpent ?

C'est un travail scolaire. Créer un jeu de serpent. J'ai donc créé deux paquets, un pour les graphiques et un pour le serpent. Le serpent se déplace en douceur et tout fonctionne. Mais j'ai besoin de contrôler le serpent avec le clavier, c'est la procédure principale :

with Graphics; use Graphics;
with Graphics.Snake; use Graphics.Snake;

procedure Run_Snake is
   B : Buffer (1 .. 24, 1 .. 80);
   S : Snake_Type (1 .. 5) := ((10, 10),
                               (10, 11),
                               (10, 12),
                               (11, 12),
                               (12, 12));
   D : Duration := 0.07;
begin

  loop
      Empty (B);
      Draw_Rect (B, (1, 1), Width  => 80,
                 Height            => 24);
      Draw (B, S);
      Update (B);

      Move (S, 0, -1);
      delay D;

   end loop;

end Run_Snake;

dans cette ligne de code je contrôle la rotation de la tête du serpent :

Move (S, x, y);  

où x est la valeur de x ; elle peut être -1 pour la gauche, 1 pour la droite.
où y est la valeur y ; elle peut être -1 pour la baisse, 1 pour la hausse ;

Quoi qu'il en soit, comment puis-je lire l'entrée sans interrompre le mouvement du serpent ? Merci

4voto

tvuillemin Points 1098

Vous pouvez utiliser un système multitâche pour résoudre votre problème.

procedure Snake_Game is

   task Run_Snake is
      entry Input_Received (S : Snake_Type; x : Integer; y : Integer);
   end Run_Snake;

   task body Run_Snake is
      D : constant Duration := 0.07;
      B : Buffer (1 .. 24, 1 .. 80);
   begin
      loop
         select
            accept Input_Received (S : Snake_Type; x : Integer; y : Integer) do
               Move (S, x, y);
            end;
         or
            delay D;
            Empty (B);
            Draw_Rect (B, (1, 1), Width => 80, Height => 24);
            Draw (B, S);
            Update (B);
         end select;
      end loop;
   end Run_Snake;

   S : Snake_Type (1 .. 5) := ((10, 10),
                              (10, 11),
                              (10, 12),
                              (11, 12),
                              (12, 12));

begin
   loop
      Do_Whatever_You_Want_To_Get (x, y)
      Run_Snake.Input_Received(S, x, y);
   end loop;
end Snake_Game;

Dans un tel système, votre processus de dessin fait l'objet d'une tâche distincte. À la ligne "select", la tâche attend un appel à Input_Received(). Si cette entrée n'est pas appelée après une durée D, alors la tâche exécute toutes les fonctions de dessin, et la boucle recommence.

J'espère que cela pourra vous aider. A la vôtre.

2voto

T.E.D. Points 26829

Il existe deux approches de base pour résoudre ce type de problème.

La première consiste à utiliser une sorte d'appel non bloquant qui vérifie l'entrée, mais qui retourne immédiatement, qu'il y ait une entrée ou non. Comme Simon Wright l'a mentionné dans les commentaires, Ada fournit un tel appel : Obtenir_Immédiat . Le petit problème avec cette routine est que (la dernière fois que j'ai vérifié) la plupart des compilateurs l'implémentent d'une manière qui nécessite toujours que l'utilisateur appuie sur la touche enter avant que leur entrée ne soit disponible pour cette routine. La plupart des systèmes d'exploitation disposent d'un appel système pour ce type d'activité (sans l'inconvénient ennuyeux de la touche Entrée), mais le blocage est généralement le comportement qu'ils préfèrent, de sorte que la mise en place d'une entrée non bloquante est souvent difficile ou obscure.

Je ne sais pas ce qu'il y a là-dedans. Graphics de votre paquet, mais vous pouvez y jeter un coup d'oeil pour voir s'il possède un tel appel. Je suppose qu'il s'agit d'un travail de programmation scolaire et qu'il s'agit d'un paquetage construit pour être utilisé dans le cadre de ce travail. Si c'est le cas, il y a probablement une possibilité de le faire, ou bien votre instructeur n'a pas imaginé que votre jeu fonctionne de cette façon.

L'autre approche consiste à utiliser un autre fil de contrôle pour lire les entrées de l'utilisateur. De cette façon, vous pouvez utiliser les appels bloquants que les systèmes d'exploitation adorent dans un thread, et laisser le reste de votre jeu se dérouler joyeusement comme il se doit dans l'autre. Ada implémente cela avec des tâches (comme indiqué dans la réponse de tvuillemin). Le problème ici est que toute interaction entre ces deux tâches (par exemple : passer l'entrée à la tâche de jeu) doit être correctement synchronisée. De plus, tout paquet ou installation utilisé par les deux tâches doit être sûr pour la tâche. Pour les paquets Ada, vous êtes plutôt en sécurité, tant que vous n'essayez pas de partager des objets de fichiers ou autre. Mais pour les paquets tiers (comme Graphics ?) il est généralement préférable de choisir une seule tâche pour "posséder" ce paquet.

La plupart des systèmes de fenêtrage résolvent ce problème d'une manière incroyablement complexe. Ils implémentent leur propre "boucle principale" qui est censée prendre en charge votre fil de contrôle, et qui s'occupera de tâches banales comme le rafraîchissement du contenu de Windows et la recherche d'entrées. Si vous voulez faire quelque chose de personnalisé (par exemple, mettre à jour l'état du jeu périodiquement), vous devez le mettre dans une routine et l'enregistrer comme "callback".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X