#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Keypad.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// 键盘配置
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9,8,7,6}; // 连接行引脚
byte colPins[COLS] = {5,4,3,2}; // 连接列引脚
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// 游戏区域参数
#define SCORE_AREA_HEIGHT 12 // 分数栏高度
#define OFFSET_Y SCORE_AREA_HEIGHT
#define CELL_SIZE 4 // 网格单元尺寸
#define GAME_WIDTH SCREEN_WIDTH
#define GAME_HEIGHT (SCREEN_HEIGHT - SCORE_AREA_HEIGHT)
#define GRID_WIDTH (GAME_WIDTH / CELL_SIZE) // 32
#define GRID_HEIGHT (GAME_HEIGHT / CELL_SIZE) // 13
#define SNAKE_MAX_LENGTH 30 // 最大蛇长
// 游戏数据结构
struct GameObject {
int8_t x;
int8_t y;
};
GameObject snake[SNAKE_MAX_LENGTH];
GameObject food;
uint8_t snakeLength = 3;
int8_t dir = 0; // 0-right,1-down,2-left,3-up
bool gameOver = false;
uint16_t score = 0;
void setup() {
// 初始化显示
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
while(1);
}
// 初始化键盘(启用内部上拉)
for(int i=0; i<ROWS; i++) pinMode(rowPins[i], INPUT_PULLUP);
for(int j=0; j<COLS; j++) pinMode(colPins[j], INPUT_PULLUP);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
initializeGame();
}
void loop() {
static uint32_t lastUpdate = 0;
handleInput();
if (millis() - lastUpdate >= 150) { // 游戏速度控制
if (!gameOver) {
updateGame();
checkCollisions();
checkFood();
drawGame();
} else {
showGameOver();
}
lastUpdate = millis(); //millis()返回从程序启动到当前时间的毫秒数
}
}
/*********************
* 游戏核心逻辑 *
*********************/
void initializeGame() {
// 初始化蛇位置(游戏区中心)
snake[0] = {GRID_WIDTH/2, GRID_HEIGHT/2};
snakeLength=3;
for(uint8_t i=1; i<snakeLength; i++){
snake[i] = {snake[i-1].x-1, snake[i-1].y};
}
dir = 0;
gameOver = false;
score = 0;
generateFood();
}
void handleInput() {
static int8_t lastDir = dir;
char key = customKeypad.getKey();
if (key) {
switch(key) {
case '2': if(lastDir != 1) dir = 3; break; // 上
case '8': if(lastDir != 3) dir = 1; break; // 下
case '4': if(lastDir != 0) dir = 2; break; // 左
case '6': if(lastDir != 2) dir = 0; break; // 右
}
lastDir = dir;
}
}
void updateGame() {
// 移动身体
for(int i=snakeLength-1; i>0; i--){
snake[i] = snake[i-1];
}
// 移动头部
switch(dir) {
case 0: snake[0].x++; break; // 右
case 1: snake[0].y++; break; // 下
case 2: snake[0].x--; break; // 左
case 3: snake[0].y--; break; // 上
}
}
void checkCollisions() {
// 游戏区边界检测
if(snake[0].x < 0 || snake[0].x >= GRID_WIDTH ||
snake[0].y < 0 || snake[0].y >= GRID_HEIGHT) {
gameOver = true;
return;
}
// 自碰撞检测
for(uint8_t i=1; i<snakeLength; i++){
if(snake[0].x == snake[i].x && snake[0].y == snake[i].y){
gameOver = true;
return;
}
}
}
void checkFood() {
if(snake[0].x == food.x && snake[0].y == food.y){
if(snakeLength < SNAKE_MAX_LENGTH) snakeLength++;
score += 10;
generateFood();
}
}
void generateFood() {
bool valid;
do {
valid = true;
food.x = random(0, GRID_WIDTH);
food.y = random(0, GRID_HEIGHT);
for(uint8_t i=0; i<snakeLength; i++){
if(food.x == snake[i].x && food.y == snake[i].y){
valid = false;
break;
}
}
} while(!valid);
}
/*********************
* 显示功能 *
*********************/
void drawGame() {
display.clearDisplay();
// 绘制分数栏
display.fillRect(0, 0, SCREEN_WIDTH, SCORE_AREA_HEIGHT, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(SCREEN_WIDTH/2 - 24, 2);
display.print("SCORE: ");
display.print(score);
// 绘制游戏区边框
display.drawRect(0, OFFSET_Y-1, SCREEN_WIDTH, GAME_HEIGHT+1, SSD1306_WHITE);
// 绘制蛇身
for(uint8_t i=0; i<snakeLength; i++){
display.fillRect(
snake[i].x * CELL_SIZE,
OFFSET_Y + snake[i].y * CELL_SIZE,
CELL_SIZE-1,
CELL_SIZE-1,
SSD1306_WHITE
);
}
// 绘制闪烁食物
static bool blink = false;
blink = !blink;
if(blink){
display.drawRect(
food.x * CELL_SIZE,
OFFSET_Y + food.y * CELL_SIZE,
CELL_SIZE-1,
CELL_SIZE-1,
SSD1306_WHITE
);
}
display.display();
}
void showGameOver() {
display.clearDisplay();
// 绘制分数栏
display.fillRect(0, 0, SCREEN_WIDTH, SCORE_AREA_HEIGHT, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(SCREEN_WIDTH/2 - 24, 2);
display.print("SCORE: ");
display.print(score);
// 游戏结束信息
display.setTextColor(SSD1306_WHITE);
display.setCursor(SCREEN_WIDTH/2 - 30, OFFSET_Y + 10);
display.print("GAME OVER");
display.setCursor(SCREEN_WIDTH/2 - 20, OFFSET_Y + 25);
display.print("Final: ");
display.print(score);
display.setCursor(SCREEN_WIDTH/2 - 40, OFFSET_Y + 40);
display.print("PRESS ANY KEY");
display.display();
// 等待按键重启
if(customKeypad.getKey()){
delay(500);
initializeGame();
}
}