ayumin.log

読みにくかったら脳内sedで整形してね

CUIのアニメ的なやつはどう作っているのか

プログレスバーを作りたい

きっかけはCUIツールを使っていた時、ふとVimプログレスバーのように、明らかにstdoutにprintするだけでは難しそうなものをどう作っているのか気になった。

また、CUIテトリスのようなものを作ろうとすると、ブロックが動くたびに下に新しく文字がprintされるのは明らかにおかしいので、どうやって同じ場所でアニメーションみたいに動きを表現しているのか気になっていた。

結論

結論から言うと、エスケープシーケンスでカーソルを移動し、再度同じところに出力し直している。

詳細はANCI Escape sequenceを見ると分かるが、その中に\033[nA というものがある。nには数字が入る。なお \033 の代わりに ^ と記載されている場合もあるが、

例えば \033[3A だとカーソルを3行上に移動する。

ここで注意すべき点は、エスケープシーケンスを出力している行も1行とカウントされるということ。 例えば2行の絵を表示させ続けたいからといって、\033[2A としてしまうとうまくいかない。

fmt.Println("1")
fmt.Println("2")
fmt.Println("\033[2A")

とすると、標準出力は

> go run main.go
1
> (入力待ち)

という状態になる。

なお、このエスケープシーケンスによって、標準出力が消えているわけではない。 というか標準出力はストリームであり、データの流れを今のカーソルの位置に出力し続けるに過ぎない。

アニメーションが同じような絵を同じ場所でパラパラとめくるのと理屈は全く同じで、データを消しているわけではなく、同じところに再度出力し直しているだけである。

感想

初歩的なことかもしれないが、標準出力にカーソル位置という概念があることを知らなかった。

カーソル位置という概念がなければ、printなどで命令を受け取った時に出力場所が定まらないので、言われてみればない方が不自然だった。

またエスケープシーケンスというと、\n 辺りはCを書いたことがある人なら誰しも書いたことあると思うが、画面制御で使う印象がなかった。

こういう見落としてること割とあるので気を付けなくては。

あと、これを調べる時に検索ワードが思いつかなったので、CUI アニメ とか C言語 テトリス とかで調べた。

使いそうな場面

やはりプログレスバーが一番あり得そう。あとはCUIゲーム作ったりするときにも使える。

プログレスバーはこの記事が分かりやすい。今度作ってみる。

t-keita.hatenadiary.jp