基于Arduino的贪吃蛇游戏开发:核心逻辑实现
本文是”基于Arduino的贪吃蛇游戏开发”系列的第二篇,将深入探讨游戏的核心逻辑实现。在上一篇中,我们完成了硬件搭建和基础显示功能,现在让我们让这个游戏真正”活”起来!
游戏核心架构 贪吃蛇游戏的核心逻辑可以分为四个主要部分:
蛇的移动与转向控制
食物生成与碰撞检测
游戏状态管理
用户输入处理
让我们逐一分析这些关键组件在代码中的实现。
1. 蛇的移动与转向控制 方向表示与移动逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 volatile int direction = 1 ;volatile int old_direction = 1 ;void snack_next () { if (direction == 2 ) snack_head[0 ]++; else if (direction == 4 ) snack_head[0 ]--; else if (direction == 1 ) snack_head[1 ]++; else if (direction == 3 ) snack_head[1 ]--; if (snack_head[0 ] == food[0 ] && snack_head[1 ] == food[1 ]) { score++; snack_body[score - 1 ][0 ] = snack_head[0 ]; snack_body[score - 1 ][1 ] = snack_head[1 ]; food_summon (); } else { for (int i = 0 ; i < score - 1 ; i++) { snack_body[i][0 ] = snack_body[i + 1 ][0 ]; snack_body[i][1 ] = snack_body[i + 1 ][1 ]; } snack_body[score - 1 ][0 ] = snack_head[0 ]; snack_body[score - 1 ][1 ] = snack_head[1 ]; } }
这段代码实现了贪吃蛇的核心移动逻辑:
方向控制 :使用简单的整数表示方向(1-右,2-下,3-左,4-上)
吃到食物 :当蛇头与食物位置重合时,蛇身长度增加
正常移动 :蛇身向前移动一节,尾部消失,头部新增一节
转向限制机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void control () { while (millis () - time_ < 500 ) { if (digitalRead (3 )) { if (button_ != 1 && s3) { if (old_direction == 1 ) { direction = 4 ; } else { direction = old_direction - 1 ; } } button_ = 1 ; } else if (digitalRead (2 )) { if (button_ != -1 && s2) { if (old_direction == 4 ) { direction = 1 ; } else { direction = old_direction + 1 ; } } button_ = -1 ; } } }
这段代码实现了两个重要功能:
500ms时间窗口 :限制玩家在500ms内只能进行一次转向操作
方向限制 :防止蛇进行180度直接转向(如从右直接转左)
2. 食物生成与碰撞检测 智能食物生成算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void food_summon () { int x, y; bool m; do { m = false ; x = random (1 , w); y = random (1 , h); if (x == snack_head[1 ] && y == snack_head[0 ]) { m = true ; continue ; } for (int i = 0 ; i < score - 1 ; i++) { if (x == snack_body[i][1 ] && y == snack_body[i][0 ]) { m = true ; break ; } } } while (m); food[1 ] = x; food[0 ] = y; }
这个算法确保食物不会出现在蛇身或蛇头的位置:
使用do-while
循环代替递归,避免栈溢出风险
随机生成位置后检查是否与蛇的任何部分重叠
如果重叠则重新生成,直到找到有效位置
碰撞检测系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void snack_next () { for (int i = 0 ; i < score - 1 ; i++) { if (snack_head[0 ] == snack_body[i][0 ] && snack_head[1 ] == snack_body[i][1 ]) { game_over = true ; } } if (snack_head[1 ] == 0 || snack_head[0 ] == 0 || snack_head[1 ] == w + 1 || snack_head[0 ] == h + 1 ) { game_over = true ; } }
碰撞检测分为两部分:
自碰撞 :遍历蛇身,检查蛇头是否与任何身体部分重合
边界碰撞 :检查蛇头是否超出游戏区域边界(1-8)
3. 游戏状态管理 游戏结束处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void loop () { if (game_over) { for (int i = 0 ; i < 5 ; i++) { P.displayClear (); mx.clear (); delay (250 ); show (); mx.setBuffer (15 , 8 , game_over_bitmap); delay (250 ); } while (true ) { delay (250 ); if (digitalRead (3 ) || digitalRead (2 )) { reset (); break ; } } } }
游戏结束流程:
显示闪烁动画(交替显示当前状态和游戏结束图标)
进入等待循环,检测按钮输入
当玩家按下任意按钮时,重置游戏
游戏重置功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void reset () { for (int i = 0 ; i < 64 ; i++) { snack_body[i][0 ] = 0 ; snack_body[i][1 ] = 0 ; } food[0 ] = 4 ; food[1 ] = 6 ; snack_head[0 ] = 4 ; snack_head[1 ] = 3 ; score = 3 ; direction = 1 ; old_direction = 1 ; game_over = false ; snack_body[0 ][0 ] = 4 ; snack_body[0 ][1 ] = 1 ; snack_body[1 ][0 ] = 4 ; snack_body[1 ][1 ] = 2 ; snack_body[2 ][0 ] = 4 ; snack_body[2 ][1 ] = 3 ; }
重置功能将所有游戏状态恢复为初始值,确保玩家可以重新开始游戏。
4. 用户输入处理 按钮检测机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void control () { time_ = millis (); button_ = 0 ; old_direction = direction; bool s3 = false , s2 = false ; while (millis () - time_ < 500 ) { if (!digitalRead (3 )) s3 = true ; if (!digitalRead (2 )) s2 = true ; if (digitalRead (3 )) { if (button_ != 1 && s3) { } button_ = 1 ; } else if (digitalRead (2 )) { if (button_ != -1 && s2) { } button_ = -1 ; } } }
这段代码实现了一个巧妙的按钮检测机制:
按下检测 :当按钮被按下时设置标志(s2/s3)
释放检测 :当按钮被释放时执行转向操作
状态锁定 :使用button_变量防止同一按钮多次触发
游戏循环流程 整个游戏的主循环流程如下:
优化与改进建议 虽然当前实现已经相当完整,但仍有优化空间:
使用枚举提升可读性 :
1 2 enum Direction { RIGHT = 1 , DOWN, LEFT, UP };volatile Direction direction = RIGHT;
增加游戏难度 :
1 2 int speed = max (100 , 500 - score * 20 ); while (millis () - time_ < speed) { ... }
添加音效反馈 :
1 2 3 4 5 void playTone (int freq, int duration) { tone (speakerPin, freq, duration); } playTone (523 , 150 );
总结与下期预告 在本章中,我们深入探讨了贪吃蛇游戏的核心逻辑实现,包括:
蛇的移动与转向控制
食物生成与碰撞检测
游戏状态管理与结束处理
用户输入处理机制
这些核心组件共同构成了一个完整可玩的贪吃蛇游戏。在下一篇中,我们将:
优化游戏性能与显示效果
添加游戏难度分级
实现高分记录功能
解决现有实现中的边界情况问题
项目源码更新 :本文涉及的优化代码已更新到GitHub仓库 Arduino-Snake-Game
挑战任务 :尝试添加游戏暂停功能,当玩家同时按下两个按钮时暂停游戏,再次按下时继续。这将帮助你深入理解游戏状态管理!