アイキャッチ1280x670

micro:bitではじめる電子工作「もぐらたたきゲーム」を作ってみよう

こんにちは、遠藤です。
コピペテックマガジン第2回目は、BBC micro:bitを使った電子工作です。

作るものはこんな感じで、「もぐらたたきゲーム」といってもmicro:bitのLEDディスプレイに表示された絵(アイコン)と同じ絵がかかれた場所をハンマーでたたいて点数を競うというゲームです。


買い物リスト

まずは工作に必要な機材・部材を揃えます。
もぐらたたきゲームを作成するために必要なものは以下になります。

・micro:bit
・USBケーブル
・抵抗 1kΩ 1/4W x 5

・抵抗 100kΩ 1/4W x 1
・ミノムシ(ワニ)口クリップ付きワイヤー 30cm程度 x 5
・ジャンパーワイヤー (メス-メス) x 1
・2mくらいの長い導線
・ピコピコハンマー
・スピーカー
・アルミホイル (アルミテープ)
・ゼムクリップ (色などついていないもの)

・セロハンテープまたは両面テープ
・ダンボールなどの板
・マジック

上記のうち、電子パーツが購入可能なオンラインサイトをいくつかリストしてみます。

micro:bit: マルツ秋月電子通商千石電商
抵抗 1kΩ: マルツ秋月電子通商千石電商
抵抗 100kΩ: マルツ秋月電子通商千石電商
ミノムシクリップ付きワイヤー: マルツ秋月電子通商千石電商
導線200cm: マルツ、 秋月電子通商千石電商
ジャンパーワイヤー(メス-メス): マルツ秋月電子通商千石電商

抵抗は、千石電商ではバラで1個6円で買えますが、マルツと秋月は1袋100個単位のものしか無いようでした。

店舗で買う場合、秋葉原であれば秋月電子通商の通りに、千石電商、マルツもあるので、このあたりに行けば一通り揃うと思います。

ピコピコハンマーは、100円ショップでも買えるらしいですが、置いていないところがほとんどでした。今回はビックカメラで買ってみました。

スピーカーは、100円ショップ ダイソーで買ったものです。

その他の部材は文房具店、100円ショップなどで揃えていただければと思います。

micro:bit用プログラムエディタ MakeCode

micro:bitのプログラム作成には、ブラウザで利用できるmakecodeというエディタを利用します。
https://makecode.microbit.orgを開くと以下の画面が開きます。

makecodeでは、グラフィカルなブロックを積み上げていくだけで簡単にプログラミングすることができます。
Scratchをご存知の方はとっつきやすいかもしれません。

ブロックは、中央の[基本]、[入力]などが表示されている一覧から右側にドラッグ&ドロップして追加していきます。

左側のmicro:bitの画像は、シュミレータになっていてブロックを追加するたびに、プログラムの動作を都度確認することができます。

また、makecodeのプロジェクトは公開して共有することもできます。「もぐらたたきゲーム」の完成版のプロジェクトも以下のURLで公開していますので、途中で詰まったりした際には参考にしてみてください。

https://makecode.microbit.org/_RFodK6RKhfHT

LEDディスプレイにアイコンを表示してみよう

まずは、ランダムに5つのアイコンをLEDディスプレイに表示するところまでプログラミングしていきます。

[最初だけ]のブロックを削除して、[ずっと]に以下のようにブロックを追加していきます。

1. ブロック[ループ] > [繰り返し {4} 回][ずっと]ブロックに追加して、繰り返し回数を10回にセットします。
2. 続けて、ブロック[変数] > [変数 {変数} を {0} にする]を追加し、{変数}を「shape」に変更して、{0}はブロック[計算] > [ 0 〜 {4} の範囲の乱数]に置き換えます。
3. 次に、ブロック[論理] > [もし {真} なら]を追加して、歯車のアイコンをクリックして以下の図のように[else if]を3つ、最後の[else]を追加します。

