はじめに
今回はタイトルの通り、Pyxelでタイピングゲームを作ろうと思います。特に以下の三点について取り上げます。
- キー入力
- 正誤判定
- 日本語フォント
↓解説に使っているコードとは別ですが、筆者が試しに作ってみたものです。
キー入力について
pygame
やtkinter
と比べたときに、Pyxelでキーの名前を取得しようと思うと、癖があるな~と思ったので、詳しく取り上げようと思いますこのキーが押されたら~するみたいな処理だったら、pygame
やtkinter
と同じような感覚で出来るので、気にしたことがない人が大半だと思います。
違いについて
「キーを押された」というイベントが、「どのキーを押されたか」という情報も持っているのが、pygame
やtkinter
です。例えばtkinter
では、バインドされている関数の第一引数にevent
と書いて受け取るのが一般的だと思いますが、event.char
とすると押されたキーの名前が取得できます。
それに対して、今回Pyxelで実装した方法は、「このキーを押された」という個別のイベントを、まとめて判定しています。
コメントで教えていただいたinput_keys
というのを使うことで、もっと楽に扱えました。これにより、使用感としてはpygame
やtkinter
と変わらなくなったので消しておきます。
キーの名前の取得方法
キーが押されているかの判定と、あらかじめ決められた数字からキーの名前を取得するという事ができるので、それらを組み合わせれば実装できます。
キーに割り振られている定数名の一覧は以下のページにありました。数字に関しては、45~122辺りの数字に使いそうな文字がまとまっていますが、今回は諸事情により0~122を使います。
早速コードを載せます。キー入力を検知して、押されたキーの名前をprint
します。
import pyxel
shifted = {
'a':'A', 'b':'B', 'c':'C', 'd':'D', 'e':'E', 'f':'F', 'g':'G', 'h':'H', 'i':'I', 'j':'J', 'k':'K', 'l':'L', 'm':'M', 'n':'N', 'o':'O', 'p':'P', 'q':'Q', 'r':'R', 's':'S', 't':'T', 'u':'U', 'v':'V', 'w':'W', 'x':'X', 'y':'Y', 'z':'Z',
'1':'!', '2':'"', '3':'#', '4':'$', '5':'%', '6':'&', '7':"'", '8':'(', '9':')', '0':'0',
'-':'=', '^':'~', '\\':'|', '@':'`', '[':'{', ']':'}', ';':'+', ':':'*', ',':'<', '.':'>', '/':'?',' ':' '
}
class TypingGame:
def __init__(self):
pyxel.init(160, 120, fps=50)
pyxel.run(self.update, self.draw)
def update(self):
for key in pyxel.input_keys:
if key not in range(pyxel.KEY_UNKNOWN, pyxel.KEY_Z+1): #0~122でなければ無視
continue
char = chr(key) #キーの名前を取得
if pyxel.btnp(pyxel.KEY_UNKNOWN): #Windowsでは\が空文字になるので、空文字を\として扱う
char = '\\'
if pyxel.btn(pyxel.KEY_SHIFT): #シフト入力時
if key == pyxel.KEY_UNKNOWN:
char = '_'
else:
char = shifted[char]
print(char)
def draw(self):
pyxel.cls(0)
pyxel.text(50, 60, "Press any key...", pyxel.frame_count % 16)
TypingGame()
コメントに書いたのですが、筆者がWindowsを使っているので、\
が空文字になってしまっていました。他に空文字で認識されるキーがあるかもしれませんが、タイピングゲームとかを作るなら_
が打てることを優先したいので、空文字を\
として扱っています。この空文字がpyxel.KEY_UNKNOWN
で数字では0
に当たるので、0~122としました。(使わなくてもいいなってだけで、0~44の間にもTabとかあるので、ついでに含めました)
簡単なコードですが、Pyxel向けの記事のため、シフト入力について初心者向けに補足です。左右どちらかのシフトキーが押されたり離されたりすると、self.shift
がTrue
やFalse
に切り替わるようにしてあります。このフラグがTrue
の時だけ辞書を参照して、シフト後の文字を取ってくるという処理を追加することで、シフト入力に対応しています。btnp
というのがキーを押したときで、btnr
がキーを離したときです。(多分pressとreleaseかな)
もっといい方法をコメントで教えていただいたので、コードの方は修正後のものにしてありますが、元の解説は残しておきます。(pygameでマウスのドラッグを実装するときによく使うから、それに引っ張られたのかな...)
正誤判定について
正誤判定の部分は、typehandler
というモジュールがあるので、これに任せれば実装できます。(差分表示の下にコピー用のコードも置いておきます)
python -m pip install typehandler
import pyxel
+ from typehandler import Process
shifted = {
'a':'A', 'b':'B', 'c':'C', 'd':'D', 'e':'E', 'f':'F', 'g':'G', 'h':'H', 'i':'I', 'j':'J', 'k':'K', 'l':'L', 'm':'M', 'n':'N', 'o':'O', 'p':'P', 'q':'Q', 'r':'R', 's':'S', 't':'T', 'u':'U', 'v':'V', 'w':'W', 'x':'X', 'y':'Y', 'z':'Z',
'1':'!', '2':'"', '3':'#', '4':'$', '5':'%', '6':'&', '7':"'", '8':'(', '9':')', '0':'0',
'-':'=', '^':'~', '\\':'|', '@':'`', '[':'{', ']':'}', ';':'+', ':':'*', ',':'<', '.':'>', '/':'?',' ':' '
}
+ words = {
+ 'Pyxel':'Pyxel',
+ 'typing':'typing',
+ 'Python':'Python'
+ }
+ process = Process(words) #お題とフリガナの辞書を渡しておく
class TypingGame:
def __init__(self):
pyxel.init(160, 120, fps=50)
pyxel.run(self.update, self.draw)
def update(self):
+ process.update_show_roman() #おまじない
#キー入力の判定部分
for key in pyxel.input_keys:
if key not in range(pyxel.KEY_UNKNOWN, pyxel.KEY_Z+1): #0~122でなければ無視
continue
char = chr(key) #キーの名前を取得
if pyxel.btnp(pyxel.KEY_UNKNOWN): #Windowsでは\が空文字になるので、空文字を\として扱う
char = '\\'
if pyxel.btn(pyxel.KEY_SHIFT): #シフト入力時
if key == pyxel.KEY_UNKNOWN:
char = '_'
else:
char = shifted[char]
- print(char)
+ process.main(char) #これ一行で正誤判定から文章の更新まで行う
def draw(self):
pyxel.cls(0)
- pyxel.text(50, 60, "Press any key...", pyxel.frame_count % 16)
+ pyxel.text(20, 40, process.show_roman, 13) #入力パターンの一例
+ pyxel.text(20, 40, process.input, 14) #現在の入力
+ pyxel.text(20, 60, process.sentence, 7) #お題
TypingGame()
コピー用
import pyxel
from typehandler import Process
shifted = {
'a':'A', 'b':'B', 'c':'C', 'd':'D', 'e':'E', 'f':'F', 'g':'G', 'h':'H', 'i':'I', 'j':'J', 'k':'K', 'l':'L', 'm':'M', 'n':'N', 'o':'O', 'p':'P', 'q':'Q', 'r':'R', 's':'S', 't':'T', 'u':'U', 'v':'V', 'w':'W', 'x':'X', 'y':'Y', 'z':'Z',
'1':'!', '2':'"', '3':'#', '4':'$', '5':'%', '6':'&', '7':"'", '8':'(', '9':')', '0':'0',
'-':'=', '^':'~', '\\':'|', '@':'`', '[':'{', ']':'}', ';':'+', ':':'*', ',':'<', '.':'>', '/':'?',' ':' '
}
words = {
'Pyxel':'Pyxel',
'typing':'typing',
'Python':'Python'
}
process = Process(words) #お題とフリガナの辞書を渡しておく
class TypingGame:
def __init__(self):
pyxel.init(160, 120, fps=50)
pyxel.run(self.update, self.draw)
def update(self):
process.update_show_roman() #おまじない
#キー入力の判定部分
for key in pyxel.input_keys:
if key not in range(pyxel.KEY_UNKNOWN, pyxel.KEY_Z+1): #0~122でなければ無視
continue
char = chr(key) #キーの名前を取得
if pyxel.btnp(pyxel.KEY_UNKNOWN): #Windowsでは\が空文字になるので、空文字を\として扱う
char = '\\'
if pyxel.btn(pyxel.KEY_SHIFT): #シフト入力時
if key == pyxel.KEY_UNKNOWN:
char = '_'
else:
char = shifted[char]
process.main(char) #これ一行で正誤判定から文章の更新まで行う
def draw(self):
pyxel.cls(0)
pyxel.text(20, 40, process.show_roman, 13) #入力パターンの一例
pyxel.text(20, 40, process.input, 14) #現在の入力
pyxel.text(20, 60, process.sentence, 7) #お題
TypingGame()
日本語フォントについて
とりあえず簡単な英字で説明しましたが、typehandler
は日本語にも対応しているので、せっかくなら日本語のタイピングゲームを作っていきましょう。それにあたって、Pyxel側も日本語に対応させる必要があるので、その方法を説明していきます。
まずはbdf
形式のフォントを用意します。以下のサイトのダウンロードリンクから、k8x12.bdf
をダウンロードしてください。(他にフォントがある方は自分の好みのものでOK)
次に、先ほどのコードに以下の行を追加します
process = Process(words)
+ font = pyxel.Font("k8x12.bdf")
def draw(self):
pyxel.cls(0)
- pyxel.text(20, 40, process.show_roman, 13) #入力パターンの一例
- pyxel.text(20, 40, process.input, 14) #現在の入力
- pyxel.text(20, 60, process.sentence, 7) #お題
+ pyxel.text(20, 40, process.show_roman, 13, font) #入力パターンの一例
+ pyxel.text(20, 40, process.input, 14, font) #現在の入力
+ pyxel.text(20, 60, process.sentence, 7, font) #お題
これで日本語が表示できるようになったので、words
のお題を日本語のものに変えてみましょう。好きなもので構いませんが、以下に例を置いておきます(ついでにコード全体)
import pyxel
from typehandler import Process
shifted = {
'a':'A', 'b':'B', 'c':'C', 'd':'D', 'e':'E', 'f':'F', 'g':'G', 'h':'H', 'i':'I', 'j':'J', 'k':'K', 'l':'L', 'm':'M', 'n':'N', 'o':'O', 'p':'P', 'q':'Q', 'r':'R', 's':'S', 't':'T', 'u':'U', 'v':'V', 'w':'W', 'x':'X', 'y':'Y', 'z':'Z',
'1':'!', '2':'"', '3':'#', '4':'$', '5':'%', '6':'&', '7':"'", '8':'(', '9':')', '0':'0',
'-':'=', '^':'~', '\\':'|', '@':'`', '[':'{', ']':'}', ';':'+', ':':'*', ',':'<', '.':'>', '/':'?',' ':' '
}
words = {
'タイピングゲーム':'たいぴんぐげーむ',
'Pyxelでも':'Pyxelでも',
'作ってみたよ':'つくってみたよ'
}
process = Process(words) #お題とフリガナの辞書を渡しておく
font = pyxel.Font("k8x12.bdf")
class TypingGame:
def __init__(self):
pyxel.init(160, 120, fps=50)
pyxel.run(self.update, self.draw)
def update(self):
process.update_show_roman() #おまじない
#キー入力の判定部分
for key in pyxel.input_keys:
if key not in range(pyxel.KEY_UNKNOWN, pyxel.KEY_Z+1): #0~122でなければ無視
continue
char = chr(key) #キーの名前を取得
if pyxel.btnp(pyxel.KEY_UNKNOWN): #Windowsでは\が空文字になるので、空文字を\として扱う
char = '\\'
if pyxel.btn(pyxel.KEY_SHIFT): #シフト入力時
if key == pyxel.KEY_UNKNOWN:
char = '_'
else:
char = shifted[char]
process.main(char) #これ一行で正誤判定から文章の更新まで行う
def draw(self):
pyxel.cls(0)
pyxel.text(20, 40, process.show_roman, 13, font) #入力パターンの一例
pyxel.text(20, 40, process.input, 14, font) #現在の入力
pyxel.text(20, 60, process.sentence, 7, font) #お題
TypingGame()
たった40行ほどで本格的なタイピングゲームが実装できました!
最後に
ここまで読んで下さりありがとうございました。Pyxelってデフォルトだと日本語対応してないんだーと思っていたんですが、ビットマップフォントを読み込むだけなら、ほぼ手間0で導入できますね。
キー入力の方も簡単に実装できたので、この記事を役立てて、タイピングゲームを作る人が増えればいいなと思います!
最後まで読んで下さった方へ、最初に見せた画像のコードも置いておきます(70行くらい)。ちなみにmainの第二引数にTrue
を渡すと、シフト入力を勝手に実装してくれるので、その機能を使っています。|
だけは使えませんが、マニアックな文章でなければ、この機能で事足りるでしょう。
コードを見る
import pyxel
from typehandler import Process
words = {
'画竜点睛を欠く':'がりょうてんせいをかく',
'十把一絡げ':'じっぱひとからげ',
'ヘキサクロロシクロヘキサン':'へきさくろろしくろへきさん',
'部分分数分解':'ぶぶんぶんすうぶんかい',
'for i in range(100):':'for i in range(100):',
'random.randint(0, 10):':'random.randint(0, 10):',
'I wanna eat salmon.':'I wanna eat salmon.',
"Shall we dance?":"Shall we dance?"
}
process = Process(words)
def draw_text_with_border(x, y, s, col, bcol, font):
for dx in range(-1, 2):
for dy in range(-1, 2):
if dx != 0 or dy != 0:
pyxel.text(
x + dx,
y + dy,
s,
bcol,
font,
)
pyxel.text(x, y, s, col, font)
font = pyxel.Font("k8x12.bdf")
class TypingGame:
def __init__(self):
self.miss = False
self.sentence_count = 0
pyxel.init(160, 120, fps=50)
pyxel.run(self.update, self.draw)
def update(self):
process.update_show_roman()
if pyxel.btnp(pyxel.KEY_LSHIFT) or pyxel.btnp(pyxel.KEY_RSHIFT):
self.shift = True
if pyxel.btnr(pyxel.KEY_LSHIFT) or pyxel.btnr(pyxel.KEY_RSHIFT):
self.shift = False
#キー入力の判定部分
for key in pyxel.input_keys:
if key not in range(pyxel.KEY_UNKNOWN, pyxel.KEY_Z+1): #0~122でなければ無視
continue
char = chr(key) #キーの名前を取得
if pyxel.btnp(pyxel.KEY_UNKNOWN): #Windowsでは\が空文字になるので、空文字を\として扱う
char = '\\'
#正誤判定部分
result = process.main(char, pyxel.btn(pyxel.KEY_SHIFT))
if result == process.SENTENCE_COMPLETE:
self.sentence_count += 1
elif result == process.MISS:
self.miss = True
def draw(self):
pyxel.cls(0)
if self.miss:
pyxel.cls(4)
self.miss = False
pyxel.text(10, 10, f'打ち終わった数:{self.sentence_count}', 7, font)
pyxel.text(20, 40, process.show_roman, 7, font)
draw_text_with_border(20, 40, process.input, 7, 10, font)
pyxel.text(20, 60, process.sentence, 7, font)
pyxel.text(20, 100, 'next=>'+process.next, 13, font)
TypingGame()
参考
Pyxelについてはこちら
キー入力のところで紹介したリンク(再掲)
typehandler
についてはこちら