본문 바로가기
라즈베리파이

휴대용 스네이크 게임 콘솔 만들기- Raspberry Pi PICO2 버전

by 모빌리티키즈 2025. 3. 9.
728x90
반응형

여러분께 흥미롭고 창의적인 DIY 프로젝트를 소개합니다. 휴대용 스네이크 게임 콘솔Raspberry Pi PICO 2 드라이버 회로RGB 64x32 P3 매트릭스 패널로 구성된 독특한 게임 콘솔로, 완전히 휴대가 가능하고 어디서든 즐길 수 있습니다.

주요 특징 및 개요

이 프로젝트의 하이라이트는 다음과 같습니다:

  • 독특한 설계: Fusion360으로 디자인된 장치를 기반으로 3D 프린팅된 프레임과 맞춤형 PCB를 사용합니다.
  • 클래식 스네이크 게임: 방향 버튼 4개로 간단하게 즐길 수 있는 스네이크 게임입니다. 빨간 점을 먹으며 점수를 쌓고, 자신의 몸에 부딪히면 게임이 끝납니다.
  • 휴대성: 단일 3.7V 2600mAh 리튬 이온 셀로 구동되어 완전히 이동 가능한 게임기로 제작되었습니다.
 
 

프로젝트에 필요한 준비물

필요한 자재는 다음과 같습니다:

  • 맞춤형 PCB
  • Raspberry Pi PICO 2
  • RGB 매트릭스 64x32
  • IP5306 IC
  • 커패시터(10uF)
  • USB 마이크로 포트
  • 18650 리튬 배터리 및 셀 홀더
  • 푸시 버튼
  • 3D 프린팅 부품

주요 구성 요소: 64x32 RGB 매트릭스 패널

64x32 RGB 매트릭스 패널은 2048개의 RGB LED가 64x32 그리드로 배열되어 있어 생생한 텍스트, 그래픽 및 애니메이션을 구현합니다. 패널은 HUB75 인터페이스로 작동하며, 이를 통해 RGB 채널 및 데이터 처리가 이루어집니다.

  • 작동 원리: 행렬 스캐닝 기술이 시프트 레지스터를 통해 픽셀 데이터를 이동시키고, 디멀티플렉서를 사용해 특정 행을 결정합니다.
  • 확장 가능성: IN/OUT 연결을 사용해 여러 패널을 연결할 수 있지만, 제어 솔루션(PICO 2)의 데이터 처리 용량을 고려해야 합니다.

Waveshare 패널 정보

RGB 매트릭스 패널은 Waveshare에서 제작되었습니다. Waveshare Wiki에서 추가적인 기술 정보를 확인할 수 있습니다.

 

1단계: 64x32 RGB 매트릭스

 
 
 

우리는 64x32 RGB 매트릭스 패널을 사용하고 있으며, 64 x 32 그리드에 2048개의 RGB LED를 배열하여 생생한 텍스트, 그래픽 및 애니메이션을 만듭니다.

아래 링크를 클릭하여 이 매트릭스 패널에 대한 간략한 소개를 읽을 수 있습니다.

https://www.instructables.com/64x32-Matrix-Panel-Setup-With-PICO-2/

RGB, 주소, 클럭, 데이터 래치 및 출력 활성화 핀을 포함한 여러 제어 핀을 사용하는 HUB75 인터페이스는 이 패널을 작동하는 데 사용됩니다.

행 열 스캐닝 기술은 픽셀 데이터 행을 시프트 레지스터로 이동하는 HUB75 링크에 의해 가능합니다. 그런 다음 디멀티플렉서를 사용하여 표시해야 하는 행을 결정합니다. RGB 채널, 어드레싱 핀 A, B, C, D, 클록 신호(CLK), 래치 신호(LAT) 및 출력 활성화(OE) 핀은 모두 HUB75 커넥터에 포함되어 있습니다.

또한 제공된 IN 및 OUT 연결을 사용하여 체인을 생성하기 위해 여러 패널을 쌍으로 연결할 수 있습니다. 우리가 사용하고 있는 제어 솔루션(PICO 2)이 두 개 이상의 디스플레이의 추가 데이터 부하를 관리할 수 있는지 확인하는 것은 여러 패널을 연결하는 데 있어 어려운 점 중 하나입니다.

