user1012032
user1012032

Reputation:

How to read the input without pausing the snake game?

This is an school assignment. To create a snake game. So I have created two packages one for graphics and one for the snake. The snake moves smoothly and everything works. But I need to control the snake with the keyboard.This is the main procedure:

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;

in this line of code I controll the snakes head rotation:

Move (S, x, y);  

where x is the x value it can be -1 for left, 1 for right.
where y is the y value it can be -1 for down, 1 for up;

Anyways, how can I read the input without pausing the snake from moving? Thanks

Upvotes: 2

Views: 601

Answers (2)

T.E.D.
T.E.D.

Reputation: 44804

There are a couple of basic approaches to solving this sort of problem.

The first is to use some kind of non-blocking call that checks for input, but returns immediately whether it finds any input or not. As Simon Wright mentioned in the comments, Ada provides such a call: Get_Immediate. The little nit with this routine is that (when last I checked) most compilers implement it in a way that still requires the user to hit the enter key before their input is available to that routine. Most OS's will have a system call for such an activity too (without that annoying enter key drawback), but blocking is usually their preferred behavior, so setting up non-blocking input is often difficult or arcane.

I don't know what's in that Graphics package of yours, but you may want to look through it to see if it possesses such a call. I'm guessing this is a school programming assignment, and that is a package built to use with this assignment. If so, there's probably some facility in there for this, or else your instructor did not picture your game working that way.

The other approach is to use a different thread of control for reading user input. That way you can use the blocking calls that OS's love in one thread, and let the rest of your game merrily chug away as it should in the other. Ada implements this with tasks (as shown in tvuillemin's answer). The issue here is that any interactions between those two tasks (eg: passing the input to the game task) has to be properly synchronized. Also, any package or facility used by both tasks must be task-safe. For Ada packages you are pretty safe, as long as you don't try to share file objects or something. But for third party packages (like Graphics?) you are generally best just picking one task to "own" that package.

Most windowing systems solve this problem in an incredibly complex way. They implement their own "main loop" that is supposed to take over your thread of control, and it will take care of mundane duties like refreshing the contents of windows and polling for input. If you want to do something custom (eg: update game state periodically), you have to put it in a routine and register it as a "callback".

Upvotes: 2

tvuillemin
tvuillemin

Reputation: 1148

You might want to use a multitasking system to overcome your problem.

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;

In such a system, you have your drawing process in a separate task. At the "select" line, the task waits for a call to Input_Received(). If this entry is not called after a duration D, then the task executes all the drawing functions, and the loop begins anew.

Hope it can help. Cheers.

Upvotes: 3

Related Questions