コントローラー組み立てガイド

クレーンゲーム コントローラーの配線とプログラムをまとめています。 Arduino Pro Micro(ATmega32U4)を使い、ブレッドボード上で組み立てます。

1. 回路構成

配線表

接続先Pro Micro ピン
7セグ TM1637 — CLKD2
7セグ TM1637 — DIOD3
投下ボタンD4
スタートボタンD5
LED(220Ω 経由)D6
ジョイスティック VRX(X軸)A0
ジョイスティック VRY(Y軸)A1
電源 +(7セグ・ジョイスティック)VCC
GND(7セグ・ジョイスティック・ボタン・LED)GND

Pro Micro ピン配置(使用ピンを赤で表示)

Pro Micro TXO RXI GND GND 2 7セグ CLK 3 7セグ DIO 4 投下ボタン 5 スタートボタン 6 LED (220Ω) 7 8 9 RAW GND ブレッドボード − RST VCC ブレッドボード + A3 A2 A1 ジョイ VRY (Y) A0 ジョイ VRX (X) 15 14 16 10

ブレッドボード回路図

ジョイスティック GND VCC VRX VRY 7セグ TM1637 CLK DIO VCC GND 投下 → D4 スタート → D5 LED → D6 220Ω以上 Pro Micro RAW GND RST VCC A3 A2 A1 A0 15 14 16 TXO RXI GND GND 2 3 4 5 6 7 8 VCC(+) GND(−) 信号
メモ
  • 各パーツの端子の並び順(CLK / DIO / VCC / GND など)は、製品によって異なる場合があります。必ず実物に印刷された文字を見て繋いでください。この図の並び順はあくまで一例です。
  • 今回はブレッドボード上での再現でOKです。本番の筐体では、各ボタン・スティックの設置位置が離れるため、配線ケーブルが長くなります。
  • VCC と GND はブレッドボードの電源レール(+/−)に繋ぎ、そこから 7セグとジョイスティックに分配します。
  • ボタンは INPUT_PULLUP 方式のため、片側を信号ピン、反対側を GND に繋ぎます(外付け抵抗は不要)。
  • LED には抵抗を必ず挟んでください。220Ω が目安ですが、手持ちがなければ 220Ω 以上でもOKです(明るさが少し落ちる程度)。アノード=長い足が D6 側、カソード=短い足が GND 側。

2. プログラミング

ゲームの流れ

係員が Start を押すと1ラウンド開始。Drop ボタン押下、または 30秒経過で強制的に投下し、待機に戻ります。 これを繰り返します(3回で1プレイ)。

待機(IDLE) Start待ち。ジョイ・Drop は無視 ① 係員が Start を押す Enter送信 / Timer=30 / LED点灯 プレイ中(30秒) ・7セグ カウントダウン ・ジョイスティック → WASD 送信 ・Drop で Space 送信も可 ② Drop押下 または 30秒経過 投下(強制押し判定) Space送信 / WASD全release LED消灯 / 操作受付終了 降下アニメーション (Unity 側で再生) ③ アニメ後、また Start(繰り返し)

実装のヒント

コントローラー班が書きはじめたコード(Is_game / Timer)をベースに、全体の流れ(loop の骨格)と、必要な部品(関数)をまとめました。 まず「全体像」で loop が何をするかを掴んでから、①〜④の部品を用意して loop に書き込んでいくと分かりやすいです。

全体像:loop の流れ

これが骨格です。コメントの各処理を、下の①〜④の部品を使って書き込んでいきます。

void loop() {
  if (!Is_game) {
    // 【待機】
    //   Start が押されたら(②の justPressed を使う):
    //     Keyboard.write(KEY_RETURN);   // Enter送信
    //     Timer = 30;  lastTick = millis();
    //     digitalWrite(LED_DROP, HIGH); // LED点灯
    //     Is_game = true;
    //   ※ ジョイスティックは送らない(操作を受け付けない)

  } else {
    // 【プレイ中】
    //   1) ④のカウントダウン処理を書く(Timer=0 なら投下へ)
    //   2) ③のジョイスティック → WASD を書く(setKey を4方向ぶん)
    //   3) Drop が押された(②)or Timer=0 → 投下:
    //        Keyboard.write(' ');          // Space送信
    //        keyW/keyA/keyS/keyD を全部 release
    //        digitalWrite(LED_DROP, LOW);  // LED消灯
    //        Is_game = false;              // 待機に戻る
  }
}