이 매트릭스는 Waveshare에서 제작했으며 매트릭스 보드에 대한 자세한 내용은 아래 위키 페이지에서 확인할 수 있습니다.

https://www.waveshare.com/wiki/RGB-Matrix-P3-64x32

2단계: 콘솔 디자인


이 프로젝트의 첫 번째 단계는 매트릭스 뒷면에 두 개의 손잡이 모양의 구성 요소가 설치된 콘솔의 3D 모델을 구축하는 것이었습니다. 그런 다음 한쪽에 특수 버튼 보드의 모델을 만들었습니다.

4개의 스페이서로 핸드그립 프레임에 고정되는 PICO 드라이버 회로는 장치 후면에 위치합니다.

매트릭스에 이미 있는 3개의 M3 인서트를 사용하여 2개의 Handgrip 프레임 구성 요소가 매트릭스 패널 후면에 장착됩니다. 각 손잡이에는 M3 볼트를 사용하여 손잡이를 매트릭스에 부착할 수 있도록 추가한 세 개의 장착 구멍이 있습니다.

4개의 M2 나사는 버튼 보드를 콘솔의 한쪽에 고정하는 데 사용됩니다.

모델이 완성되면 왼쪽 및 오른쪽 손잡이와 4개의 스페이서에 대한 메시 파일을 내보낸 다음 0.6mm 노즐을 사용하여 검은색 PLA로 3D 프린팅했습니다.

행렬

3단계: PCB 설계: PICO 드라이버

PCB Cad 소프트웨어를 사용하여 먼저 PICO 드라이버 보드 설계를 위한 회로도를 만듭니다. Raspberry Pi PICO 2를 Matrix의 HUB75 커넥터에 연결하기 위해 설정은 CON 16 커넥터로 구성됩니다.

A에서 GPIO19로, B에서 GPIO16으로, C에서 GPIO18로, D에서 GPIO20으로, E에서 GPIO22로, CLK에서 GPIO11, LAT/STB에서 GPIO12, OE에서 GPIO13, R1에서 GPIO2, G1에서 GPIO3, B1에서 GPIO4, R2에서 GPIO5, G2에서 GPIO8, B2에서 GPIO9로.

버튼용 CON5 커넥터를 추가했으며 4개의 핀은 PICO의 GPIO6, GPIO7, GPIO14 및 GPIO15에 연결됩니다. GND는 CON5의 5번째 핀에 부착되어 있습니다. 각 GPIO는 이 CON5에 연결된 버튼 보드에 의해 GND로 당겨지며 PICO는 이를 버튼 누름으로 감지할 수 있습니다.

또한 전력 관리 IC인 IP5306(완전 통합형 다기능 전력 관리 SoC)을 통합하여 전체 설정에 전력을 공급했습니다.

3.7V를 입력으로 사용하여 5V 2.1A를 안정적으로 제공할 수 있으며, 이 경우 매트릭스 및 PICO 2와 같은 모든 5V 장치에 전원을 공급하는 데 사용할 수 있습니다.

자세한 내용은 IP5306 데이터시트를 확인할 수 있습니다 http://www.injoinic.com/wwwroot/uploads/files/20200221/0405f23c247a34d3990ae100c8b20a27.pdf

회로도 설정에 따라 netlist를 내보내고 CAD 파일의 보드 레이아웃을 참조하여 보드 파일을 생성했습니다. PICO 2, 버튼, 리튬 전지 홀더 및 USB 미니 포트는 모두 기판의 상단에 있고 모든 SMD 구성 요소는 하단에 있습니다.

4 단계 : PCB 디자인 : 버튼 보드

다음으로 버튼 보드의 회로도를 준비합니다. 4개의 푸시 버튼이 있으며 각 버튼의 4개 및 3개 핀이 GND에 연결됩니다. 또한 GPIO의 경우 각 커넥터의 1 및 2, GND의 경우 3 및 4에 연결된 CON5 커넥터가 있습니다.

회로도를 설정한 후 PCB 편집기를 사용하여 버튼을 적절한 위치에 정렬하고 CAD 파일 레이아웃을 정확히 따라 보드 파일을 준비했습니다.

5단계: PCB

 
 

