아두이노 나노와 네오픽셀 매트릭스로 만드는 네온 퐁 게임
부품 및 소모품
| 네오픽셀 스트립 (개별 제어 가능) |
1개 |
| 점퍼 와이어 (일반) |
1세트 |
| 아두이노 나노 R3 |
1개 |
| 암 헤더 8핀 1열 (0.1인치) |
1개 |
| 퍼마-프로토 브레드보드 하프 사이즈 |
1개 |
| 아날로그 조이스틱 (일반) |
1개 |
| 회전 포텐셔미터 (일반) |
1개 |
필요한 도구 및 장비
앱 및 온라인 서비스
프로젝트 소개
매트릭스 만들기
영화 '매트릭스'가 아니라 네오픽셀 매트릭스입니다! 개별 제어 가능한 네오픽셀 LED 스트립을 사서 풀고, 스트립의 화살표 방향을 확인하세요. 한 방향으로만 연결해야 하며 반대로 연결하면 손상될 수 있습니다.
30 LED/m, 5m 스트립을 사용해 10개 LED씩 잘라냈습니다. 카드스톡 위에 스트립을 지그재그로 번갈아 배치하고, 적당한 길이로 자른 와이어를 네오픽셀 단자에 납땜했습니다. 14번 반복하니 매트릭스 완성! 멀티미터로 쇼트 여부 확인 필수(쇼트 나면 나노, 매트릭스, USB 포트까지 타버립니다 - 경험담). 'NEO_BOTTOM + NEO_LEFT + NEO_COLUMNS + NEO_ZIGZAG' 파라미터로 네오매트릭스 테스트 스케치 업로드해 'howdy'가 스크롤되면 디스플레이 완료!
게임 로직
퐁 게임엔 패들, 벽, 공이 있습니다. 코드는 공의 X, Y 위치를 추적하는 변수를 가집니다. 공의 방향은 방향 변수에 1 또는 -1을 대입해 처리. 패들은 포텐셔미터 값을 0~15로 매핑해 제어하는 직사각형입니다. 패들의 Y는 고정이라 별도 매핑 변수 불필요. 게임 시작 시 공은 (1,1)에서 시작하고 플레이어는 공이 한 번 튕기게 해 시작합니다. 물리는 공의 방향과 패들 맞은 위치를 고려합니다.
플레이 타임!
이제 네오픽셀 매트릭스를 아두이노 나노에 연결하세요. 조이스틱 +5V→5V, GND→GND, X축→A0. 새 퐁 게임 즐기세요! 플레이어 추가, 물리 개선 등으로 난이도 높여보세요!
코드
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#define PIN 6
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(15, 10, PIN,
NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT + NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800);
// 패들 및 공 위치 변수
int paddleX = 0, paddleY = 0;
int oldPaddleX, oldPaddleY;
int ballDirectionX = 1, ballDirectionY = 1;
int score = 0;
int ballSpeed = 10; // 낮을수록 빠름
int ballX, ballY, oldBallX, oldBallY;
void setup() {
Serial.begin(9600);
matrix.begin();
matrix.fillScreen(0);
matrix.setTextColor(matrix.Color(0,255,0));
matrix.print("GO");
matrix.show();
delay(2000);
matrix.fillScreen(0);
matrix.show();
}
void loop() {
int myWidth = matrix.width();
int myHeight = matrix.height();
// 포텐셔미터로 패들 위치 매핑
paddleX = map(analogRead(A0), 0, 1023, 0, 12);
paddleY = 8;
// 이전 패들 위치 지우기
if (oldPaddleX != paddleX || oldPaddleY != paddleY) {
matrix.fillRect(oldPaddleX, oldPaddleY, 4, 1, matrix.Color(0,0,0));
}
// 패들 그리기
matrix.fillRect(paddleX, paddleY, 4, 1, matrix.Color(0,0,255));
oldPaddleX = paddleX;
oldPaddleY = paddleY;
matrix.show();
// 공 업데이트 및 그리기
if (millis() % ballSpeed < 2) {
moveBall();
}
matrix.show();
// 득점 처리
if(ballY > 8 && (millis() > 10000)){
score += 1;
matrix.fillScreen(0);
matrix.setTextColor(matrix.Color(255,0,0));
matrix.setCursor(0,2);
matrix.print(String(score));
matrix.show();
delay(4000);
ballX = random(3,11);
ballY = random(1,1);
matrix.fillScreen(0);
matrix.show();
delay(1000);
}
delay(5);
}
void moveBall() {
// 화면 벗어나면 방향 반전
if (ballX > matrix.width() - 1 || ballX < 0) ballDirectionX = -ballDirectionX;
if (ballY > matrix.height() || ballY < 0) ballDirectionY = -ballDirectionY;
// 공과 패들 충돌 체크
if (inPaddle(ballX, ballY, paddleX, paddleY, 4, 1)) {
if(ballX == paddleX && ballY == paddleY){ ballDirectionX = -ballDirectionX; ballDirectionY = -ballDirectionY; }
else if(ballX == paddleX + 3 && ballY == paddleY){ ballDirectionX = ballDirectionX; ballDirectionY = -ballDirectionY; }
else if(ballX == paddleX + 1 && ballY == paddleY){ ballDirectionX = -ballDirectionX; ballDirectionY = -ballDirectionY; }
else if(ballX == paddleX + 2 && ballY == paddleY){ ballDirectionX = ballDirectionX; ballDirectionY = -ballDirectionY; }
}
// 공 위치 업데이트
ballX += ballDirectionX;
ballY += ballDirectionY;
// 이전 공 위치 지우기
if (oldBallX != ballX || oldBallY != ballY) {
matrix.drawPixel(oldBallX, oldBallY, matrix.Color(0,0,0));
}
// 현재 공 위치 그리기
matrix.drawPixel(ballX, ballY, matrix.Color(150,150,0));
oldBallX = ballX;
oldBallY = ballY;
}
// 공이 패들 영역과 겹치는지 확인
boolean inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
boolean result = false;
if ((x >= rectX && x <= (rectX + rectWidth)) && (y >= rectY && y <= (rectY + rectHeight))) {
result = true;
}
return result;
}
회로도
회로도 이미지 참조