超详细!Arduino+0.96寸OLED屏+摇杆模块实现贪吃蛇游戏,附代码及优化建议
# Arduino贪吃蛇游戏代码(OLED 0.96寸 + 摇杆模块) 以下是完整的贪吃蛇游戏实现代码,包含详细 ```cpp #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // OLED屏幕设置 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 摇杆引脚定义 #define JOYSTICK_X A0 #define JOYSTICK_Y A1 #define JOYSTICK_BUTTON 2 // 游戏参数 #define GRID_SIZE 4 #define GRID_WIDTH (SCREEN_WIDTH / GRID_SIZE) #define GRID_HEIGHT (SCREEN_HEIGHT / GRID_SIZE) // 蛇结构体 struct Snake { int x[100]; int y[100]; int length; int direction; // 0:上, 1:右, 2:下, 3:左 }; // 食物结构体 struct Food { int x; int y; }; Snake snake; Food food; bool gameOver = false; unsigned long lastUpdateTime = 0; const int gameSpeed = 150; // 游戏速度(毫秒) void setup() { Serial.begin(9600); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;); } // 初始化摇杆按钮 pinMode(JOYSTICK_BUTTON, INPUT_PULLUP); // 初始化游戏 initGame(); // 显示开始画面 display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(20, 20); display.println("Snake Game"); display.setCursor(20, 40); display.println("Press to Start"); display.display(); // 等待按钮按下 while(digitalRead(JOYSTICK_BUTTON) { delay(10); } } void loop() { if(gameOver) { // 游戏结束显示 display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(30, 20); display.println("Game Over!"); display.setCursor(20, 40); display.print("Score: "); display.print(snake.length - 3); display.display(); // 按下按钮重新开始 if(!digitalRead(JOYSTICK_BUTTON)) { delay(200); // 防抖 initGame(); gameOver = false; } return; } // 读取摇杆输入 readJoystick(); // 游戏逻辑更新 if(millis() - lastUpdateTime > gameSpeed) { updateGame(); lastUpdateTime = millis(); } // 绘制游戏 drawGame(); } void initGame() { // 初始化蛇 snake.length = 3; snake.direction = 1; // 初始向右移动 // 蛇的初始位置 for(int i = 0; i < snake.length; i++) { snake.x[i] = 10 - i; snake.y[i] = 10; } // 生成第一个食物 generateFood(); } void readJoystick() { int xValue = analogRead(JOYSTICK_X); int yValue = analogRead(JOYSTICK_Y); // 确定方向(忽略中间值) if(xValue < 300 && abs(yValue - 512) < 200) { if(snake.direction != 3) snake.direction = 1; // 右 } else if(xValue > 700 && abs(yValue - 512) < 200) { if(snake.direction != 1) snake.direction = 3; // 左 } else if(yValue < 300 && abs(xValue - 512) < 200) { if(snake.direction != 2) snake.direction = 0; // 上 } else if(yValue > 700 && abs(xValue - 512) < 200) { if(snake.direction != 0) snake.direction = 2; // 下 } } void updateGame() { // 保存蛇尾位置(可能用于食物碰撞检测) int prevX = snake.x[snake.length-1]; int prevY = snake.y[snake.length-1]; // 移动蛇身(从尾部开始更新位置) for(int i = snake.length-1; i > 0; i--) { snake.x[i] = snake.x[i-1]; snake.y[i] = snake.y[i-1]; } // 根据方向移动蛇头 switch(snake.direction) { case 0: snake.y[0]--; break; // 上 case 1: snake.x[0]++; break; // 右 case 2: snake.y[0]++; break; // 下 case 3: snake.x[0]--; break; // 左 } // 边界检查 if(snake.x[0] < 0 || snake.x[0] >= GRID_WIDTH || snake.y[0] < 0 || snake.y[0] >= GRID_HEIGHT) { gameOver = true; return; } // 自碰撞检查 for(int i = 1; i < snake.length; i++) { if(snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) { gameOver = true; return; } } // 食物碰撞检测 if(snake.x[0] == food.x && snake.y[0] == food.y) { // 增加蛇长度 snake.length++; snake.x[snake.length-1] = prevX; snake.y[snake.length-1] = prevY; // 生成新食物 generateFood(); } } void generateFood() { bool validPosition = false; while(!validPosition) { validPosition = true; // 随机生成食物位置 food.x = random(0, GRID_WIDTH); food.y = random(0, GRID_HEIGHT); // 确保食物不在蛇身上 for(int i = 0; i < snake.length; i++) { if(food.x == snake.x[i] && food.y == snake.y[i]) { validPosition = false; break; } } } } void drawGame() { display.clearDisplay(); // 绘制蛇 for(int i = 0; i < snake.length; i++) { display.fillRect( snake.x[i] * GRID_SIZE, snake.y[i] * GRID_SIZE, GRID_SIZE, GRID_SIZE, WHITE ); } // 绘制食物 display.fillRect( food.x * GRID_SIZE, food.y * GRID_SIZE, GRID_SIZE, GRID_SIZE, WHITE ); // 绘制分数 display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.print("Score: "); display.print(snake.length - 3); display.display(); } ``` ## 额外需要注意的逻辑点 1. 摇杆输入的防抖处理:代码中通过判断摇杆模拟值的范围来确定方向,但没有加入延时防抖,可能导致偶尔的误操作。 2. 食物生成算法:当前使用随机位置+验证的方式,在蛇身较长时可能导致生成时间变长,可以考虑优化为更高效的算法。 3. 游戏速度固定:代码中使用固定的150毫秒更新间隔,可以考虑随着分数增加而逐渐提高游戏难度(减少间隔时间)。 ######[AI写代码神器 | 1825点数解答 | 2025-04-11 15:00:05]
- 超燃年会来袭!诗舞歌戏轮番上阵,共赴欢乐盛宴! (字节豆包 | 206点数解答 | 2025-01-08 10:59:43)358
- 缜密逻辑,全新无名杀“风餐”技能代码精心打造与深度解析 (字节豆包 | 469点数解答 | 2025-04-05 11:37:06)265
- STM32F10x代码解析:多模块引用与变量定义及功能探讨(字节豆包 | 25点数解答 | 2025-05-06 01:13:43)200
- Keil编译报错:找不到core_cm3.h文件的原因及解决办法(字节豆包 | 666点数解答 | 2025-11-12 18:26:21)240
- Keil MDK编译报错“无法打开 core_cm3.h”,解决办法及注意事项全揭秘!(字节豆包 | 560点数解答 | 2025-11-12 18:31:47)93
- 数列筛选:统计并求和大于 x 小于 y 的整数,这些开发要点要留意!(字节豆包 | 281点数解答 | 2025-11-29 20:20:58)148
- 用C++实现黄金城演讲比赛评分计算及有效分数输出程序(字节豆包 | 250点数解答 | 2026-02-28 20:18:01)21
- 黄金城演讲比赛评分程序的 C++ 实现(字节豆包 | 548点数解答 | 2026-02-28 20:20:14)23
- 黄金城演讲比赛评分程序的 C++ 实现(字节豆包 | 638点数解答 | 2026-02-28 20:25:32)23
- 黄金城演讲比赛评分程序的 C++ 实现(字节豆包 | 631点数解答 | 2026-02-28 20:28:21)24
- 黄金城演讲比赛评分程序的 C++ 实现及开发要点(字节豆包 | 703点数解答 | 2026-02-28 22:22:05)29
- 黄金城演讲比赛评分程序的 C++ 实现(字节豆包 | 374点数解答 | 2026-04-10 19:34:49)10