52
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[PHP]ゲーム作成初心者がPHPでテト〇ス風ゲームを作ってみた

Last updated at Posted at 2017-10-31

作ってみた

名前は TETRICE です!

image.gif

リポジトリはこちら

なんで?

どっかの質問サイトでPHPでゲームは作れますか?作るにはどうしたらいいですか?ってQに対するAが作れません or Javascript で作りますみたいな内容だらけだったのに触発されただけ。

ちなみにゲーム作ったことはほぼないです。

いきなりまとめ

使ったことないけどCUIでグラフィカルなことしたいんだったらtermbox使った方がいいと思う。
(この記事全否定)
PHPライブラリyum install -y php-pecl-termbox

スペルミスじゃないよ。
http://hot-topic-news.com/tetris-copyright

[暇な人用]にっき

以下時系列で。

標準入力のノンブロッキング化

普通にキーボードの入力受けようとするとその時点でブロックされてしまう。
(キーボード入力待ちみたいな状態)
画面垂れ流しのまま標準入力を受けようとするとnon-blockingにしないといけない。

  1. fcntl() で nonblocking にしてやろうとする
  2. dio_fcntl() 『注意: この関数は Windows 環境にはまだ実装されていません。』
  3. 色々ぐぐって stream_set_blocking(STDIN, FALSE);見つけた。
  4. 組み込んでみるもなんか動かない。
  5. 動かない報告いっぱいあった。

最終的に stream_select のタイムアウトを0にして標準入力を監視する方式にした。

src/app/Controller.php
<?php
class Controller implements LoopProcessInterface
{
    private function readNonBlock(&$data) {
        $read = [STDIN];
        $write = [];
        $except = [];

        $result = stream_select($read, $write, $except, 0);

        if($result === false) {
            throw new \Exception('stream_select failed');
        }

        // Cygwinだとここが必ず1になるから`stream_get_contents`でブロック状態になってしまう・・・
        if($result === 0) {
            return false;
        }

        $data = stream_get_contents(STDIN);

        return true;
    }

    public function loopProcess()
    {
        $data = "";
        if ($this->readNonBlock($data)) {
            echo $data;
        }
    }
}

stream_select で変更があれば 1、変更がなければ 0 が返る。
1の時に入力内容を取得してブロック状態にならないようにしたかったのだが、Cygwin上だと必ず1が返る/(^o^)\(未解決)

ユーザー入力値の制御

  • 標準入力をエンター押すまでじゃなくて押下した段階で取りたい
  • ユーザーの入力をコンソールに出さない

右とか左とかならまだしもスペースとか入力した値を表示してしまうと画面表示時にずれちゃう。なのでパスワードの入力時みたいにユーザーの入力値を非表示にしたい。

正直気持ち悪いが stty コマンド呼び出して対応・・・
shell_exec("stty -icanon -echo")

プログラム終了時に元のターミナルに戻っても適用されたままなので元に戻す処理を組み込み。
try - finally で戻す場合、開発中にexit したときとかfinallyが呼ばれない。
ただしデストラクタは呼ばれるのでそっち側に組み込んだ。

画面の描画

とりあえず画面クリアして書き込めばいいんだろとか思って clear して echo したところちらつきと出力ログ大量みたいなことになった。やりたいこととしては top コマンドに近い感じなので標準出力の内容をカーソル移動して書き換える形にした。

こんな感じでANSIエスケープシーケンスで戻って上書き可能。

sample.php
<?php
for ($i = 1; $i <= 5; $i++) {
    $str = "--- {$i}/5 ---\n";
    echo $str;

    // カーソルを一つ上にずらす
    echo "\e[1A";

    sleep(1);
}

cursor.gif

この部分書き換え+flush()を駆使することで意図通り綺麗にでるようになった。

他にもANSIエスケープシーケンスでブロックの色を表現したりした。
(スペース2つ+背景色を変更)

その他

  • ゲーム部分実装をかなりやっつけで作ってしまった感がある。
    テトリスのアルゴリズムちゃんとやるとかなりしんどそうね。
  • ブロックの種類的に1色足りなかった・・・
  • 完全にプロトタイプ作りながらなのでUnitテストがかなり後発になってしまった。
  • TravisCI + Coveralls 導入した
52
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?