我正在开发一款用PHP编写并在控制台中运行的游戏。回想一下旧的MUD和其他基于文本的游戏,甚至是一些ASCII艺术!
无论如何,我正在尝试做的事情是在接受用户输入的同时发生事情。
例如,让我们说这是一个双人游戏,而玩家1正在等待玩家2进行移动。只需收听消息即可轻松完成。
但是如果玩家1想要改变一些选择呢?如果他们想要查看有关游戏状态方面的详细信息怎么办?承认游戏怎么样?在等待对手进行移动时,玩家可能想做很多事情。
不幸的是,我现在最好的是Ctrl + C完全杀死程序。然后另一个玩家被挂起,直到连接断开。哦,游戏完全丢失了。
我通过fgets(STDIN)
获得用户输入。但这会阻止执行直到收到输入(这通常是一件好事)。
这样的控制台程序是否可以同时处理输入和输出?或者我应该只看一些其他界面?
答案 0 :(得分:3)
简而言之,PHP不是为此而构建的,但您可以从these extensions之一获得一些帮助。我不确定它们有多彻底,但你真的可能想要使用文本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
。与此同时,我们每5秒钟就会发出一声,只是为了表明我们在等待输入时可能会做其他事情。 (这将消耗最大的CPU;您必须发出sleep()
来解决这个问题。)
这本身并不是一个实际的例子,但也许会让你思考正确的方向。
答案 1 :(得分:0)
可以使用ncurses(非阻塞模式)和libevent构建一个类似于你描述的游戏。这样,您就可以接近没有CPU消耗。处理单个密钥有时很尴尬(自己实现Backspace,它根本不是很有趣 - 你知道各种操作系统在Backspace上发送不同的密码吗?),如果你想要正确支持UTF-8,那就非常棘手。仍然,完全可行。
特别是,通过读取网络和键盘(stdin)输入来广泛使用libevent是有益的。此功能使您可以侦听单个键: http://www.php.net/manual/en/function.ncurses-cbreak.php 您可以稍后使用libevent API读取它。要记住的关键是,您有时会一次读取多于一个键,并且必须进行处理(因此循环读取您阅读的所有内容)。否则,用户会恼火地看到并非所有按键都“到达”应用程序而有些按键丢失。
答案 2 :(得分: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"
}
}
然后可以使用它来中断循环并进入选项菜单,或触发其他一些事件,或者如果使用正确,甚至可以用来输出命令。