「こんにちはマイコン」という伝説の書をご存知でしょうか。
そのリメイクとも言える本、すがやみつる著 「ゲームセンターあらしと学ぶ プログラミング入門 まんが版こんにちはPython」が出版されました! 今回は、その本に出てくる懐かしのゲームをMac上で動かしてみました。
「こんにちはマイコン」と「こんにちはPython」
私が子どもの頃、プログラミングを始めたきっかけとなったのが「こんにちはマイコン」という本です。「ゲームセンターあらし」という漫画の番外編?で、主人公 あらし がマイコン(=パソコンのこと)でプログラミングを学んでいくという内容でした。
変数、条件分岐、ループなどの基本を押さえつつ、最後には「たしざんゲーム」や「テニスゲーム」を作ることができるようになるので、それはもう夢中になって読みました!(同じPC-6001も買ってもらいました)
ちなみに、今でも電子版で読めるようですね。
その「こんにちはマイコン」とほぼ同じ語感の「こんにちはPython」! (←いい響きw)
同じすがやみつる先生による、同じくプログラミングの入門書(入門の入門)です。その上、テニスゲーム(スカッシュ)まで同じです。読んでみるとますます、事実上のリメイクのように思いました。
というのも、私がこれまで少し学んだPythonでは、テキスト整形のような実用的?な使い方がメインだったのですが、この本ではその話は一切ないです(笑)。まさに「こんにちはマイコン」のときにBASICで作ったゲームを今度はPythonで作る感じで、楽しく学ぶことができます。最後のスカッシュゲームではGUIまで実装できます。それだけPythonを使えばなんでもできるということなのでしょうが、初心者の私としてはかなり勉強になりました。
テニスゲーム(スカッシュ)を動かしてみた!
ソースコードは、サポートサイトからダウンロードできましたので、早速スカッシュを動かしてみました。Macで試しましたが、実行結果は以下の通りです。(結構遊べますw)
ただ、ここまでたどり着くまでに少し手がかかりました。(^^;
まず、PythonでGUI(Tkinter)を使ったことがなかったので環境を作る必要がありました。インストールするだけだと動いてくれなかったのでGoogle先生に聞いてみたところ、以下のサイトに書かれてている手順で手パッチをあてると動きました。
あと、そのままのソースだとビープ音のところで引っかかるようでしたので色々と直してみました。
以下その覚え書きです。
Macでもビープ音を出せるようにする
ソースをVSCodeで開くと、winsound をインポートできないと言われました。。
どうやら、ビープ音を鳴らすために使っているライブラリがWindows専用のようでした。
Macでビープ音を出すためには、SoXをインストールして、コマンドラインから以下のようにplayコマンドを叩く必要があるようです。
$ play -n synth duration sin frequency
パラメータは、frequency が周波数[Hz] で duration が時間[sec] です。
なお、Pythonのwinsound.Beep()の形式は以下です。
import winsound
winsound.Beep(frequency, duration)
周波数(frequency)と時間(duration)を引数に指定するのは同様なのですが、時間の単位はミリ秒です。
というわけで、以下の通り、Pythonからos.system()を使って外部のplayコマンドを呼ぶような関数を作って、winsound.Beep()の代わりにそれを呼ぶように変えてみました。関数の形式はwinsound.Beep()に合わせて、時間の単位もミリ秒としました。
import os
def beep(frequency, duration):
command = 'play -n synth %s sin %s' % (duration / 1000, frequency)
os.system(command)
これで無事にビープ音も鳴るようになりました!
ビープ音を鳴らしたとき一瞬固まるのを改善
壁やラケットにボールが当たるとビープ音が鳴るようになったのですが、動作がカクカクするようになりました。。何故なんでしょう。
プログラムを見直してみると、ビープ音は50ミリ秒間鳴らします。このとき、子プログラムとしてplayコマンドを起動していますが、その完了を待ってから次の処理に行っているようです。いわゆる同期処理です。
というわけで、子プログラムを非同期(バックグラウンド)で処理するように変えてみます。
コマンドラインだと最後に & を付けて起動するだけです。
$ play -n synth duration sin frequency &
これを Pythonから起動するためには、os.system()のところを subprocess.Popen()に変更すればいいようです。なお、変数commandは一行分の文字列ですが、Popen()の引数として渡すには空白で区切った文字列の配列に分割してやる必要があるので、command.split()としています。
import subprocess
def beep(frequency, duration):
command = 'play -n synth %s sin %s' % (duration / 1000, frequency)
subprocess.Popen(command.split())
これでビープ音によるカクカクは直りました!
コマンドラインに出てしまうメッセージを改善
もう一つ気になる点として、ビープ音のためにplayコマンドを起動するとコマンドラインに以下のようなメッセージが出ます。
File Size: 0
Encoding: n/a
Channels: 1 @ 32-bit
Samplerate: 48000Hz
Replaygain: off
Duration: unknown
In:0.00% 00:00:00.17 [00:00:00.00] Out:2.20k [-=====|=====-] Hd:3.0 Clip:0
Done.
これは、playコマンドが標準エラー出力にメッセージを出すためのようです。気にしなければいいだけかもしれませんが、GUIでゲームしているのに横でコマンドラインがメッセージで埋まるのもいまいちです。
というわけで、標準エラー出力を捨てるように変更しました。
playコマンドのオプション指定でできたのかもしれませんが、今回はリダイレクションで対応しました。
コマンドラインだと以下のように書けます。標準エラー出力を標準出力に切り替えて、NULLデバイスに吐き出せばよいわけです。
$ play -n synth duration sin frequency >/dev/null 2>&1
これをPythonから起動するためには、subprocess.Popen()の引数でリダイレクションを指定すればいいようです。
import subprocess
def beep(frequency, duration):
command = 'play -n synth %s sin %s' % (duration / 1000, frequency)
subprocess.Popen(command.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
これでビープ音が鳴ってもコマンドラインにメッセージは出なくなりました!
まとめ
この度、「こんにちはPython」という本に出会えてめちゃくちゃ嬉しかったです。早速Macで動かしてみるとビープ音のライブラリがそのまま使えず、色々と手を加えてみました。それはそれで楽しくて、うまく行ったときには達成感も得られます。これからも改造したりして楽しみたいと思います。
全体ソースはGitHubに置いてあります。
最後まで読んでいただきありがとうございました!
参考にさせていただいたサイト