이 프로젝트를 위해 버튼 보드와 PICO 드라이버 보드라는 두 개의 PCB를 만들었습니다. 버튼 보드와 PICO 드라이버 보드에 대한 두 가지 주문이 이루어졌습니다.

버튼 보드 PCB는 화이트 솔더 마스크와 블랙 실크스크린으로 주문되었으며 PICO 드라이버 PCB는 블루 솔더 마스크와 화이트 실크스크린으로 주문되었습니다.

 

6단계: PCB 어셈블리: PICO 드라이버

 
 
 
 
 
 
  1. 솔더페이스트 디스펜싱 니들을 사용하여 각 SMD 부품 PAD에 솔더페이스트(이 경우 63/37 Sn/Pb 솔더페이스트)를 적용하여 PICO 드라이버 조립 공정을 시작합니다.
  2. 다음으로 ESD 핀셋을 사용하여 PCB의 각 SMD 구성 요소를 선택하고 배치합니다.
  3. 구성 요소 배치 후 회로가 올라가고 리플 로우 핫플레이트에 설정되어 PCB의 온도를 아래에서 솔더 페이스트의 융점까지 높입니다. 솔더 페이스트가 녹고 PCB가 190°C의 온도에 도달하면 모든 SMD 구성 요소가 제자리에 고정됩니다.
  4. 리플로우 절차에 따라 보드를 뒤집고 납땜 인두를 사용하여 18650 홀더를 배치합니다.
  5. USB 마이크로 포트와 푸시 스위치를 설치한 후 보드를 뒤집고 두 패드를 모두 납땜합니다.

7단계: 전원 섹션 테스트

 
 

PICO DRIVER 조립 공정을 진행하기 전에 조립 공정을 중지하고 18650 3.7V 2600mAh 리튬 전지를 셀 홀더에 올바른 극성으로 배치하여 전원 모듈 회로를 확인합니다.

그런 다음 푸시 버튼을 누르면 장치가 켜집니다. 우리는 멀티미터를 사용하여 장치의 출력 전압을 측정하며 이는 5V여야 합니다. 이제 PICO 2를 PCB에 추가하고 조립 프로세스를 시작할 수 있습니다.

8단계: PCB 어셈블리: PICO 드라이버(어셈블리의 나머지 부분)

 
  1. PICO 20 풋프린트에 두 개의 암 CON 2 헤더 핀을 배치하고 HUB75 커넥터 풋프린트에 두 개의 수 CON 8 헤더 핀을 배치한 후 보드를 뒤집고 납땜 인두를 사용하여 패드를 납땜합니다.
  2. 마지막으로 리튬 셀을 셀 홀더에 다시 설치하고 Raspberry Pi PICO를 CON 20 헤더 핀 위에 배치했습니다.

PICO DRIVER 어셈블리가 완료되었습니다.

9단계: PCB 어셈블리: 버튼 보드

 
 
 

버튼 보드 조립 프로세스를 시작하기 위해 먼저 보드 상단에서 푸시 버튼을 배치한 다음 하단에서 패드를 납땜합니다.

10단계: PICO 드라이버 및 매트릭스 어셈블리

 
 
 
 
  1. Matrix Kit에 포함된 와이어 하네스를 사용하여 먼저 PICO 드라이버와 Matrix를 연결합니다. 와이어 하네스의 양극 와이어를 PICO DRIVER의 5V 출력에 납땜하고 음극 와이어를 PICO 드라이버의 GND에 납땜했습니다.
  2. 다음으로, 암 와이어 하니스 커넥터를 매트릭스의 수 커넥터에 연결합니다.
  3. 그런 다음 HUB75 와이어 하네스를 사용하여 매트릭스 및 PICO 드라이버 GPIO를 연결합니다. 먼저 매트릭스 커넥터에 연결한 다음 다른 쪽 끝을 PICO 드라이버에 연결합니다.

11단계: 테스트 스케치: 인생의 게임

 

Mattrix와 PICO DRIVE를 먼저 연결한 다음, 구성이 제대로 작동하는지 확인하기 위해 FastLED 라이브러리의 예제 스케치에서 가져온 이전에 변환된 Game of Life 코드를 사용하여 PICO를 플래시했습니다.

https://www.instructables.com/64x32-Matrix-Panel-Setup-With-PICO-2/