4. [{真}]をブロック[論理] > [{0} {=} {0}] に置き換えて、[{shape} {=} {0}] となるように先頭の{0}をブロック[変数] > [{shape}] で置き換えます。
5. [もし{shape} {=} {0} なら]の中に、ブロック[基本] > [アイコンを表示 {アイコン}]を追加して、{アイコン}はハートを選択します。
6. 4.と5.を繰り返して、[{shape} {=} {1}] なら「8分音符」、[{shape} {=} {2}] なら「かさ」、[{shape} {=} {3}] なら「きりん」、[でなければ] では「ちょうちょ」 を表示するようにセットします。
7. [もし・・・なら・・・でなければ]の次にブロック[基本] > [一時停止(ミリ秒) {100}]を追加して、停止時間を「1000」に変更します。
8. ブロック[変数] > [変数 {変数} を {0} にする]を追加して、{変数}を「shape」に変更して、{-1}をセットするように変更します。
9.ブロック[基本 (さらに表示)] > [表示を消す]を追加します。
10. ブロック[基本] > [一時停止(ミリ秒) {100}]を追加して、停止時間を「500」に変更します。

ここまでできたら、左側のシュミレータはブロックを追加するごとに動作を確認できます。

micro:bitにプログラムを流し込んで実機でも確認してみましょう。

プロジェクト名の「題名未設定」を「もぐらたたきゲーム」に名前を変更して、[ダウンロード]ボタンを押します。

 「もぐらたたきゲーム.hex」 というファイルが保存されます。

次にmicro:bitをPCにUSBで接続します。接続するとリムーバブルディスクとして認識されるので、以下のようにエクスプローラでhexファイルをMICROBITにコピーします。

以上でmicro:bitへもぐらたたきゲームのプログラムが転送されて使える状態になります。

このように、ランダムにアイコンが表示され続けます。

ゲームの開始と終了を追加しよう

Aボタンが押されたらゲームを開始して、10回アイコンが表示されたらスコアが表示されてゲームが終了するところまでプログラミングしていきます。

ゲームの開始はAボタンを押した後に、「3,2,1」とカウントダウンした後にゲームが始まるようにしてみます。

[ボタンAが押されたとき]を追加して以下のようにブロックを追加します。

1. ブロック[入力] > [ボタン {A} が押されたとき]を追加します。
2. ブロック[変数] > [変数 {変数} を {0} にする]を追加して、変数名に「score」をセットします。
3. ブロック[ループ] > [変数 {カウンター} を0〜 {4} に変えてくりかえす]を追加して{4}を「2」に変更します。
4. 3.のループ内に、ブロック[基本] > [数を表示 {0}]を追加して、{0}をブロック[計算] > [{0} {-} {0}]に置き換え、左側の{0}に「3」をセットし、右側の{0}をブロック[変数] > [{カウンター}]に置き換えます。
5. ループに、ブロック[基本] > [一時停止 (ミリ秒) {100}]を追加して停止時間を「500」にセットします。

次に、ゲームが開始されたらLEDディスプレイにアイコンの表示を始めるように[ずっと]の中にあった[くりかえし {10} 回]のブロック全体を[変数 {カウンター} を 0〜{2} に変えてくりかえす]の下に移動します。

ループ[くりかえし {10}回]が終わった後に、ゲームの終了を知らせる音を鳴らして、スコアを表示するようにしてみます。

1. ブロック[音楽] > [メロディを開始する {ダダダム} 繰り返し {一度だけ}]を追加し、{ダダダム}を「パワーアップ」に変更します。
2. ブロック[基本] > [数を表示 {0}]を追加して、{0}をブロック[変数] >[{score}]に置き換えます。

ここまでできたらファイルをダウンロードしてmicro:bitに転送し、動きを確認してみましょう。

音を出すためにスピーカーをmicro:bitのP0とGND端子につなげます。

最後に音がなります。スコアはまだ0のままです。

タッチしたらスコアを加算するようにしてみよう

次にピコピコハンマーで叩いたらスコアが表示されるようにプログラミングしてみます。

まずは新たに[ずっと]ブロックを追加し、そこに以下のようにブロックを追加します。

