专业版
#第十届立创电赛#esp32游戏机

创建时间:1个月前

63 0

描述

<div> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">* 1、项目功能介绍</h3> <hr> <p style="line-height:1.8;"><span style="color:#95a5a6;font-size:14px;">基于ESP32的贪吃蛇游戏机,能够使用摇杆控制,能够穿过边界,当蛇头碰到蛇身游戏结束</span></p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">*2、项目属性</h3> <hr> <p style="line-height:1.8;"><span style="color:#95a5a6;font-size:14px;">原创项目,未在其他比赛中获奖</span></p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">* 3、开源协议</h3> <hr> <p style="line-height:1.8;"><span style="color:#95a5a6;font-size:14px;">GPL3.0</span></p> <p> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">*4、硬件部分</h3> <hr> <p style="line-height:1.8;"><span style="color:#95a5a6;font-size:14px;">使用ESP32开发板,摇杆,微动按键,蜂鸣器,预留IP5306充放电电路。</span></p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">*5、软件部分</h3> <hr> <div> <div>#include</div> <br> <div>// 定义WS2812B灯板参数</div> <div>#define LED_PIN     13</div> <div>#define LED_WIDTH   8</div> <div>#define LED_HEIGHT  8</div> <div>#define NUM_LEDS    (LED_WIDTH * LED_HEIGHT)</div> <br> <div>// 定义摇杆引脚</div> <div>#define JOYSTICK_X  34</div> <div>#define JOYSTICK_Y  35</div> <br> <div>// 摇杆阈值定义(更灵敏的阈值设置)</div> <div>#define THRESHOLD   200   // 降低阈值,提高灵敏度</div> <div>#define CENTER_MIN  512 - THRESHOLD</div> <div>#define CENTER_MAX  512 + THRESHOLD</div> <br> <div>// 创建NeoPixel对象</div> <div>Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);</div> <br> <div>// 游戏参数</div> <div>int snake[64][2];</div> <div>int snakeLength = 3;</div> <div>int food[2];</div> <div>int dir[2] = {1, 0};      // 当前方向</div> <div>int nextDir[2] = {1, 0};  // 下一个方向(确保转向后不回退)</div> <div>bool gameOver = false;</div> <div>unsigned long lastMoveTime = 0;</div> <div>int moveDelay = 300;</div> <div>unsigned long lastReadTime = 0;</div> <div>int readInterval = 50;    // 更高频率检测摇杆输入(50ms一次)</div> <br> <div>// 颜色定义</div> <div>uint32_t snakeColor = strip.Color(0, 255, 0);</div> <div>uint32_t foodColor = strip.Color(255, 0, 0);</div> <div>uint32_t bgColor = strip.Color(0, 0, 0);</div> <div>uint32_t gameOverColor = strip.Color(255, 0, 0);</div> <br> <div>void setup() {</div> <div>  strip.begin();</div> <div>  strip.show();</div> <div> </div> <div>  // 初始化蛇的位置</div> <div>  snake[0][0] = 3; snake[0][1] = 4;</div> <div>  snake[1][0] = 2; snake[1][1] = 4;</div> <div>  snake[2][0] = 1; snake[2][1] = 4;</div> <div> </div> <div>  generateFood();</div> <div>}</div> <br> <div>void loop() {</div> <div>  if (!gameOver) {</div> <div>    // 高频检测摇杆输入</div> <div>    if (millis() - lastReadTime > readInterval) {</div> <div>      readJoystick();</div> <div>      lastReadTime = millis();</div> <div>    }</div> <div>   </div> <div>    // 定时移动蛇</div> <div>    if (millis() - lastMoveTime > moveDelay) {</div> <div>      moveSnake();</div> <div>      checkCollision();</div> <div>      drawGame();</div> <div>      lastMoveTime = millis();</div> <div>    }</div> <div>  } else {</div> <div>    gameOverAnimation();</div> <div>    int x = analogRead(JOYSTICK_X);</div> <div>    int y = analogRead(JOYSTICK_Y);</div> <div>    if (x < CENTER_MIN || x > CENTER_MAX || y < CENTER_MIN || y > CENTER_MAX) {</div> <div>      restartGame();</div> <div>    }</div> <div>  }</div> <div>}</div> <br> <div>// 摇杆输入读取函数</div> <div>void readJoystick() {</div> <div>  int xValue = analogRead(JOYSTICK_X);</div> <div>  int yValue = analogRead(JOYSTICK_Y);</div> <div> </div> <div>  // 优先检测X轴方向(左右)</div> <div>  bool xMoved = false;</div> <div>  if (xValue < CENTER_MIN && dir[0] != 1) {  // 左</div> <div>    nextDir[0] = -1;</div> <div>    nextDir[1] = 0;</div> <div>    xMoved = true;</div> <div>  }</div> <div>  else if (xValue > CENTER_MAX && dir[0] != -1) {  // 右</div> <div>    nextDir[0] = 1;</div> <div>    nextDir[1] = 0;</div> <div>    xMoved = true;</div> <div>  }</div> <div> </div> <div>  // 检测Y轴方向(上下),如果X轴没有输入才处理Y轴</div> <div>  if (!xMoved) {</div> <div>    if (yValue < CENTER_MIN && dir[1] != 1) {  // 上</div> <div>      nextDir[0] = 0;</div> <div>      nextDir[1] = -1;</div> <div>    }</div> <div>    else if (yValue > CENTER_MAX && dir[1] != -1) {  // 下</div> <div>      nextDir[0] = 0;</div> <div>      nextDir[1] = 1;</div> <div>    }</div> <div>  }</div> <div>}</div> <br> <div>// 移动蛇</div> <div>void moveSnake() {</div> <div>  // 更新方向</div> <div>  dir[0] = nextDir[0];</div> <div>  dir[1] = nextDir[1];</div> <div> </div> <div>  // 移动身体</div> <div>  for (int i = snakeLength - 1; i > 0; i--) {</div> <div>    snake[i][0] = snake[i-1][0];</div> <div>    snake[i][1] = snake[i-1][1];</div> <div>  }</div> <div> </div> <div>  // 移动头部并处理穿墙逻辑</div> <div>  snake[0][0] += dir[0];</div> <div>  snake[0][1] += dir[1];</div> <div> </div> <div>  // 穿墙处理:当蛇头超出边界时,从对面出现</div> <div>  if (snake[0][0] < 0) {</div> <div>    snake[0][0] = LED_WIDTH - 1;  // 左边穿出,从右边进入</div> <div>  } else if (snake[0][0] >= LED_WIDTH) {</div> <div>    snake[0][0] = 0;  // 右边穿出,从左边进入</div> <div>  }</div> <div> </div> <div>  if (snake[0][1] < 0) {</div> <div>    snake[0][1] = LED_HEIGHT - 1;  // 上边穿出,从下边进入</div> <div>  } else if (snake[0][1] >= LED_HEIGHT) {</div> <div>    snake[0][1] = 0;  // 下边穿出,从上边进入</div> <div>  }</div> <div> </div> <div>  // 检查是否吃到食物</div> <div>  if (snake[0][0] == food[0] && snake[0][1] == food[1]) {</div> <div>    snakeLength++;</div> <div>    generateFood();</div> <div>    if (moveDelay > 100) {</div> <div>      moveDelay -= 10;</div> <div>    }</div> <div>  }</div> <div>}</div> <br> <div>// 检查碰撞(只检测自身碰撞,不检测墙壁碰撞)</div> <div>void checkCollision() {</div> <div>  // 只检查蛇头是否碰到自己的身体</div> <div>  for (int i = 1; i < snakeLength; i++) {</div> <div>    if (snake[0][0] == snake[i][0] && snake[0][1] == snake[i][1]) {</div> <div>      gameOver = true;</div> <div>      return;</div> <div>    }</div> <div>  }</div> <div>}</div> <br> <div>// 生成食物</div> <div>void generateFood() {</div> <div>  bool onSnake;</div> <div> </div> <div>  do {</div> <div>    onSnake = false;</div> <div>    food[0] = random(LED_WIDTH);</div> <div>    food[1] = random(LED_HEIGHT);</div> <div>   </div> <div>    for (int i = 0; i < snakeLength; i++) {</div> <div>      if (snake[i][0] == food[0] && snake[i][1] == food[1]) {</div> <div>        onSnake = true;</div> <div>        break;</div> <div>      }</div> <div>    }</div> <div>  } while (onSnake);</div> <div>}</div> <br> <div>// 绘制游戏</div> <div>void drawGame() {</div> <div>  for (int i = 0; i < NUM_LEDS; i++) {</div> <div>    strip.setPixelColor(i, bgColor);</div> <div>  }</div> <div> </div> <div>  for (int i = 0; i < snakeLength; i++) {</div> <div>    int pixelIndex = getPixelIndex(snake[i][0], snake[i][1]);</div> <div>    strip.setPixelColor(pixelIndex, snakeColor);</div> <div>  }</div> <div> </div> <div>  int foodIndex = getPixelIndex(food[0], food[1]);</div> <div>  strip.setPixelColor(foodIndex, foodColor);</div> <div> </div> <div>  strip.show();</div> <div>}</div> <br> <div>// 游戏结束动画</div> <div>void gameOverAnimation() {</div> <div>  static unsigned long lastBlink = 0;</div> <div>  static bool ledState = false;</div> <div> </div> <div>  if (millis() - lastBlink > 200) {</div> <div>    ledState = !ledState;</div> <div>    lastBlink = millis();</div> <div>   </div> <div>    for (int i = 0; i < NUM_LEDS; i++) {</div> <div>      strip.setPixelColor(i, ledState ? gameOverColor : bgColor);</div> <div>    }</div> <div>    strip.show();</div> <div>  }</div> <div>}</div> <br> <div>// 重新开始游戏</div> <div>void restartGame() {</div> <div>  snakeLength = 3;</div> <div>  dir[0] = 1; dir[1] = 0;</div> <div>  nextDir[0] = 1; nextDir[1] = 0;</div> <div>  gameOver = false;</div> <div>  moveDelay = 1000;</div> <div> </div> <div>  snake[0][0] = 3; snake[0][1] = 4;</div> <div>  snake[1][0] = 2; snake[1][1] = 4;</div> <div>  snake[2][0] = 1; snake[2][1] = 4;</div> <div> </div> <div>  generateFood();</div> <div>  drawGame();</div> <div> </div> <div>  delay(500);</div> <div>}</div> <br> <div>// 坐标转索引</div> <div>int getPixelIndex(int x, int y) {</div> <div>  return y * LED_WIDTH + x;</div> <div>}</div> </div> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">*6、BOM清单</h3> <hr> <p style="line-height:1.8;">由原理图导出</p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">*7、大赛LOGO验证</h3> <hr> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"><img src="https://image.lceda.cn/oshwhub/pullImage/d9a9466041544232978f96a726b17994.jpg" alt="" width="577" height="769"></p> <p style="line-height:1.8;"> </p> <h3 style="line-height:1.8;">* 8、演示您的项目并录制成视频上传</h3> <hr> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> <p style="line-height:1.8;"> </p> </div>

文档

BOM

暂无

附件

附件名 下载
tanchishe8.ino
46a34627dcc19ac75d7c1a87d6a12c0a.mp4

成员

评论(0)

  • 表情
    emoji
    小嘉工作篇
    小嘉日常篇
  • 图片
成功
工程所有者当前已关闭评论
goToTop
svg-battery svg-battery-wifi svg-books svg-more svg-paste svg-pencil svg-plant svg-ruler svg-share svg-user svg-logo-cn svg-double-arrow