2

我正在开发一个用 PHP 编写并在控制台中运行的游戏。回想一下旧的 MUD 和其他基于文本的游戏,甚至是一些 ASCII 艺术!

无论如何,我想做的是在接受用户输入的同时让事情发生。

例如,假设这是一个两人游戏,玩家 1 正在等待玩家 2 采取行动。这很容易通过只听消息来完成。

但是如果玩家 1 想要改变一些选项呢?如果他们想查看游戏状态各个方面的详细信息怎么办?丢球怎么办?在等待对手采取行动时,玩家可能想做很多事情。

不幸的是,我现在拥有的最好的是 Ctrl+C 完全杀死程序。然后让另一个玩家挂起,直到连接断开。哦,游戏完全输了。

我得到用户输入fgets(STDIN)。但这会阻止执行,直到收到输入(这通常是一件好事)。

像这样的控制台程序甚至可以同时处理输入和输出吗?还是我应该只看其他界面?

4

3 回答 3

5

简而言之,PHP 不是为此而构建的,但您可能会从这些扩展之一中获得一些帮助。我不确定它们有多彻底,但您可能真的想使用文本 UI 库。(实际上您可能不想为此使用 PHP。)

STDIN综上所述,您需要逐个字符地获取非阻塞输入。不幸的是,从 PHP 的角度来看,大多数终端都是缓冲的,所以在按下 enter 之前你什么也得不到。

如果您stty -icanon在终端上运行(或您的操作系统的等效程序)以禁用缓冲,那么以下短程序基本上可以工作:

<?php

stream_set_blocking(STDIN, false);

$line = '';

$time = microtime(true);

$prompt = '> ';

echo $prompt;

while (true)
{
  if (microtime(true) - $time > 5)
  {
    echo "\nTick...\n$prompt$line";
    $time = microtime(true);
  }

  $c = fgetc(STDIN);
  if ($c !== false)
  {
    if ($c != "\n")
      $line .= $c;
    else
    {
      if ($line == 'exit' || $line == 'quit')
        break;
      else if ($line == 'help')
        echo "Type exit\n";
      else
        echo "Unrecognized command.\n";

      echo $prompt;
      $line = '';
    }
  }
}

(它依赖于启用本地回显来在键入字符时打印字符。)

如您所见,我们只是永远循环。如果存在字符,请将其添加到$line. 如果按下回车,则处理$line。同时,我们每五秒打勾,只是为了表明我们在等待输入时可以做其他事情。(这将消耗最大的 CPU;你必须发出 asleep()来解决这个问题。)

这本身并不是一个实际的例子,但也许会让你朝着正确的方向思考。

于 2012-06-14T03:17:23.003 回答
1

可以使用 ncurses(非阻塞模式)和 libevent 构建像您描述的游戏。这样,您几乎没有 CPU 消耗。处理单个键有时很尴尬(自己实现 Backspace,它一点也不好玩 - 你知道各种操作系统在 Backspace 按下时发送不同的键码吗?),如果你想正确支持 UTF-8,就会变得非常棘手。不过,完全可行。

特别是,通过读取网络和键盘 (stdin) 输入来广泛使用 libevent 是有益的。此函数使您能够侦听单个键: http ://www.php.net/manual/en/function.ncurses-cbreak.php ,稍后您可以使用 libevent API 读取。要记住的关键是,有时您最终会一次阅读超过 1 个密钥,并且必须对其进行处理(因此遍历您已阅读的所有内容)。否则,用户会恼火地看到并非所有按键都“到达”应用程序并且有些按键丢失了。

于 2012-06-22T10:58:56.683 回答
0

抱歉,马修,我将不得不不接受您的回答,因为我自己找到了:

使用以下代码在执行其他操作的同时接收用户输入:

while(/* some condition that the code running is waiting on */) {
    // perform one step or iteration of that code
    exec("choice /N /C ___ /D _ /T _",$out,$ret);
    // /C is a list of letters that do something
    // /D is the default action that will be used as a no-op
    // /T is the amount of time to wait, probably best set to one second
    switch($ret) {
        // handle cases - the "default" case should be "continue 2"
    }
}

然后可以使用它来中断循环并进入选项菜单,或触发其他事件,或者如果使用正确,甚至可以用于输入命令。

于 2012-07-12T13:07:29.953 回答