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なページが ありました ので、参考にさせていただきます。なるほど、部屋と部屋を結ぶ方法は意外と単純だったのですね。線を引くのにジェネレーターを使うのはよいアイデアと思いました。