Table of Contents
Rogue + Wizライクプロジェクトの2回目はキー入力と画面表示です。
1 ノンブロッキングキー入力
まずはリアルタイムキースキャンから。これがいきなり曲者で、簡単には実現できないようです。
Windowsではmsvcrtというライブラリを使うみたいです。msvcrt.getch()は使い勝手がよさそう。しかし私の手元にはMacしかないので、別のやり方を探してみます。検索すること数十分、いちおう標準ライブラリの(?)termiosを使うやり方がいいのかな。StackoverflowのこのQ&A に例が出ています。引用します。
import termios, fcntl, sys, os fd = sys.stdin.fileno() oldterm = termios.tcgetattr(fd) newattr = termios.tcgetattr(fd) newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO termios.tcsetattr(fd, termios.TCSANOW, newattr) oldflags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK) try: while 1: try: c = sys.stdin.read(1) if c: print("Got character", repr(c)) except IOError: pass finally: termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm) fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
Windowsよりもずいぶん準備や後処理が面倒だし、tryブロックがネストしていて微妙な感じですが、試したところ十分使えそうです。これをお借りしましょう。
2 任意位置への文字表示
次は画面上の指定位置への文字表示です。昔懐かしのBASIC言語でいうところのLOCATEに相当することがしたいです。検索でだいぶ手間取りましたが、思いのほか簡単に実現できることがわかりました。ANSIエスケープシーケンスで指定するのですね。
print(f"\033[{y};{x}H@", end='')
これでx, yの位置にアットマーク('@')を表示します。バックスラッシュ('\')から'H'までが位置指定のエスケープシーケンスです。ここでは1文字しか表示していませんが、2文字以上の文字列を表示させることももちろんできます。
簡単な性能テストをしてみます。
import random import time while True: c = random.choice([b'1',b'2',b'3',b'4',b'5',b'6',b'7',b'8',b'9']) start = time.time() for y in range(25): print(f"\033[{y};0H", end='') line = bytearray(c*60) print(line.decode()) delta = time.time() - start print(f"{delta:.3f}")
これはランダムな数字を60x25文字の表示エリアに一様に表示することを繰り返します。実際のゲームと同様にbytearrayで持つ1行ごとのデータを、文字列にデコードしながら1回のプリント文で表示しています。
実行したところ、十分に高速でした。描画しているところはほとんど見えず、実行時間は1画面あたり100分の一秒未満でした。行けそうです。
3 次回
次はローグライクなランダムマップの自動生成に挑戦します。ありがたいことにRoguelike Tutorialなページが ありました ので、参考にさせていただきます。なるほど、部屋と部屋を結ぶ方法は意外と単純だったのですね。線を引くのにジェネレーターを使うのはよいアイデアと思いました。