"생명의 게임"으로 알려진 놀라운 세포 자동화 장치는 1970년 영국의 수학자 존 호튼 콘웨이에 의해 개발되었습니다. 제로 플레이어 게임이기 때문에 추가 입력이 필요하지 않습니다. 오히려 게임의 진행은 초기 상태에 따라 결정됩니다.

인생의 게임에 대한 자세한 내용은 여기를 참조하십시오.

https://playgameoflife.com/

게임은 무작위 구성으로 시작하고 중단된 후 새로운 무작위 구성으로 다시 시작되며 예, 이 설정은 Turing이 완료되었습니다.

#include <Adafruit_Protomatter.h>

// Pin definitions
#define R1 2
#define G1 3
#define B1 4
#define R2 5
#define G2 8
#define B2 9
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11
#define LAT 12
#define OE 13

#define WIDTH 64
#define HEIGHT 32

uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };
Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);

bool grid[WIDTH][HEIGHT];
bool newGrid[WIDTH][HEIGHT];

void setup() {
matrix.begin();
randomSeed(analogRead(0));

// Initialize grid with random values
for(int x = 0; x < WIDTH; x++) {
for(int y = 0; y < HEIGHT; y++) {
grid[x][y] = random(2);
}
}
}

void loop() {
matrix.fillScreen(0);

// Update grid based on Game of Life rules
for(int x = 0; x < WIDTH; x++) {
for(int y = 0; y < HEIGHT; y++) {
int aliveNeighbors = countAliveNeighbors(x, y);
if(grid[x][y]) {
// Any live cell with two or three live neighbors survives.
if(aliveNeighbors < 2 || aliveNeighbors > 3) {
newGrid[x][y] = false;
} else {
newGrid[x][y] = true;
}
} else {
// Any dead cell with three live neighbors becomes a live cell.
if(aliveNeighbors == 3) {
newGrid[x][y] = true;
} else {
newGrid[x][y] = false;
}
}
if(newGrid[x][y]) {
matrix.drawPixel(x, y, matrix.color565(255, 255, 255)); // White color
}
}
}

// Copy newGrid to grid
memcpy(grid, newGrid, sizeof(grid));
matrix.show();
delay(100); // Adjust the delay for speed control

// Check if the grid is stable or empty
if(isGridStableOrEmpty()) {
resetGrid();
}
}

int countAliveNeighbors(int x, int y) {
int aliveNeighbors = 0;
for(int dx = -1; dx <= 1; dx++) {
for(int dy = -1; dy <= 1; dy++) {
if(dx == 0 && dy == 0) continue;
int nx = (x + dx + WIDTH) % WIDTH;
int ny = (y + dy + HEIGHT) % HEIGHT;
if(grid[nx][ny]) {
aliveNeighbors++;
}
}
}
return aliveNeighbors;
}

bool isGridStableOrEmpty() {
for(int x = 0; x < WIDTH; x++) {
for(int y = 0; y < HEIGHT; y++) {
if(grid[x][y]) {
return false;
}
}
}
return true;
}

void resetGrid() {
for(int x = 0; x < WIDTH; x++) {
for(int y = 0; y < HEIGHT; y++) {
grid[x][y] = random(2);
}
}
}

여기에서 Adafruit_Protomatter 라이브러리를 사용하고 있으며, 이 코드를 사용하기 전에 Arduino IDE에 설치해야 합니다.

12단계: 프레임 & 매트릭스 조립

 
 
 

3D 프린팅된 두 개의 손잡이 프레임의 장착 구멍을 매트릭스의 장착 구멍과 정렬하여 이제 매트릭스 뒷면에 부착할 수 있습니다. 그런 다음 6개의 M3 볼트를 사용하여 프레임과 매트릭스를 함께 연결합니다. 매트릭스 뒷면에 추가된 M3 황동 인서트 덕분에 프레임과 매트릭스를 M3 볼트로 쉽게 연결할 수 있습니다.

13단계: 버튼 보드 - 프레임 조립

 
 
 

그런 다음 버튼 보드는 콘솔 전면에서 배치되고 4개의 M2 나사를 사용하여 제자리에 고정됩니다.