1. ブロック[変数] > [変数 {変数} を {0} にする]を追加、{変数}の名前を「analogInput」にし、{0}はブロック[高度なブロック] > [入出力端子] > [アナログ値を読み取る 端子 {P0}]に置き換えて、端子{P0}を{P1}に変更します。
2. ブロック[論理] > [もし {真} なら]を追加して、{真}をブロック[論理]  > [{0} {=} {0}]に置き換え、「もし変数 {isHit} が {0} と同じならという条件になるように左側の{0}をブロック[変数] > [{変数}]に置き換え{変数}を「isHit」に変更します。
3. 2.の条件の中にブロック[論理] > [もし {真} なら]を追加して、{真}をブロック[論理]  > [{0} {=} {0}]に置き換え、[もし変数 {analogInput} が {>} {900}]という条件になるように変数と比較演算子、数値をセットします。
4. 3.の条件の中に、ブロック[変数] > [変数 {変数} を {0} にする]を追加して、{変数}を「isHit」に、{0}を「1」に変更します。
5. ループの最後にブロック[基本] > [一時停止 (ミリ秒) {100} ]を追加します。

アイコンを10回表示する繰り返しブロックの最後に、当たったらスコアを加算するようにブロックを追加して、最後にスコアを表示されるようにします。

1. ループ[繰り返し {10} 回][表示を消す]の次に、ブロック[変数] > [変数 {変数}を {1} だけ増やす]を追加し、{変数}を「score」に、{1}をブロック[変数] >[{isHit}]に置き換えます。
2. 続けて、ブロック [変数] > [変数 {変数} を {0} にする]を追加して、{変数}に「isHit」をセットします。

ここまでできたらプログラムをダウンロードし直して、micro:bitに転送しておきます。

続いて工作です。

2mの導線の両端を3cmほど剥がします。専用の道具がなくてもハサミで切り込みをいれればはがせると思います。

ピコピコハンマーにアルミホイルを巻いて、導線の片方の端をアルミホイルにしっかり接触するようにテープで止めます。

確実に電気を通すには、導線の端をアルミホイルをゼムクリップで止めて、その上からテープを貼ると良さそうです。

次に抵抗100kΩと1kΩの抵抗をジャンパーワイヤーでつなぎます。

スピーカーにGNDからつなげていた黒いミノムシクリップを外して100kΩ抵抗(赤い線がない方)のジャンパーワイヤー側に止めます。
スピーカーとは、新しく白いワイヤーで写真のようにつなぎます。

抵抗の足をミノムシクリップで挟むとき、深く挟むと浮いてしまうので注意してしっかり挟んでください。

ピコピコハンマーにつながる導線の端をmicro:bitのP1端子に巻きます。

P1端子に巻いた導線の上から黄色のミノムシクリップを止めます。

黄色のケーブルは、100kΩ抵抗の反対側の足をみのむしクリップで挟みます。

次にアルミホイルを1枚適当な長さに切り取り折り畳んで、1kΩ抵抗の足をゼムクリップで止めます。

最後に、micro:bitのV3端子とアルミホイルを赤いミノムシクリップケーブルで繋ぎます。

ここまでの工作の全体はこんな感じになります。

Aボタンでスタートして、アイコンが表示されたらアルミホイルをアルミホイルを巻いたピコピコハンマーで叩いてください。

叩いても反応がないので不安になりますが、叩いた数だけスコアが加算されていきます。

