寄り道してRaspberryPi Picoで反射神経ゲームを作る
龍野プログラミングクラブはJR本竜野駅2階多目的室(上の写真)で定期開催しています。教材にそって進めるスタイルで運営しています。が、この日はすこし寄り道して、RaspberryPi Picoを使ってゲームを作ります。
タッチタイピングの練習とアルゴロジック
わたしが運営するクラブでは、かならずタッチタイピングを練習します。キーを打つことを、息をするようにできるまで練習します。
タッチタイピングで使うツールは、無料タイピングと寿司打と右手専用タイピングです。
無料タイピング https://manabi.benesse.ne.jp/gakushu/typing
右手専用タイピング https://typing.twi1.me/game/15206
この日は、メンバーが自己ベストを大幅に更新しました! 無料タイピングでも寿司打でも両方自己ベストを更新したので、一回限りのまぐれではありません。特異点を越えたかもしれません。
しばらく停滞が続いていたので、喜びもひとしおです。メンバーが嬉しいことは、わたしも嬉しいのです!
ただし、本番は別に用意しているので、タイピングとアルゴロジックを20分くらいで切り上げます。
Raspberrypi Picoで電子工作+プログラミング
前回メンバーに電子工作のゲームを見せたら、やりたい!と興味を持ったようなので、今回は寄り道してMicroPythonでプログラミングします。
プログラミングの前に、ブレットボードの上に電子回路を組み立てます。散々苦労して小学生でもかんたんにブレットボード上に電子回路を作れる資料を完成させました。ツールがどこかに公開されていればよかったのですが、公開されているツールではなかなかうまくいきませんでした。「無ければ作る」これが、わたしたちの合言葉です。
さて、回路制作はあっという間に終わります。
反射神経ゲームのコーディングはハードモードで
反射神経ゲームのコードをすべて公開しておきます。
import machine
import time
import random
from machine import Pin
# ピン設定
btn = Pin(9, Pin.IN, Pin.PULL_UP)
leds = [Pin(12, Pin.OUT), Pin(13, Pin.OUT), Pin(14, Pin.OUT), Pin(15, Pin.OUT)] # 青、赤、黄、緑
# LEDの制御関数
def set_leds(b, r, y, g):
values = [b, r, y, g]
for i, led in enumerate(leds):
led.value(values[i])
def all_leds_off(): set_leds(0, 0, 0, 0)
def all_leds_on(): set_leds(1, 1, 1, 1)
# ボタン関連
last_press_time = 0
debounce_ms = 50
def wait_for_button_press():
global last_press_time
while btn.value() == 1: # ボタンが押されるまで待機
time.sleep(0.01)
current_time = time.ticks_ms()
if time.ticks_diff(current_time, last_press_time) > debounce_ms:
last_press_time = current_time
while btn.value() == 0: # ボタンが離されるまで待機
time.sleep(0.01)
return True
return False
def check_button_press():
global last_press_time
if btn.value() == 0: # ボタンが押されている
current_time = time.ticks_ms()
if time.ticks_diff(current_time, last_press_time) > debounce_ms:
last_press_time = current_time
while btn.value() == 0: # ボタンが離されるまで待機
time.sleep(0.01)
return True
return False
# メインゲームループ
def main():
while True:
try:
all_leds_on()
wait_for_button_press() # 1回目
all_leds_off()
wait_time = random.uniform(1, 10)
start_time = time.time()
# フライング検出
early_press = False
while time.time() - start_time < wait_time:
if check_button_press():
early_press = True
break
time.sleep(0.01)
if early_press:
set_leds(0, 1, 1, 1) # フライング: 青以外点灯
wait_for_button_press()
all_leds_off()
time.sleep(1)
continue
leds[0].value(1) # 青色LED点灯
reaction_start = time.ticks_ms()
wait_for_button_press() # 3回目
reaction_time = (time.ticks_ms() - reaction_start) / 1000
# 反応時間に応じたLED点灯
if reaction_time < 0.2:
set_leds(1, 0, 0, 1) # 青と緑
elif reaction_time < 0.4:
set_leds(1, 0, 1, 0) # 青と黄
else:
set_leds(1, 1, 0, 0) # 青と赤
print(reaction_time)
wait_for_button_press() # 4回目
all_leds_off()
time.sleep(1)
except KeyboardInterrupt:
all_leds_off()
print("プログラムを終了します")
break
except Exception as e:
all_leds_off()
print(f"エラー: {e}")
time.sleep(3)
if __name__ == "__main__":
main()
ここまでの取り組みで、このメンバーならこのコードをすべて時間内に入力して動かせるだろうと考えて、全部手入力してもらいました。
1時間位で見事完成。無事に動きました。
コードの意味をすべて理解するのは難しいものの、いつものパソコン画面で動くゲームとの違いを感じ取ってくれたようです。
今はまだ感じ取るだけで十分です。オブジェクト指向とか、例外処理とか、リスト内包表記なんてまだやっていませんから。進んでいくうちに、振り返ったらわかるようになります。
そして、電子工作のプログラムは、画面の中だけで完結するプログラムでは出てこない要素があります。それが「debounce_ms」が出てくるあたりです。これは、スイッチのチャタリング防止のコードで使うものです。
くわしくは書きませんが、電子工作は現実にある「モノ」を制御するため、理想通りの動きをしないことがあります。その一つがチャタリングです。
画面のコードだけを見ていたのではわからない、それが電子工作のプログラミングの面白さであって、難しさなのです。
ぜひそういう感性を養ってほしいと思います。
さて次回は?
もとに戻ってゲームプログラミングをしましょうか、もっとラズPicoをすすめましょうか。思案中です。