① ピン定義・状態変数・setup

#include <TM1637Display.h>
#include <Keyboard.h>

#define CLK 2
#define DIO 3
#define BTN_DROP  4
#define BTN_START 5
#define LED_DROP  6
#define JOY_X A0
#define JOY_Y A1
#define THRESHOLD_LOW  300
#define THRESHOLD_HIGH 700

bool Is_game = false;        // プレイ中か
int  Timer = 30;             // 残り秒数
unsigned long lastTick = 0;  // カウントダウン用

TM1637Display display(CLK, DIO);

void setup() {
  pinMode(BTN_DROP, INPUT_PULLUP);
  pinMode(BTN_START, INPUT_PULLUP);
  pinMode(LED_DROP, OUTPUT);
  display.setBrightness(7);
  display.showNumberDec(0);
  Keyboard.begin();
  delay(2000);   // 暴走対策:起動後すこし待つ
}

② ボタンの「押された瞬間」を検出(チャタリング対策込み)

機械式ボタンは押した瞬間に信号が細かくバタつきます(チャタリング)。 「一度変化したら少しの間は無視する」ことで防ぎます。これを使うと、押しっぱなしでも1回だけ反応します。

const unsigned long DEBOUNCE = 30;  // ms

// pin が押された瞬間だけ true を返す(チャタリング対策込み)
// stableLow=安定状態, lastChange=最後に変化した時刻(ボタンごとに用意)
bool justPressed(int pin, bool &stableLow, unsigned long &lastChange) {
  bool reading = (digitalRead(pin) == LOW);            // 押すと LOW
  if (reading != stableLow && millis() - lastChange > DEBOUNCE) {
    lastChange = millis();
    stableLow = reading;
    if (stableLow) return true;                        // HIGH→LOW = 押された瞬間
  }
  return false;
}

// ボタンごとに状態をグローバルに用意する:
// bool startLow = false, dropLow = false;
// unsigned long startChg = 0, dropChg = 0;
// 使うとき:if (justPressed(BTN_START, startLow, startChg)) { ... }

③ ジョイスティック → WASD(押した間だけ送る)

倒した瞬間に press、戻した瞬間に release。これでキーが押しっぱなしで固まりません。

bool keyW = false, keyA = false, keyS = false, keyD = false;

// 倒した瞬間に press、戻した瞬間に release
void setKey(char key, bool &state, bool wantPress) {
  if (wantPress && !state)      { Keyboard.press(key);   state = true;  }
  else if (!wantPress && state) { Keyboard.release(key); state = false; }
}

// プレイ中に呼ぶイメージ:
// int x = analogRead(JOY_X);  int y = analogRead(JOY_Y);
// setKey('w', keyW, y > THRESHOLD_HIGH);  // 上  …これを4方向ぶん

④ カウントダウンは millis() で(delay を使わない)

delay(1000) を使うと、その1秒間ジョイスティックやボタンが反応しません。millis() で「1秒経ったら減らす」方式にします。

// 「プレイ中」の中で呼ぶ。1秒ごとに Timer を1減らす
if (millis() - lastTick >= 1000) {
  lastTick += 1000;
  Timer--;
  display.showNumberDec(Timer);
  if (Timer <= 0) {
    // ここに「時間切れ = 強制投下」の処理を書く
  }
}

ここから仕上げるポイント

  • しきい値(THRESHOLD)を実機で調整:ジョイスティックの個体差で中央値がズレるので、実際の値を見て微調整する
  • 本番ゲーム(Unity)との接続確認:WASD / Space / Enter が正しく届くか、デバッグ用サイトで確認する
  • LED の発展:将来は「投下後に3秒点滅 → 消灯」などの演出も追加できる
  • ベルは手動:景品が取れたら係員が手動でベルを鳴らす運用。コントローラー側の電子工作は不要
このあとの流れ

コントローラーが組めたら、テストページで「キー入力が正しく届くか」「クレーンが操作できるか」を確認してください。本番ゲームができる前の動作チェックに使えます。

コントローラーをテストする →