ATtiny85 迷你街机:贪吃蛇
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
必要的工具和机器
| ||||
|
应用和在线服务
| ||||
|
关于这个项目
灵感和过去的项目
2017 年 12 月,我创建了一个使用 Arduino Nano 和 OLED 屏幕以及两个按钮的手持式 Arduino Pong Console。
图>这在当时很好,但控制台有点太大和笨重。然而,最近我一直在尝试重新创建我过去的一些项目。这一次,我想做一个非常小的控制台,人们可以在上面玩蛇。
图>
选择的组件
为了使控制台变小,我无法使用任何 Arduino 开发板,这就是我使用 ATtiny85 的原因。
图>它具有足够的 I/O 来运行游戏,包括两个 ADC 引脚、I2C 端口和一个 GPIO 引脚。我选择使用一个简单的 2 轴操纵杆/开关模块,因为它易于连接,并且只需要 3 个引脚来发送信号。
图>最后,是时候决定需要哪种显示器了。由于DFRobot的128 x 64 OLED屏幕尺寸小但分辨率高,所以我选择了它。
图>
焊接系统
首先,我将 ATtiny85 连接到 SOP-8 转 DIP-8 分线 PCB 并将其焊接到一小块穿孔板上。接下来,我焊接了两个接头 - 一个用于编程,另一个用于屏幕。完成后,我将模拟操纵杆的引脚连接到性能板并相应地进行接线。对于最后一步,我将一个微型 USB 分线板连接到系统的其余部分以获取电源。
图> 图>
设计外壳
外壳是在 Fusion 360 中设计的。我首先创建并布置了我在实际构建中使用的组件,然后围绕它们创建了一个外壳。
图> 图>我想模仿 80 年代的街机盒子,但仍然保持很小的尺寸。以下是外壳的一些渲染:
图> 图>
游戏编程
Snake 是一个相当容易编程的游戏。我将蛇的最大长度设置为 30 以节省 RAM,这意味着一旦蛇吃掉了 29 像素,玩家就会获胜。为了跟踪蛇的片段,我创建了一个二维数组,用于存储每个片段的有序对。
图>每当头部移动到一个新位置时,它之前的位置就会向下级联。每次消耗一个段时,都会在随机点产生一个新段。碰撞检查是通过迭代每个段的坐标并查看头部坐标是否相同来完成的。此外,沿着任何墙壁击中蛇也会导致玩家失败。
图>
玩蛇
我首先打开控制台电源并等待屏幕加载食物和蛇的第一部分。然后我简单地通过在正确的方向移动操纵杆来控制蛇,同时看着它吃食物。这款游戏玩起来很有趣,是一款小巧的游戏消遣游戏。
图>代码
- ATTiny85 代码
ATTiny85 代码C/C++
确保先安装U8g2lib//#include#include U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g(U8G2_R0,/*clock=/2,/data=/0,/reset=*/N8XONE);#define MAX_LENGTH 30 //30 段 max#define X 0#define Y 1#define JOYSTICK_X 2#define JOYSTICK_Y 3#define DIR_THRESH 300 //值必须是 0-300 或 723-1023 到 countuint8_t segmentPosition[MAX_LENGTH] 2];uint8_t headPosition[2] ={63, 31}; //将蛇放在开始处 startuint8_t foodPosition[2]; //食物所在的位置uint8_t tempPosition0[2]; //存储上一段的位置以传递给nextuint8_t tempPosition1[2]; //存储前一段的位置以传递给nextint segmentLength =1;void gameUpdate();enum DIRECTIONS { RIGHT, DOWN, LEFT, UP} currentDirection;void setup() { //TinyWireM.begin(); u8g.begin(); u8g.setPowerSave(0); pinMode(JOYSTICK_X,输入); pinMode(JOYSTICK_Y,输入);随机种子(模拟读取(0)); beginGame();}void loop() { u8g.firstPage();做{游戏更新(); u8g.setColorIndex(1); } while(u8g.nextPage());}void beginGame(){ currentDirection =RIGHT;产生食物(); delay(1000);}bool checkCollisions(){ for(int i=1; i =128) return 1; else if(headPosition[Y] <=0 || headPosition[Y]>=64) 返回 1; return false;}void spawnFood(){ int randomX =random(5, 123); int randomY =random(5, 60); foodPosition[X] =randomX; foodPosition[Y] =randomY;}void checkFoodEaten(){ if(headPosition[X] ==foodPosition[X] || headPosition[Y] ==foodPosition[Y]){ segmentLength +=1;产生食物(); }}void updateDirection(){ int joy_x_val =analogRead(JOYSTICK_X); int joy_y_val =analogRead(JOYSTICK_Y); if(joy_x_val <=DIR_THRESH) currentDirection =LEFT;否则如果(joy_x_val>=1023-DIR_THRESH)currentDirection =RIGHT;否则 if(joy_y_val <=DIR_THRESH) currentDirection =UP; else if(joy_y_val>=1023-DIR_THRESH) currentDirection =DOWN;}void displaySegments(){ for(int segment=0; segment =MAX_LENGTH) endGame();延迟(50);}void endGame(){segmentLength =1;头位置[0] =63;头位置[1] =31; beginGame();}
定制零件和外壳
示意图
制造工艺