贪吃蛇游戏
使用 Arduino Nano 和 MAX7219 LED 矩阵模块构建的经典贪吃蛇游戏。
项目介绍
贪吃蛇游戏:使用 Arduino 在 LED 矩阵上重新构想的经典
经典游戏
很少有游戏能像经典的贪吃蛇游戏那样经受住时间的考验。它起源于手机的早期,以其简单而令人上瘾的游戏玩法继续吸引着各个年龄段的玩家。现在,在DIY电子产品和创意编程的时代,让我们踏上一段旅程,为这个深受喜爱的经典注入新的活力。
在这个项目中,我们将通过利用 LED 矩阵和 Arduino 的力量将 Snake 游戏提升到一个全新的水平。这种怀旧和现代技术的融合不仅保证了有趣的游戏体验,而且为那些对电子和编程感兴趣的人提供了一个绝佳的学习机会。
关于 8×8 LED 矩阵模块
MAX7219 LED矩阵模块
LED矩阵模块由两部分组成:
- 8×8 LED 矩阵 (1088AS)
- MAX7219 IC
该MAX7219是一款专为 LED 矩阵显示器设计的多功能芯片。它通过串行通信简化了控制,使用多路复用实现高效的 LED 管理,并允许亮度调节。您可以将多个芯片以菊花链方式连接起来,以实现更大的显示器,使其可靠且易于集成到使用 SPI 协议的项目中。
有关 LED 矩阵模块工作的更多详细信息,请查看以下链接:
$ https://lastminuteengineers.com/max7219-dot-matrix-arduino-tutorial/$
代码
// The MAX7219 uses SPI communication protocol...Hence, import SPI.h library
#include <SPI.h>
// The chip select pin
#define CS 10
// Few necessary registers for configuring the MAX7219 chip
#define DECODE_MODE 9
#define INTENSITY 0x0A
#define SCAN_LIMIT 0x0B
#define SHUTDOWN 0x0C
#define DISPLAY_TEST 0x0F
// Buttons used for controlling the snake
#define left_button 2
#define right_button 3
volatile byte move_left = 0;
volatile byte move_right = 0;
// Varibles for snake
int snake_l = 2;
const int max_len = 15;
int snake[max_len][2];
byte cur_heading = 0;
// Variable for food blob
int blob[2] = { 0, 0 };
int is_eaten = 1;
// The game scene
byte scene[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// A general function to send data to the MAX7219
void SendData(uint8_t address, uint8_t value) {
digitalWrite(CS, LOW);
SPI.transfer(address); // Send address.
SPI.transfer(value); // Send the value.
digitalWrite(CS, HIGH); // Finish transfer.
}
// Function to initialize the game variables
void init_game() {
is_eaten = 1;
move_left = 0;
move_right = 0;
cur_heading = 0;
snake_l = 2;
for (int i = 0; i < max_len; i++)
for (int j = 0; j < 2; j++)
snake[i][j] = 0;
snake[max_len - 1][0] = 2;
snake[max_len - 1][1] = 5;
snake[max_len - 2][0] = 1;
snake[max_len - 2][1] = 5;
refresh_scene();
while ((move_left || move_right) == 0)
;
move_left = 0;
move_right = 0;
}
// Function to draw snake on gamescene
void spawn_snake() {
// If the snake goes out of the scene, it enters from the other side
for (int j = max_len - snake_l; j < max_len; j++) {
if (snake[j][0] <= 0)
snake[j][0] = 8 + snake[j][0];
else if (snake[j][0] >= 9)
snake[j][0] = snake[j][0] - 8;
if (snake[j][1] <= 0)
snake[j][1] = 8 + snake[j][1];
else if (snake[j][1] >= 9)
snake[j][1] = snake[j][1] - 8;
// Draw the snake on the LED matrix
scene[snake[j][0] - 1] |= (1 << (snake[j][1] - 1));
}
}
// Function to update the position and length of the snake
void snake_move() {
// If snake eats a blob...Increase length
if (snake[max_len - 1][0] == blob[0] && snake[max_len - 1][1] == blob[1]) {
is_eaten = 1;
snake_l += 1;
}
// Move each pixel forward
for (int i = snake_l - 1; i >= 1; i--) {
snake[max_len - 1 - i][0] = snake[max_len - i][0];
snake[max_len - 1 - i][1] = snake[max_len - i][1];
}
// Move the head according to button input
if (move_left == 1) {
if (cur_heading == 0) {
cur_heading = 1;
snake[max_len - 1][1] -= 1;
} else if (cur_heading == 1) {
cur_heading = 2;
snake[max_len - 1][0] -= 1;
} else if (cur_heading == 2) {
cur_heading = 3;
snake[max_len - 1][1] += 1;
} else if (cur_heading == 3) {
cur_heading = 0;
snake[max_len - 1][0] += 1;
}
move_left = 0;
} else if (move_right == 1) {
if (cur_heading == 0) {
cur_heading = 3;
snake[max_len - 1][1] += 1;
} else if (cur_heading == 1) {
cur_heading = 0;
snake[max_len - 1][0] += 1;
} else if (cur_heading == 2) {
cur_heading = 1;
snake[max_len - 1][1] -= 1;
} else if (cur_heading == 3) {
cur_heading = 2;
snake[max_len - 1][0] -= 1;
}
move_right = 0;
} else {
if (cur_heading == 0) {
snake[max_len - 1][0] += 1;
} else if (cur_heading == 1) {
snake[max_len - 1][1] -= 1;
} else if (cur_heading == 2) {
snake[max_len - 1][0] -= 1;
} else if (cur_heading == 3) {
snake[max_len - 1][1] += 1;
}
}
}
// Function to generate a blob and draw the blob on the gamescene
void blob_generator() {
// If blob is eaten by the snake, generate one
if (is_eaten) {
blob[0] = random(1, 9);
blob[1] = random(1, 9);
}
// Draw the blob on the gamescene
scene[blob[0] - 1] |= (1 << (blob[1] - 1));
is_eaten = 0;
}
// Function to redraw the gamescene to the LED matrix with updated variables
void refresh_scene() {
for (int i = 0; i < 8; i++)
scene[i] = 0x00;
snake_move();
spawn_snake();
blob_generator();
for (int i = 1; i < 9; i++)
SendData(i, scene[i - 1]);
}
// Callback for interrupt attached to left button
void update_left() {
move_left = 1;
}
// Callback for interrupt attached to right button
void update_right() {
move_right = 1;
}
// Setup function
void setup() {
// GPIO Configuration
pinMode(left_button, INPUT_PULLUP);
pinMode(right_button, INPUT_PULLUP);
pinMode(CS, OUTPUT);
// SPI configuration
SPI.setBitOrder(MSBFIRST); // Most significant bit first
SPI.begin(); // Start SPI
SendData(DISPLAY_TEST, 0x00); // Finish test mode.
SendData(DECODE_MODE, 0x00); // Disable BCD mode.
SendData(INTENSITY, 0x01); // Use lowest intensity.
SendData(SCAN_LIMIT, 0x0f); // Scan all digits.
SendData(SHUTDOWN, 0x01); // Turn on chip.
// Random seed generation...Uses the noise in the analog channel 0 to create a random seed
randomSeed(analogRead(0));
// Attach interrupts to the buttons
attachInterrupt(digitalPinToInterrupt(left_button), update_left, FALLING);
attachInterrupt(digitalPinToInterrupt(right_button), update_right, FALLING);
// Enable interrupt
sei();
// Start the game
init_game();
}
void loop() {
// Check if snake is at max length
if (snake_l == max_len) {
// If yes, display win and restart
byte win_scene[8] = { B11100011, B00100100, B01000010, B11100100, B00000011, 0, B00011100, 0 };
for (int i = 1; i < 9; i++)
SendData(i, win_scene[i - 1]);
delay(5000);
init_game();
}
// Check if snake has collided with itself
for (int i = 0; i < max_len - 1; i++) {
if (snake[i][0] == snake[max_len - 1][0] && snake[i][1] == snake[max_len - 1][1]) {
// If yes, blink all leds and restart the game
delay(1000);
for (int j = 0; j < 4; j++) {
SendData(DISPLAY_TEST, 0x01);
delay(500);
SendData(DISPLAY_TEST, 0x00);
delay(500);
}
init_game();
break;
}
}
// Keep refreshing the matrix with updated data....
refresh_scene();
// ...Every 0.5 secs
delay(500);
}