14단계: PICO 드라이버 및 프레임 어셈블리

 
 
 
 
  1. 이제 4개의 3D 프린팅 스페이서를 사용하여 3D 프린팅 프레임 위의 콘솔 뒷면에 PICO DRIVER를 배치합니다.
  2. 프레임의 장착 구멍 위에 배치된 4개의 스페이서 위에 PICO 드라이버를 놓은 다음 M3 나사를 사용하여 PICO 드라이버를 프레임에 고정합니다.

단계 15: 최종 조립: 버튼 보드 & PICO 드라이버 와이어 연결

 
 
 
 
  1. DPAD 버튼 PCB와 PICO DRIVER 보드를 함께 배선하는 것은 조립 공정의 마지막 단계입니다.
  2. 이를 위해 먼저 버튼 보드의 CON5 포트에 5개의 연결 와이어를 추가한 다음 각 와이어를 올바른 핀 순서로 PICO DRIVER에 연결합니다.
  3. 버튼 보드의 UP 핀은 GPIO7에, DOWN 핀은 GPIO6에, LEFT 핀은 GPIO15에, RIGHT 핀은 GPIO14에 연결됩니다.
  4. PICO DRIVER와 버튼 보드 사이의 전선이 연결되면 여분의 전선 길이를 프레임 내부에 조심스럽게 밀어 넣고 약간의 HOTGLUE로 고정합니다.

SNAKE GAME CONSOLE ASSEMBLY가 완성되었습니다.

16단계: 메인 코드

다음은 이 프로젝트에서 사용한 기본 코드이며 간단한 코드입니다.

#include <Adafruit_Protomatter.h>
// Pin definitions
#define R1 2
#define G1 3
#define B1 4
#define R2 5
#define G2 8
#define B2 9
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11
#define LAT 12
#define OE 13
// Button pins
#define BUTTON_UP 7
#define BUTTON_DOWN 6
#define BUTTON_LEFT 15
#define BUTTON_RIGHT 14
#define WIDTH 64
#define HEIGHT 32
#define SNAKE_LENGTH 8
uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };
Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);
struct SnakeSegment {
int x;
int y;
};
SnakeSegment snake[WIDTH * HEIGHT]; // Increased size to allow snake growth
int snakeLength = SNAKE_LENGTH;
int dx = 1, dy = 0;
uint16_t snakeColor = matrix.color565(0, 255, 0); // Green color
uint16_t foodColor = matrix.color565(255, 0, 0); // Red color
uint16_t gameOverColor = matrix.color565(255, 0, 0); // Red color for "Game Over" screen
int foodX, foodY;
int score = 0; // Score variable
bool gameOver = false; // Game over flag
void placeFood() {
// Ensure the new food position does not overlap with the snake
bool foodOnSnake;
do {
foodX = random(WIDTH);
foodY = random(HEIGHT);
foodOnSnake = false;
for (int i = 0; i < snakeLength; i++) {
if (snake[i].x == foodX && snake[i].y == foodY) {
foodOnSnake = true;
break;
}
}
} while (foodOnSnake);
}
void setup() {
matrix.begin();
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);
// Initialize snake in the middle of the screen
for (int i = 0; i < snakeLength; i++) {
snake[i] = { WIDTH / 2 - i, HEIGHT / 2 };
}
placeFood(); // Place initial food
// Clear screen
matrix.fillScreen(matrix.color565(0, 0, 0));
matrix.show();
}
void drawScore() {
matrix.setCursor(WIDTH - 10, 2); // Adjust cursor to top-right corner
matrix.setTextColor(matrix.color565(255, 255, 255)); // Set text color to white for visibility
matrix.print(score); // Display only the score number
}
void checkGameOver() {
// Check if snake collides with itself
for (int i = 1; i < snakeLength; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
gameOver = true;
break;
}
}
}
void resetGame() {
snakeLength = SNAKE_LENGTH;
dx = 1;
dy = 0;
score = 0;
gameOver = false;
// Initialize snake in the middle of the screen
for (int i = 0; i < snakeLength; i++) {
snake[i] = { WIDTH / 2 - i, HEIGHT / 2 };
}
placeFood(); // Place initial food
}
void drawGameOver() {
// Fill the screen with red
matrix.fillScreen(gameOverColor);
// Draw "Game Over!" in black
uint16_t blackColor = matrix.color565(0, 0, 0);
matrix.setCursor((WIDTH / 2) - 30, (HEIGHT / 2) - 4);
matrix.setTextColor(blackColor);
matrix.print("Game Over!");
// Display score in white
matrix.setCursor(WIDTH - 10, 2); // Adjust cursor to top-right corner
matrix.setTextColor(blackColor);
matrix.print(score);
}
void loop() {
if (gameOver) {
drawGameOver();
matrix.show();
delay(5000); // Wait 5 seconds
resetGame(); // Reset the game
return;
}
// Check button states and update direction
if (!digitalRead(BUTTON_UP) && dy == 0) {
dx = 0; dy = -1;
} else if (!digitalRead(BUTTON_DOWN) && dy == 0) {
dx = 0; dy = 1;
} else if (!digitalRead(BUTTON_LEFT) && dx == 0) {
dx = -1; dy = 0;
} else if (!digitalRead(BUTTON_RIGHT) && dx == 0) {
dx = 1; dy = 0;
}
// Move snake
for (int i = snakeLength - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}
snake[0].x += dx;
snake[0].y += dy;
// Wrap around screen edges
if (snake[0].x >= WIDTH) snake[0].x = 0;
if (snake[0].x < 0) snake[0].x = WIDTH - 1;
if (snake[0].y >= HEIGHT) snake[0].y = 0;
if (snake[0].y < 0) snake[0].y = HEIGHT - 1;
// Check if snake eats the food
if (snake[0].x == foodX && snake[0].y == foodY) {
snakeLength++;
score++; // Increment score
placeFood();
}
// Clear screen
matrix.fillScreen(matrix.color565(0, 0, 0));
// Draw snake
for (int i = 0; i < snakeLength; i++) {
matrix.drawPixel(snake[i].x, snake[i].y, snakeColor);
}
// Draw food
matrix.drawPixel(foodX, foodY, foodColor);
// Draw score
drawScore();
// Update display
matrix.show();
// Check for game over
checkGameOver();
delay(100); // Adjust delay for speed control
}

