工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

Arduino MegaとMAX72XX LEDマトリックスで作るクラシック「アステロイド」ゲーム

必要な部品と材料

部品名数量備考
Arduino Mega 25601
MAX7219 LEDマトリックスドライバーシールド (Maxim MAXREFDES99#)18x8 LEDマトリックス含む
タクトスイッチ 12mm2左右移動用
アクティブブザー1効果音用
ブレッドボード1

プロジェクト概要

MAX7219ドライバーで制御する8x8 LEDマトリックスをディスプレイとして、タクトスイッチ2個で自機 (星型宇宙船) を左右に動かし、上から落下してくるアステロイド (小惑星) を避けるシンプルなゲームです。時間経過とともに落下速度が上がり、衝突するとゲームオーバー。生存時間 (ティック数) がスコアとして表示されます。Joshua Weßner氏による2018年の作品です。

回路図

Arduino MegaとMAX72XX LEDマトリックスで作るクラシック「アステロイド」ゲーム

接続ピン (シールド仕様に合わせて調整):

ソースコード (完全版)

/******************************************************** 
 * 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); }

遊び方と拡張アイデア

必要なライブラリ

製造プロセス

  1. チェス:古代起源から現代製造まで——専門家の概観
  2. ピンボール:歴史、設計、製造——クラシックアーケードゲームの全貌
  3. アメリカンフットボールの進化と職人技:古代競技から現代NFL基準まで
  4. Arduino UNOとOLEDディスプレイでクラシックPongゲームを自作:ステップバイステップチュートリアル
  5. Arduino Nanoで37LEDルーレットゲームを自作:ヨーロピアンスタイル完全再現
  6. Arduino NanoとMAX7219で作る24×16 LEDポングゲーム機
  7. 自作16×8 LEDマトリックスでテトリスゲーム:Arduino Nano実装
  8. Arduinoとジョイスティックでインタラクティブゲーム – LEDフィードバック付き
  9. PIRセンサー連動 インタラクティブLEDマトリクスドアディスプレイ - ホリデーグリーティング
  10. 音声反応型 8 LED ライトディスプレイ
  11. スマートArduino四足歩行ロボットの製作:ステップバイステップガイド