Arduino MegaとMAX72XX LEDマトリックスで作るクラシック「アステロイド」ゲーム
必要な部品と材料
| 部品名 | 数量 | 備考 |
|---|---|---|
| Arduino Mega 2560 | 1 | |
| MAX7219 LEDマトリックスドライバーシールド (Maxim MAXREFDES99#) | 1 | 8x8 LEDマトリックス含む |
| タクトスイッチ 12mm | 2 | 左右移動用 |
| アクティブブザー | 1 | 効果音用 |
| ブレッドボード | 1 |
プロジェクト概要
MAX7219ドライバーで制御する8x8 LEDマトリックスをディスプレイとして、タクトスイッチ2個で自機 (星型宇宙船) を左右に動かし、上から落下してくるアステロイド (小惑星) を避けるシンプルなゲームです。時間経過とともに落下速度が上がり、衝突するとゲームオーバー。生存時間 (ティック数) がスコアとして表示されます。Joshua Weßner氏による2018年の作品です。
回路図

接続ピン (シールド仕様に合わせて調整):
- DataIn → Pin 12
- CLK → Pin 11
- LOAD (CS) → Pin 10
- 左スイッチ → Pin 2 (割り込み対応)
- 右スイッチ → Pin 3 (割り込み対応)
- ブザー → Pin 9 (PWM)
ソースコード (完全版)
/********************************************************
* MAX72XX LEDマトリックスで遊ぶアステロイド回避ゲーム
* (c) 2018 by Joshua Weßner
*******************************************************/
#include "LedControl.h" // Eberhard Fahle氏のライブラリ https://wayoda.github.io/LedControl/
// ピン定義 (ご使用のハードウェアに合わせて変更)
LedControl lc = LedControl(12, 11, 10, 1); // DIN, CLK, LOAD, デバイス数
// グローバル変数
unsigned long delaytime = 1000; // 表示更新間隔 (初期値、実質未使用)
String scoreString;
int score = 0;
int hundreds = 0;
String scoreStr;
String scoreArr[3] = {"", "", ""};
int pinLeft = 2;
int pinRight = 3;
volatile unsigned long buttonPressed = 0;
int buttonDelay = 150; // チャタリング対策 (ms)
volatile bool gameOver = false;
int tick = 300; // ゲームティック間隔 (ms) - 徐々に短くなる
int tickCounter = 1;
unsigned long now = 0;
int ship = 3; // 自機初期位置 (列 0-7)
int columns[8] = {0,0,0,0,0,0,0,0}; // 各列のアステロイド位置 (0=なし, 1-10=行)
int randomInt;
void setup() {
gameOver = false;
hundreds = 0;
scoreArr[0] = scoreArr[1] = scoreArr[2] = "";
score = 0;
tick = 300;
tickCounter = 1;
ship = 3;
now = millis();
buttonPressed = millis();
randomSeed(analogRead(A15)); // 未使用ピンで乱数シード生成
for (int i = 0; i < 8; i++) columns[i] = 0;
// Serial.begin(9600); // デバッグ用
lc.shutdown(0, false); // パワーセービング解除
lc.setIntensity(0, 1); // 明るさ設定 (0-15)
lc.clearDisplay(0);
pinMode(pinLeft, INPUT_PULLUP);
pinMode(pinRight, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(pinLeft), left, FALLING);
attachInterrupt(digitalPinToInterrupt(pinRight), right, FALLING);
}
// 左ボタン割り込み
void left() {
if (millis() - buttonPressed > buttonDelay) {
if (ship != 0) ship--; else ship = 7; // 画面端でワープ
lc.clearDisplay(0);
buttonPressed = millis();
}
if (gameOver) { gameOver = false; setup(); } // 再起動
}
// 右ボタン割り込み
void right() {
if (millis() - buttonPressed > buttonDelay) {
if (ship != 7) ship++; else ship = 0;
lc.clearDisplay(0);
buttonPressed = millis();
}
if (gameOver) { gameOver = false; setup(); }
}
void loop() {
// ゲームティック処理
if (millis() - now > tick) {
score++; // 生存ティック数 = スコア
now = millis();
// 4ティックごとにアステロイド生成 & 速度上昇
if (tickCounter == 1) {
tick = tick / 1.02; // 約2%高速化
randomInt = random(0, 8);
if (columns[randomInt] == 0) columns[randomInt] = 1; // 最上段に生成
}
if (tickCounter != 4) tickCounter++; else tickCounter = 1;
// 全列のアステロイドを1段下げる
for (int i = 0; i < 8; i++) {
if (columns[i] == 10) columns[i] = 0; // 画面外なら消去
if (columns[i] != 0) columns[i]++; // 落下
}
lc.clearDisplay(0);
}
// 表示更新
// 自機 (最下段 row 7)
lc.setLed(0, 7, ship, true);
// アステロイド (2ドットで表示)
for (int i = 0; i < 8; i++) {
if (columns[i] > 0) {
lc.setLed(0, columns[i] - 2, i, true);
lc.setLed(0, columns[i] - 3, i, true);
}
}
// 衝突判定 (自機位置にアステロイドが row 9 または 8 に来たら衝突)
if (columns[ship] == 10 || columns[ship] == 9) {
lc.clearDisplay(0);
// 爆発アニメーション + サウンド
for (int i = 0; i < 4; i++) {
lc.setLed(0, 7, ship + i, true);
lc.setLed(0, 7, ship - i, true);
lc.setLed(0, 7 - i, ship + i, true);
lc.setLed(0, 7 - i, ship - i, true);
lc.setLed(0, 7 - 1.5 * i, ship, true);
unsigned long t = millis();
int randomSound = 1000;
while (millis() - t <= 250) {
randomSound--;
tone(9, random(randomSound, 1000));
}
lc.clearDisplay(0);
noTone(9);
}
delay(500);
// スコア表示 (最大2桁、100以上は下2桁+百の位をドットで表示)
scoreStr = String(score);
scoreArr[0] = String(scoreStr.charAt(0));
scoreArr[1] = String(scoreStr.charAt(1));
scoreArr[2] = String(scoreStr.charAt(2));
if (score < 100) {
for (int i = 0; i < 2; i++) {
drawDigit(scoreArr[i], 1 + i * 4);
}
} else {
for (int i = 1; i < 3; i++) {
drawDigit(scoreArr[i], 1 + (i - 1) * 4);
}
// 百の位を左端にドットで表示
for (int i = 1; i < 10; i++) {
if (scoreArr[0] == String(i)) hundreds = i;
}
for (int i = 1; i <= hundreds; i++) {
lc.setLed(0, 0, i - 1, true);
lc.setLed(0, 1, i - 1, true);
delay(200);
}
}
gameOver = true;
while (gameOver) { /* ボタン待ち */ }
}
}
// 数字描画関数 (3x5ドットフォント、2列使用)
void drawDigit(String d, int pos) {
if (d == "0") draw0(pos);
else if (d == "1") draw1(pos);
else if (d == "2") draw2(pos);
else if (d == "3") draw3(pos);
else if (d == "4") draw4(pos);
else if (d == "5") draw5(pos);
else if (d == "6") draw6(pos);
else if (d == "7") draw7(pos);
else if (d == "8") draw8(pos);
else if (d == "9") draw9(pos);
}
void draw0(int p) { lc.setColumn(0, 0+p, B00011111); lc.setColumn(0, 1+p, B00010001); lc.setColumn(0, 2+p, B00011111); }
void draw1(int p) { lc.setColumn(0, 0+p, B00001000); lc.setColumn(0, 1+p, B00011111); }
void draw2(int p) { lc.setColumn(0, 0+p, B00010111); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00011101); }
void draw3(int p) { lc.setColumn(0, 0+p, B00010001); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00011111); }
void draw4(int p) { lc.setColumn(0, 0+p, B00011100); lc.setColumn(0, 1+p, B00000100); lc.setColumn(0, 2+p, B00011111); }
void draw5(int p) { lc.setColumn(0, 0+p, B00011101); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00010111); }
void draw6(int p) { lc.setColumn(0, 0+p, B00011111); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00010111); }
void draw7(int p) { lc.setColumn(0, 0+p, B00010000); lc.setColumn(0, 1+p, B00010011); lc.setColumn(0, 2+p, B00011100); }
void draw8(int p) { lc.setColumn(0, 0+p, B00011111); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00011111); }
void draw9(int p) { lc.setColumn(0, 0+p, B00011101); lc.setColumn(0, 1+p, B00010101); lc.setColumn(0, 2+p, B00011111); }
遊び方と拡張アイデア
- 左右スイッチで自機を操作 (画面端でワープ)
- ゲームオーバー後、任意のボタンでリスタート
- 難易度調整:
tick初期値やtick = tick / 1.02の係数を変更 - アステロイド生成確率調整:
if (columns[randomInt] == 0)条件を確率的に - ハイスコア機能: EEPROMに保存
- 複数LEDマトリックス連結で横長画面化 (LedControlのデバイス数増加)
必要なライブラリ
- LedControl (Arduinoライブラリマネージャからインストール可能)
製造プロセス
- チェス:古代起源から現代製造まで——専門家の概観
- ピンボール:歴史、設計、製造——クラシックアーケードゲームの全貌
- アメリカンフットボールの進化と職人技:古代競技から現代NFL基準まで
- Arduino UNOとOLEDディスプレイでクラシックPongゲームを自作:ステップバイステップチュートリアル
- Arduino Nanoで37LEDルーレットゲームを自作:ヨーロピアンスタイル完全再現
- Arduino NanoとMAX7219で作る24×16 LEDポングゲーム機
- 自作16×8 LEDマトリックスでテトリスゲーム:Arduino Nano実装
- Arduinoとジョイスティックでインタラクティブゲーム – LEDフィードバック付き
- PIRセンサー連動 インタラクティブLEDマトリクスドアディスプレイ - ホリデーグリーティング
- 音声反応型 8 LED ライトディスプレイ
- スマートArduino四足歩行ロボットの製作:ステップバイステップガイド