이 코드는 다음과 같은 7개의 섹션으로 나뉩니다.

RGB 신호, 어드레스 라인, 클럭, 스네이크 길이, 래치 및 출력 인에이블, 버튼에 대한 핀과 함께 RGB 매트릭스를 제어하는 Adafruit_Protomatter 라이브러리를 포함하는 라이브러리 및 핀 정의도 여기에 정의됩니다.

 

#include <Adafruit_Protomatter.h>

// Pin definitions
#define R1 2
#define G1 3
#define B1 4
#define R2 5
#define G2 8
#define B2 9
#define A 10
#define B 16
#define C 18
#define D 20
#define CLK 11
#define LAT 12
#define OE 13

// Button pins
#define BUTTON_UP 7
#define BUTTON_DOWN 6
#define BUTTON_LEFT 15
#define BUTTON_RIGHT 14

#define WIDTH 64
#define HEIGHT 32
#define SNAKE_LENGTH 8
매트릭스 초기화 및 뱀 구조- 여기서 매트릭스는 너비, 높이, RGB 핀, 어드레스 핀, 클럭, 래치 및 출력 인에이블로 설정됩니다. 뱀 구조체(snake structure) 뱀의 세그먼트에 대한 구조를 정의하여 뱀의 길이, 방향, 색상, 먹이 위치, 점수 및 게임 오버 플래그를 초기화합니다.
uint8_t rgbPins[] = { R1, G1, B1, R2, G2, B2 };
uint8_t addrPins[] = { A, B, C, D };

Adafruit_Protomatter matrix(WIDTH, HEIGHT, 1, rgbPins, 4, addrPins, CLK, LAT, OE, false);

struct SnakeSegment {
int x;
int y;
};

SnakeSegment snake[WIDTH * HEIGHT]; // Increased size to allow snake growth
int snakeLength = SNAKE_LENGTH;
int dx = 1, dy = 0;
uint16_t snakeColor = matrix.color565(0, 255, 0); // Green color
uint16_t foodColor = matrix.color565(255, 0, 0); // Red color
uint16_t gameOverColor = matrix.color565(255, 0, 0); // Red color for "Game Over" screen
int foodX, foodY;
int score = 0; // Score variable
bool gameOver = false; // Game over flag
Place Food Function- Places food on the matrix, ensuring it doesn't overlap with the snake's position.

