Arduino NanoとMAX7219で作る24×16 LEDポングゲーム機
必要な部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino Nano R3 | 1 | |
| 8x8 LEDマトリックスモジュール (MAX7219搭載) | 6 | AliExpress等で入手可能な完成品モジュール |
| ロータリーポテンショメータ (10kΩ) | 2 | 左右パドル操作用 |
| ブザー | 1 | 効果音用 |
必要な工具と機器
- はんだごて
使用するアプリとオンラインサービス
- Arduino IDE
プロジェクト概要
この小型ポングゲーム機は、6枚の8x8 LEDマトリックスを連結し、以下のサイトの指示とコードをベースに製作しました:Instructables - Pong
自作マトリックス基板の代わりに、MAX7219搭載の安価な8x8モジュールを6個購入して使用しています。
各モジュールの「CLK」「LOAD」ピンは全て共通接続。「DIN」は最初のモジュールのみArduinoへ、「DOUT」を次のモジュールの「DIN」へ、というようにデイジーチェーン接続します。左右パドルは10kΩポテンショメータ2個をアナログピンA0、A1に接続。ブザーはD9ピンに接続。
完成後はプラスチックケースに収め、コントローラー部分は電気工事用ボックスに納めました。
使用ライブラリ LedControl は以下から入手可能です:
https://github.com/wayoda/LedControl
ソースコード
メインスケッチ (Arduino)
//We always have to include the library
#include "LedControl.h"
byte rightscore;
byte leftscore;
int toneloop=1000;
byte pongdisplay[]={ // 起動時の"Pong"表示パターン
B11111111, B00000000, B00001001, B00000000, B00001001, B00000000, B00000110, B00000000,
B00000000, B00000000, B00000000, B00000000, B01110000, B00000000, B10001000, B00000000,
B10001000, B00000000, B01110000, B00000000, B00000000, B00000000, B00000000, B00000000,
B11110000, B00000000, B00001000, B00000000, B00001000, B00000000, B11110000, B00000000,
B00000000, B00000000, B00000000, B00000000, B11001111, B00000000, B10001001, B00000000,
B10001001, B00000000, B11111111, B00000000, B00000000, B00000000, B00000000, B00000000,
};
byte zero[]={ // スコア表示用 '0'
B00000000, B00000000, B00111100, B01000010, B01000010, B01000010, B00111100, B00000000 };
byte one[]={ // '1'
B00000000, B00000000, B10001000, B10000100, B11111110, B10000000, B10000000, B00000000 };
byte two[]={ // '2'
B00000000, B01100010, B01010001, B01010001, B01001001, B01000110, B00000000, B00000000 };
byte three[]={ // '3'
B00000000, B00100110, B01000001, B01001001, B00110110, B00000000, B00000000, B00000000 };
byte four[]={ // '4'
B00000000, B00000000, B00001111, B00001000, B00001000, B11111111, B00000000, B00000000 };
byte five[]={ // '5'
B00000000, B00000000, B10011110, B10010010, B10010010, B11110010, B00000000, B00000000 };
byte displayevens=0;
byte displayrow=0;
byte displayindex=0;
byte displayodds=1;
unsigned long time;
unsigned long currenttime;
byte mode;
byte deflect;
byte carry;
int line;
LedControl lc=LedControl(12,11,10,6); // DIN=12, CLK=11, LOAD=10, デバイス数=6
long randnumber;
byte dir;
int startpos;
byte row;
byte column;
long randNumber;
byte start;
byte diry;
int pass;
byte limit;
#define leftpaddle 0 // 左パドル → アナログ入力0
#define rightpaddle 1 // 右パドル → アナログ入力1
int directionpong;
int rightpongval;
int leftpongval;
byte blinkx;
byte pong[]={ // ボールとパドル位置を格納 (48行分)
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
};
void setup(){
pinMode(leftpaddle, INPUT);
pinMode(rightpaddle, INPUT);
Serial.begin(9600);
// 6台のマトリックス初期化
for(int i=0;i<6;i++){
lc.shutdown(i,false);
lc.setIntensity(i,1);
lc.clearDisplay(i);
}
// 起動時 "Pong" 表示
while(displayevens<5){
while(displayrow<=7){
lc.setRow(displayevens,displayrow,pongdisplay[displayindex]);
displayrow++; displayindex+=2;
}
displayrow=0; displayevens+=2;
}
displayrow=0; displayindex=1;
randomSeed(analogRead(4));
start=1; time=millis(); currenttime=millis();
// ゲームモード選択画面 (左ポテンショメータで選択)
while(currenttime-time<3000){
leftpongval=analogRead(leftpaddle);
leftpongval=map(leftpongval,1023,20,2,0);
if(leftpongval==0){ lc.setLed(3,2,7,true); lc.setLed(3,3,7,false); lc.setLed(3,4,7,false); mode=1; limit=2; }
if(leftpongval==1){ lc.setLed(3,2,7,true); lc.setLed(3,3,7,true); lc.setLed(3,4,7,false); mode=2; limit=1; }
if(leftpongval==2){ lc.setLed(3,2,7,true); lc.setLed(3,3,7,true); lc.setLed(3,4,7,true); mode=3; limit=2; }
delay(50); lc.clearDisplay(3); currenttime=millis();
}
}
void loop(){
paddles(); // パドル位置読み取り・反映
pongsim(); // ボール物理演算
displayscreen(); // 画面描画
}
// パドル位置をpong[]配列に反映
void paddles(){
int carry=0;
leftpongval=analogRead(leftpaddle);
leftpongval=map(leftpongval,0,1010,0,13);
rightpongval=analogRead(rightpaddle);
rightpongval=map(rightpongval,1023,20,0,13);
// 前回のパドル表示をクリア
pong[0]=B00000000; pong[1]=B00000000; pong[46]=B00000000; pong[47]=B00000000;
// 右パドル描画 (モードに応じてサイズ変更)
if(mode!=3){
if(rightpongval<=7){
rightpongval=map(rightpongval,0,7,7,0);
bitSet(pong[46], rightpongval);
if(rightpongval-1>=0) bitSet(pong[46],rightpongval-1);
else{ bitSet(pong[47], 7); if(mode!=2){ bitSet(pong[47],6); carry=1; } }
if(mode!=2){ if(carry==0){ if(rightpongval-2>=0) bitSet(pong[46], rightpongval-2); else{ bitSet(pong[47], 7); carry=1; } } }
}
if(rightpongval>7){
rightpongval=map(rightpongval,8,13,7,limit);
bitSet(pong[47],rightpongval); bitSet(pong[47],rightpongval-1);
if(mode!=2) bitSet(pong[47],rightpongval-2);
}
} else {
pong[46]=B11111111; pong[47]=B11111111;
lc.setRow(4,7,pong[46]); lc.setRow(5,7,pong[47]);
}
// 左パドル描画 (同様)
carry=0;
if(leftpongval<=7){
leftpongval=map(leftpongval,0,7,7,0);
bitSet(pong[0], leftpongval);
if(leftpongval-1>=0) bitSet(pong[0],leftpongval-1);
else{ bitSet(pong[1], 7); if(mode!=2){ bitSet(pong[1],6); carry=1; } }
if(mode!=2){ if(carry==0){ if(leftpongval-2>=0) bitSet(pong[0], leftpongval-2); else{ bitSet(pong[1], 7); } } }
}
if(leftpongval>7){
leftpongval=map(leftpongval,8,13,7,limit);
bitSet(pong[1],leftpongval); bitSet(pong[1],leftpongval-1);
if(mode!=2) bitSet(pong[1],leftpongval-2);
}
}
// ボールの移動・衝突・反射処理
void pongsim(){
if( ((line==2 || line==3) && diry == 0) || ((line==44 || line==45) && diry==1) ){
ball_meets_paddle();
}
if(start==1){ // 新ゲーム開始時のボール初期化
randnumber=random(1,7); dir=random(2); diry=random(2);
if(diry==0){ line=random(30,34); bitSet(pong[line],randnumber); }
if(diry==1){ line=random(12,16); bitSet(pong[line],randnumber); }
start=0;
}
// 以下、dir(左右方向)とdiry(上下方向)の組み合わせごとに
// ボール移動、壁反射、マトリックス境界越え(carry)、パドル衝突判定を実装
// (コードは長大なため要約。オリジナルを参照)
// ...
// 最終的に ball_meets_paddle() でパドル衝突時の角度変化・得点処理を行う
}
// 画面描画: pong[]配列の内容を6台のマトリックスへ転送
void displayscreen(){
displayevens=0; displayrow=0; displayindex=0; displayodds=1;
while(displayevens<5){
while(displayrow<=7){
lc.setRow(displayevens,displayrow,pong[displayindex]);
displayrow++; displayindex+=2;
}
displayrow=0; displayevens+=2;
}
displayrow=0; displayindex=1;
while(displayodds<6){
while(displayrow<8){
lc.setRow(displayodds,displayrow,pong[displayindex]);
displayrow++; displayindex+=2;
}
displayrow=0; displayodds+=2;
}
}
void clearscreen(){ for(int i=0;i<49;i++) pong[i]=B00000000; }
// パドル衝突判定と反射角・得点処理 (詳細省略)
void ball_meets_paddle(){ /* ... */ }
void scoreleft(){ /* 左プレイヤー得点 */ }
void scoreright(){ /* 右プレイヤー得点 */ }
void setscore(){ /* スコアをpong[]の所定位置に描画 */ }
回路図
製造プロセス
- Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
- Arduino UNOとOLEDディスプレイでクラシックPongゲームを自作:ステップバイステップチュートリアル
- DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
- スマート貯金箱を作ろう:Arduino Nanoでコインアクセプターを制御
- Arduino NanoとMAX7219で作る24×16 LEDポングゲーム機
- 自作16×8 LEDマトリックスでテトリスゲーム:Arduino Nano実装
- ArduinoでBluetooth経由でLEDを制御 – シンプルDIYガイド
- Pixel Chaser: Arduino Nanoで作るワンタップLEDゲーム
- Arduino Unoで8×8 LEDマトリックスを制御:ステップバイステップガイド
- Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム
- Arduino UNOとEthernetシールドで太陽放射を測定する自作ピラノメーター