ATtiny85 Mini Arcade:Snake
Componentes e suprimentos
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
Ferramentas e máquinas necessárias
| ||||
|
Aplicativos e serviços online
| ||||
|
Sobre este projeto
Inspiração e projeto anterior
Em dezembro de 2017, criei um console portátil Arduino Pong que usava um Arduino Nano e uma tela OLED, junto com dois botões.
Isso era bom na época, mas o console era um pouco grande e pesado. Recentemente, entretanto, tenho tentado recriar alguns de meus projetos anteriores. Desta vez, eu queria fazer um console bem pequeno no qual as pessoas pudessem jogar o Snake.
Os componentes escolhidos
Para tornar o console pequeno, não pude usar nenhuma placa de desenvolvimento Arduino, e é por isso que optei por um ATtiny85.
Possui I / O suficiente para rodar o jogo, incluindo dois pinos ADC e uma porta I2C e um pino GPIO. Optei por usar um joystick / módulo de chave simples de 2 eixos, pois é de fácil interface e só precisa de 3 pinos para sinalização.
Finalmente, chegou a hora de decidir qual tela era necessária. Como a tela OLED de 128 x 64 do DFRobot tem um tamanho pequeno, mas bastante resolução, optei por ela.
Soldando um sistema
Para começar, conectei um ATtiny85 a um SOP-8 para DIP-8 breakout PCB e o soldei a um pequeno pedaço de perf-board. Em seguida, soldei dois conectores - um para programação e outro para a tela. Depois de fazer isso, conectei os pinos do joystick analógico ao perf-board e conectei-o de acordo. Para a etapa final, conectei uma placa de breakout micro USB ao resto do sistema para energia.
Projetando um Gabinete
O gabinete foi projetado no Fusion 360. Comecei criando e organizando os componentes que usei na construção real e, em seguida, criei um gabinete ao redor deles.
Eu queria imitar uma caixa de arcade dos anos 80, mas ainda manter um tamanho muito pequeno. Abaixo estão alguns renderizadores do gabinete:
Programando o jogo
Snake é um jogo bastante fácil de programar. Eu defini um comprimento máximo da cobra de 30 para economizar na RAM, o que significa que, uma vez que a cobra tenha comido 29 pixels, o jogador ganha. Para acompanhar os segmentos da cobra, criei uma matriz bidimensional que armazena pares ordenados para cada segmento.
Sempre que a cabeça se move para um novo local, suas posições anteriores são cascateadas para baixo. Cada vez que um segmento é consumido, um novo é gerado em um ponto aleatório. A verificação de colisão é feita iterando através das coordenadas de cada segmento e ver se as coordenadas da cabeça são as mesmas. Além disso, acertar a cobra ao longo de qualquer uma das paredes fará com que o jogador perca também.
Jogando cobra
Comecei ligando o console e esperando a tela carregar na comida e no primeiro segmento da cobra. Então, simplesmente pilotei a cobra movendo o joystick na direção correta enquanto a observava comer a comida. Este jogo é divertido de jogar e é um grande assassino do tédio em um pacote minúsculo.
Código
- Código ATTiny85
Código ATTiny85 C / C ++
Certifique-se de instalar U8g2lib primeiro// # include#include U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g (U8G2_R0, / * clock =/ 2, / data =/ 0, / reset_PIN8X8); #define MAX_LENGTH 30 // 30 segmentos máx. # define X 0 # define Y 1 # define JOYSTICK_X 2 # define JOYSTICK_Y 3 # define DIR_THRESH 300 // Os valores devem ser 0-300 ou 723-1023 para countuint8_t segmentPositions [MAX_LENGTH] [ 2]; uint8_t headPosition [2] ={63, 31}; // Coloque a cobra no início para startuint8_t foodPosition [2]; // Onde o alimento está localizadouint8_t tempPosition0 [2]; // Armazena a posição do segmento anterior para passar para nextuint8_t tempPosition1 [2]; // Armazena a posição do segmento anterior para passar para o nextint segmentLength =1; void gameUpdate (); enum DIRECTIONS {RIGHT, DOWN, LEFT, UP} currentDirection; void setup () {//TinyWireM.begin (); u8g.begin (); u8g.setPowerSave (0); pinMode (JOYSTICK_X, INPUT); pinMode (JOYSTICK_Y, INPUT); randomSeed (analogRead (0)); beginGame ();} void loop () {u8g.firstPage (); faça {gameUpdate (); u8g.setColorIndex (1); } while (u8g.nextPage ());} void beginGame () {currentDirection =RIGHT; spawnFood (); delay (1000);} bool checkCollisions () {for (int i =1; i =128) return 1; else if (headPosition [Y] <=0 || headPosition [Y]> =64) retorna 1; return false;} void spawnFood () {int randomX =random (5, 123); int randomY =aleatório (5, 60); foodPosition [X] =randomX; foodPosition [Y] =randomY;} void checkFoodEaten () {if (headPosition [X] ==foodPosition [X] || headPosition [Y] ==foodPosition [Y]) {segmentLength + =1; spawnFood (); }} void updateDirection () {int joy_x_val =analogRead (JOYSTICK_X); int joy_y_val =analogRead (JOYSTICK_Y); if (joy_x_val <=DIR_THRESH) currentDirection =LEFT; else if (joy_x_val> =1023-DIR_THRESH) currentDirection =RIGHT; senão 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 (); atraso (50);} void endGame () {segmentLength =1; headPosition [0] =63; headPosition [1] =31; beginGame ();}
Peças personalizadas e gabinetes
Esquemas
Processo de manufatura