2

私はPHPで書かれた、コンソールで実行されるゲームに取り組んでいます。古いMUDやその他のテキストベースのゲーム、さらにはASCIIアートを思い出してください。

とにかく、私がやろうとしているのは、ユーザー入力を受け入れながら何かが起こっていることです。

たとえば、2人用ゲームで、プレーヤー1がプレーヤー2の動きを待っているとします。これは、メッセージを聞くだけで簡単に実行できます。

しかし、プレーヤー1がいくつかのオプションを変更したい場合はどうなりますか?ゲームの状態の詳細を表示したい場合はどうなりますか?ゲームを譲歩するのはどうですか?対戦相手が動くのを待っている間にプレイヤーがやりたいことがたくさんあります。

残念ながら、私が今持っている最高のものは、Ctrl+Cがプログラムを完全に強制終了するという事実です。その後、接続が切断されるまで、他のプレーヤーはぶら下がったままになります。ああ、そしてゲームは完全に失われました。

でユーザー入力を取得しますfgets(STDIN)。ただし、これは入力が受信されるまで実行をブロックします(これは通常は良いことです)。

このようなコンソールプログラムで入力と出力を同時に処理することも可能ですか?それとも、他のインターフェイスを見る必要がありますか?

4

3 に答える 3

5

要するに、PHPはこのために構築されていませんが、これらの拡張機能の1つから助けを得ることができるかもしれません。それらがどれほど徹底的かはわかりませんが、おそらくテキストUIライブラリを使用したいと思うでしょう。(そして実際には、おそらくこれにPHPを使用したくないでしょう。)

STDINとはいえ、文字ごとにノンブロッキング入力を取得する必要があります。残念ながら、ほとんどの端末はPHPの観点からバッファリングされているため、Enterキーを押すまで何も取得できません。

ターミナルで(またはOSに相当するものを)実行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ます。Enterキーが押された場合は、処理します$line。その間、入力を待っている間に何か他のことをしている可能性があることを示すために、5秒ごとにカチカチ音をたてています。(これは最大のCPUを消費します。それsleep()を回避するには、を発行する必要があります。)

これは、それ自体が実際的な例となることを意図したものではありませんが、おそらく適切な方向に考えさせるでしょう。

于 2012-06-14T03:17:23.003 に答える
1

ncurses(非ブロッキングモード)とlibeventを使用して、説明したようなゲームを構築することができます。そうすれば、CPUをほとんど消費しなくなります。個々のキーの処理は厄介な場合があり(Backspaceを自分で実装するのはまったく面白くありません。また、さまざまなOSがBackspaceのプレスで異なるキーコードを送信することを知っていましたか?)、UTF-8を適切にサポートしたい場合は非常に注意が必要です。それでも、完全に実行可能です。

特に、libeventを使用してネットワークとキーボード(stdin)入力の両方を読み取ることにより、libeventを広範囲に使用することが有益です。この関数を使用すると、個々のキーをリッスンできます: http : //www.php.net/manual/en/function.ncurses-cbreak.php後でlibeventAPIを使用して読み取ることができます。覚えておくべき重要な点は、一度に複数のキーを読み取ることになり、それを処理する必要があることです(したがって、読み取ったすべてのものをループします)。そうしないと、すべてのキーの押下がアプリケーションに「到達」しているわけではなく、一部が失われていることにユーザーはイライラします。

于 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 に答える