(しかし、なんとも地味な映像です(^ ^; )

ここまでのプログラムでは、P1端子から読み取った analogInput が 900より大きな値だった場合に当たりと判定しました。micro:bitのアナログ入力は、リファレンス「入出力端子 — BBC micro:bit MicroPython 0.5.0 ドキュメント」に書いてあるように、P1とGNDの間の電圧を0V〜3.3Vを計測して0-1023の値に変換してくれます。
3V端子とGND端子の間に抵抗が1kΩ一つだけなので、アルミホイル側の電圧を読み取ると常に1023に近い値になります。ちなみに、抵抗の反対側ではケーブルなどが完全に無抵抗ではないので1か2になります。
また、P1端子は常に100kΩの抵抗を挟んでGND端子につながっていますが、こちらはハンマーが離れているときにも一定の値(42くらい)が読み取られるようにするためです。この抵抗値100kオームが妥当なのかどうかは試してみた結果の値なので、きちんと調べてみたいところです。

正しく叩けたら音を出して知らせてみよう

ここまでだと叩いてもちゃんと反応しているか不安になってしまします。正しく叩けたら音を鳴らしてフィードバックしてあげるようにしてみます。

1. [変数 {isHit} を {1} にする]ブロックの次に、ブロック[変数 {変数} を {0} にする]を追加して、{変数}を「soundOn」に、{0}を「1」に変更します。

続いて「soundOn」が「1」のときに、つまり当たったときに「ピコーン!」と音をならすようにブロックを追加します。

1. 新しく、ブロック[基本] > [ずっと]を追加します。
2. [ずっと]の中に、ブロック[論理] > [もし {真} なら]を追加して、{真}をブロック[論理] > [{0} {=} {0}]に置き換え、[もし変数 [{soundOn} が {=} {1}]なら]という条件になるように左側の{0}をブロック[変数] > [soundOn]に置き換え、右側の{0}を「1」に変更します。
3. 2.の条件の中に、ブロック[変数] > [変数 {変数} を {0} にする]を追加して、[変数 {soundOn} を {0} にする]となるように{変数}を「soundOn」に変更します。
4. 次にブロック[音楽] > [メロディを開始する {ダダダム} くり返し {一度だけ}]を追加し、{ダダダム}を「ピコーン!」に変更します。
5. [ずっと]の最後に、ブロック[基本] > [一時停止 (ミリ秒)]を追加します。 

micro:bitにプログラムを転送して動作を確認してみましょう。

今度は、叩くたびに「ピコーン!」と音が鳴ります。

当たったときに「ピコーン!」という音を鳴らすプログラムでが、わざわざ独立したループを用意しているのは、音を鳴らすと音を鳴っている間はプログラム(スレッド)の処理が止まってしまうためです。
はじめ、当たり判定をしているループの方で、当たったときに「メロディを開始する」を実行するようにしましたが、音が鳴っている間はゲームが止まり次のアイコンがLEDに表示されなくなってしまいました。それを避けるためにこのような作りにしてあります。

叩く場所を増やしてみよう

ここまでで基本動作のアルミホイルを叩いたら、音が鳴って、スコアが加算されるところまでできました。

段階的に叩く場所を増やしてみましょう。まずは、アルミホイルのカードを一つ追加して2つにしてみます。

プログラムの、P1端子からアナログ入力値を読み取っている箇所に条件を追加していきます。

1. [もし変数 [{analogInput} が {>} {900}]なら]の中に、ブロック[論理] > [もし {真} なら]を追加して、{真}をブロック[論理]  > [{0} {=} {0}]に置き換え、「もし変数 {shape} が {0} と同じなら」という条件になるように、左側の{0}をブロック[変数] > [shape]に置き換えます。
2. [変数 {isHit}を {1} にする]のみを1.の条件の中に移動します。
3. [もし変数 [{analogInput} が {>} {900}]なら]の左の歯車をクリックして[else if]を追加します。
4. 3. で追加した[でなければもし]の右にブロック[論理]  > [{0} {=} {0}]を追加し[もし変数 {analogInput} が {>} {100}]という条件になるように変数、比較演算子および数値をセットします。
5. 4.の条件の中に、ブロック[論理] > [もし {真} なら]を追加して、{真}をブロック[論理]  > [{0} {=} {0}]に置き換え、「もし変数 {shape} が {1} 以上なら」という条件になるように、左側の{0}をブロック[変数] > [shape]に置き換え、比較演算子を{>=}に、右側の{0}を{1}に変更します。
6. 5.の条件の中にブロック[変数] > [変数 {変数} を {0} にする]を追加して、{変数}を「isHit」に、{0}を「1」に変更します。
7. [もし {isHit} {=} {0}]の条件の中に、もう一つ[論理] > [もし {真} なら]を追加して、{真}をブロック[論理]  > [{0} {=} {0}]に置き換え、「もし変数 {isHit} が {1} と同じなら」という条件になるように、左側の{0}をブロック[変数] > [isHit]に置き換え、{0}に「1」をセットします。
8. [変数 {soundOn} を {1} にする]を6. の条件の中に移動します。

次に工作です。
アルミホイルのカードをもう一枚用意して、2つのアルミホイルを抵抗でつなぎます。
抵抗とアルミホイルはゼムクリップではさみます。

アルミホイルと抵抗のつなぎ方についてですが、はじめは抵抗をアルミホイルにテープで留めようとしていましたがが、つなぐ箇所が増えていくとどこかで接触がうまく行かず導通が途切れてしまうことがありました。試行錯誤したところゼムクリップでつなぐと動作が安定しました。ゼムクリップ自体が電気を通すのとしっかり接触して留められるのが良いようです。

micro:bitにプログラムを転送して動作を確認してみます。
「ハート」が出たら左のカード、それ以外のアイコンなら右のカードを叩くと「ピコーン!」と音がでます。

あとは、同じ要領で5つに増やしてみます。

プログラムの変更は、前の3.から5.を繰り返し、条件[{analogInput} {>} {数値}]の数値のみ画像のように置き換えてください。

アナログ入力値の変数「analogInput」による場合分けは整理すると以下のようになります。

analogInputが900より大きい かつ shape = 1 (ハート) なら当たり
analogInputが700より大きく900以下 かつ shape = 2 (8分音符) なら当たり
analogInputが500より大きく700以下 かつ shape = 3 (かさ) なら当たり
analogInputが300より大きく500以下 かつ shape = 4 (きりん) なら当たり
analogInputが100より大きく300以下 かつ shape = 5 (ちょうちょ) なら当たり

参考までですが、完成版のスピーカーを省略した回路図は以下のようになります。(回路図中のアルミホイルは右が「ハート」で、左が「ちょうちょ」となり写真や動画とは逆になります)

3VとGNDの間を1kΩの等間隔に電圧が下がっていくので、analogInputは、1023を5で割った値が読み取られるという仕組みです。

プログラムの準備ができたので、アルミホイルのカードを追加していきます。
5つのカードをこのように抵抗でつなげていきます。

アルミホイルのカードをダンボールに両面テープで貼り付けて、以下のように模様を書きます。

左から「ハート」、「8分音符」、「かさ」、「きりん」、「ちょうちょ」です。
micro:bitを見やすい位置に配置したら完成です。

ではさっそく遊んでみましょう。

5つのカードがちゃんと反応しています。

ここまでのプログラムは、makecodeの[javascript]タブで開くとこのようになっています。コードにするとすごいシンプルに見えてしまいますね。

let shape = 0
let soundOn = 0
let isHit = 0
let analogInput = 0
let score = 0
input.onButtonPressed(Button.A, () => {
    score = 0
    isHit = 0
    for (let カウンター = 0; カウンター <= 2; カウンター++) {
        basic.showNumber(3 - カウンター)
        basic.pause(500)
    }
    for (let i = 0; i < 10; i++) {
        shape = Math.random(5)
        if (shape == 0) {
            basic.showIcon(IconNames.Heart)
        } else if (shape == 1) {
            basic.showIcon(IconNames.EigthNote)
        } else if (shape == 2) {
            basic.showIcon(IconNames.Umbrella)
        } else if (shape == 3) {
            basic.showIcon(IconNames.Giraffe)
        } else {
            basic.showIcon(IconNames.Butterfly)
        }
        basic.pause(1000)
        shape = -1
        basic.clearScreen()
        score += isHit
        isHit = 0
        basic.pause(500)
    }
    music.beginMelody(music.builtInMelody(Melodies.PowerUp), MelodyOptions.Once)
    basic.showNumber(score)
})
basic.forever(() => {
    if (soundOn == 1) {
        soundOn = 0
        music.beginMelody(music.builtInMelody(Melodies.BaDing), MelodyOptions.Once)
    }
    basic.pause(100)
})
basic.forever(() => {
    analogInput = pins.analogReadPin(AnalogPin.P1)
    if (isHit == 0) {
        if (analogInput > 900) {
            if (shape == 0) {
                isHit = 1
            }
        } else if (analogInput > 700) {
            if (shape == 1) {
                isHit = 1
            }
        } else if (analogInput > 500) {
            if (shape == 2) {
                isHit = 1
            }
        } else if (analogInput > 300) {
            if (shape == 3) {
                isHit = 1
            }
        } else if (analogInput > 100) {
            if (shape == 4) {
                isHit = 1
            }
        }
        if (isHit == 1) {
            soundOn = 1
        }
    }
    basic.pause(100)
})

このコードを新しいプロジェクトの[javascript]タブに貼り付けて、[ブロックタブ]に戻すと、ブロックを再現することができます。

プログラムの動作がおかしいなと思ったら、こちらのコードを貼り付けて動作を確認することもできますので、お試しください。

ゲームを発展させてみよう

以上で本編は完成ですが、よりゲームらしくするアイディアを少し紹介してみます。

例えば、途中から絵の切り替え速くして見るなどプログラムを追加するとよりゲーム性が増しそうです。
試しに、10回足して11回目からは表示の切り替えスピードを早くしてみたコードはこんな感じになります。

let shape = 0
let soundOn = 0
let isHit = 0
let analogInput = 0
let score = 0
input.onButtonPressed(Button.A, () => {
    score = 0
    isHit = 0
    for (let カウンター = 0; カウンター <= 2; カウンター++) {
        basic.showNumber(3 - カウンター)
        basic.pause(500)
    }
    for (let i = 0; i < 10; i++) {
        shape = Math.random(5)
        if (shape == 0) {
            basic.showIcon(IconNames.Heart)
        } else if (shape == 1) {
            basic.showIcon(IconNames.EigthNote)
        } else if (shape == 2) {
            basic.showIcon(IconNames.Umbrella)
        } else if (shape == 3) {
            basic.showIcon(IconNames.Giraffe)
        } else {
            basic.showIcon(IconNames.Butterfly)
        }
        basic.pause(1000)
        shape = -1
        basic.clearScreen()
        score += isHit
        isHit = 0
        basic.pause(500)
    }
    music.beginMelody(music.builtInMelody(Melodies.PowerUp), MelodyOptions.Once)
    for (let i = 0; i < 10; i++) {
        shape = Math.random(5)
        if (shape == 0) {
            basic.showIcon(IconNames.Heart)
        } else if (shape == 1) {
            basic.showIcon(IconNames.EigthNote)
        } else if (shape == 2) {
            basic.showIcon(IconNames.Umbrella)
        } else if (shape == 3) {
            basic.showIcon(IconNames.Giraffe)
        } else {
            basic.showIcon(IconNames.Butterfly)
        }
        basic.pause(300)
        shape = -1
        basic.clearScreen()
        score += isHit
        isHit = 0
        basic.pause(200)
    }
    music.beginMelody(music.builtInMelody(Melodies.PowerUp), MelodyOptions.Once)
    basic.showNumber(score)
})
basic.forever(() => {
    analogInput = pins.analogReadPin(AnalogPin.P1)
    if (isHit == 0) {
        if (analogInput > 900) {
            if (shape == 0) {
                isHit = 1
            }
        } else if (analogInput > 700) {
            if (shape == 1) {
                isHit = 1
            }
        } else if (analogInput > 500) {
            if (shape == 2) {
                isHit = 1
            }
        } else if (analogInput > 300) {
            if (shape == 3) {
                isHit = 1
            }
        } else if (analogInput > 100) {
            if (shape == 4) {
                isHit = 1
            }
        }
        if (isHit == 1) {
            soundOn = 1
        }
    }
    basic.pause(100)
})
basic.forever(() => {
    if (soundOn == 1) {
        soundOn = 0
        music.beginMelody(music.builtInMelody(Melodies.BaDing), MelodyOptions.Once)
    }
    basic.pause(100)
})

また、抵抗値とアルミホイルを増やして叩く場所を更に増やすというのも良いかもしれません。1kΩの抵抗でアルミホイルを繋いで増やしていけば、おおむね1023をアルミホイルの数で割った値がアナログ入力として読み込まれるので、それに合わせて条件を足して行くことができます。

あとは、今回はmicro:bitらしい工作になるようにエッジコネクタとブレッドボードは使用しないで作れる範囲のものを取り上げてみましたが、最終的には結構複雑になってしまっていました。ジャンパーワイヤーのあたりがごちゃっとしています。
このもぐらたたきゲームもエッジコネクタとブレッドボードを使用するともう少しスッキリと実装できるかもしれませんのでそれも試してみてください。


この記事が気に入ったらサポートをしてみませんか?