void placeFood() {
bool foodOnSnake;
do {
foodX = random(WIDTH);
foodY = random(HEIGHT);
foodOnSnake = false;
for (int i = 0; i < snakeLength; i++) {
if (snake[i].x == foodX && snake[i].y == foodY) {
foodOnSnake = true;
break;
}
}
} while (foodOnSnake);
}

 

설정 기능- 매트릭스 및 단추 핀을 초기화합니다. 뱀을 화면 중앙에 배치하고 초기 먹이를 놓습니다.

void setup() {
matrix.begin();
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);

// Initialize snake in the middle of the screen
for (int i = 0; i < snakeLength; i++) {
snake[i] = { WIDTH / 2 - i, HEIGHT / 2 };
}

placeFood(); // Place initial food

// Clear screen
matrix.fillScreen(matrix.color565(0, 0, 0));
matrix.show();
}

Drawing Functions-drawScore는 화면에 점수를 표시합니다. drawGameOver는 "게임 오버" 메시지와 점수를 표시합니다.

void drawScore() {
matrix.setCursor(WIDTH - 10, 2);
matrix.setTextColor(matrix.color565(255, 255, 255));
matrix.print(score);
}

void drawGameOver() {
matrix.fillScreen(gameOverColor);
uint16_t blackColor = matrix.color565(0, 0, 0);
matrix.setCursor((WIDTH / 2) - 30, (HEIGHT / 2) - 4);
matrix.setTextColor(blackColor);
matrix.print("Game Over!");
matrix.setCursor(WIDTH - 10, 2);
matrix.setTextColor(blackColor);
matrix.print(score);
}

게임 로직 함수-checkGameOver는 뱀이 자신과 충돌하는지 확인하여 게임 오버 플래그를 설정합니다. resetGame은 게임 변수를 재설정하고 뱀 위치를 초기화합니다.

void checkGameOver() {
for (int i = 1; i < snakeLength; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
gameOver = true;
break;
}
}
}

void resetGame() {
snakeLength = SNAKE_LENGTH;
dx = 1;
dy = 0;
score = 0;
gameOver = false;

for (int i = 0; i < snakeLength; i++) {
snake[i] = { WIDTH / 2 - i, HEIGHT / 2 };
}

placeFood();
}
Main Loop- Controls the main game logic, including snake movement, food placement, score updates, drawing, and game-over checks.

void loop() {
if (gameOver) {
drawGameOver();
matrix.show();
delay(5000);
resetGame();
return;
}

if (!digitalRead(BUTTON_UP) && dy == 0) {
dx = 0; dy = -1;
} else if (!digitalRead(BUTTON_DOWN) && dy == 0) {
dx = 0; dy = 1;
} else if (!digitalRead(BUTTON_LEFT) && dx == 0) {
dx = -1; dy = 0;
} else if (!digitalRead(BUTTON_RIGHT) && dx == 0) {
dx = 1; dy = 0;
}

for (int i = snakeLength - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}
snake[0].x += dx;
snake[0].y += dy;

if (snake[0].x >= WIDTH) snake[0].x = 0;
if (snake[0].x < 0) snake[0].x = WIDTH - 1;
if (snake[0].y >= HEIGHT) snake[0].y = 0;
if (snake[0].y < 0) snake[0].y = HEIGHT - 1;

if (snake[0].x == foodX && snake[0].y == foodY) {
snakeLength++;
score++;
placeFood();
}

matrix.fillScreen(matrix.color565(0, 0, 0));

for (int i = 0; i < snakeLength; i++) {
matrix.drawPixel(snake[i].x, snake[i].y, snakeColor);
}

matrix.drawPixel(foodX, foodY, foodColor);
drawScore();
matrix.show();
checkGameOver();
delay(100);
}

루프는 뱀의 방향을 제어하기 위해 버튼 입력을 확인하고, 뱀의 위치를 업데이트하고, 화면 래핑을 처리하고, 음식 섭취를 확인하고, 디스플레이를 업데이트합니다. 게임이 종료되면 "게임 오버" 메시지가 표시되고 5초 동안 기다린 후 게임이 초기화됩니다.

 

 

 

 

 

 

 

728x90
반응형