2

这是学校的作业。创建一个蛇游戏。所以我创建了两个包,一个用于图形,一个用于蛇。蛇移动平稳,一切正常。但我需要用键盘控制蛇。这是主要程序:

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;

在这行代码中,我控制了蛇头的旋转:

Move (S, x, y);  

其中 x 是 x 值,左侧可以为 -1,右侧可以为 1。
其中 y 是 y 值,它可以是 -1 表示向下,1 表示向上;

无论如何,如何在不暂停蛇移动的情况下读取输入?谢谢

4

2 回答 2

4

您可能想使用多任务系统来解决您的问题。

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;

在这样的系统中,您将绘图过程置于单独的任务中。在“选择”行,任务等待对 Input_Received() 的调用。如果在持续时间 D 后未调用此条目,则任务执行所有绘图函数,并重新开始循环。

希望它可以提供帮助。干杯。

于 2012-11-28T09:38:05.090 回答
2

有几种基本方法可以解决此类问题。

第一种是使用某种非阻塞调用来检查输入,但无论是否找到任何输入都会立即返回。正如 Simon Wright 在评论中提到的那样,Ada 提供了这样一个调用:Get_Immediate。这个例程的小缺点是(当我上次检查时)大多数编译器以仍然需要用户enter在输入可用于该例程之前敲击键的方式实现它。大多数操作系统也会对此类活动进行系统调用(没有那个烦人的输入键缺点),但是阻塞通常是他们的首选行为,因此设置非阻塞输入通常很困难或晦涩难懂。

我不知道你的那个Graphics包裹里有什么,但你可能想看看它是否有这样的电话。我猜这是一个学校编程作业,这是一个用于此作业的包。如果是这样,可能有一些设施可以做到这一点,或者你的教练没有想象你的游戏是这样工作的。

另一种方法是使用不同的控制线程来读取用户输入。这样,您可以在一个线程中使用操作系统喜欢的阻塞调用,并让您的游戏的其余部分像在另一个线程中一样愉快地运行。Ada 通过任务实现了这一点(如 tvuillemin 的回答所示)。这里的问题是这两个任务之间的任何交互(例如:将输入传递给游戏任务)都必须正确同步。此外,这两个任务使用的任何包或设施都必须是任务安全的。对于 Ada 包,只要您不尝试共享文件对象或其他东西,您就非常安全。但是对于第三方软件包(例如Graphics?),您通常最好只选择一项任务来“拥有”该软件包。

大多数窗口系统以极其复杂的方式解决了这个问题。他们实现了自己的“主循环”,它应该接管你的控制线程,它会处理日常任务,比如刷新窗口的内容和轮询输入。如果您想做一些自定义的事情(例如:定期更新游戏状态),您必须将其放入例程中并将其注册为“回调”。

于 2012-11-28T15:01:50.080 回答