电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
用ATtiny85单片机制作迷你游戏机
分 享
扫描二维码分享
用ATtiny85单片机制作迷你游戏机
单片机
DIY
游戏机
☻
关注
发布时间: 2020-06-03
丨
阅读: 4895
大家好,又是我!每天上午只要有空就做一个有趣的小项目,并在下午通过电子芯吧客分享给大家,欢迎围观喔! ------------ 今天这个,说有趣也有趣,说无聊也无聊,可能更适合给小朋友当玩具。。。。 它就是一个用ATtiny85制作的迷你游戏机。制作起来并不是很复杂,比较适合新手。 # 材料 ![](https://cf03.ickimg.com/bbsimages/202006/769883d11a34f7b59815bec0708751eb.jpg) # 电路与芯片 ![](https://cf03.ickimg.com/bbsimages/202006/8b6236adbbda34ed799b0da9907a64ac.png) 芯片引脚功能: ![](https://cf03.ickimg.com/bbsimages/202006/7f1fe7455e678519a77fd2901d731038.png) ATtiny85是一种与Arduino类似的微控制器,但是IO引脚少得多,内存更小,尺寸更小。它功耗低,所以3V纽扣电池就足够 # 按图施工 ![](https://cf03.ickimg.com/bbsimages/202006/ced184100645ed4dcf05711f861e5c87.png) 不要吐槽飞线: ![](https://cf03.ickimg.com/bbsimages/202006/563726913cb33c1a852095a1cf75fe89.png) 玩游戏时蜂鸣片会发出声音。由于位置所限,我将其放置在OLED显示器下方。最好让蜂鸣器和OLED之间保持一定的距离,以免声音过小: ![](https://cf03.ickimg.com/bbsimages/202006/b35a699f3477d27e912a8d61d666a785.png) 焊屏幕和电池座 ![](https://cf03.ickimg.com/bbsimages/202006/7c77e020d8df37be1d2706d4aac6d5a5.png) ![](https://cf03.ickimg.com/bbsimages/202006/b4bf6054eb11a55ff6acc7f34685e4b3.png) ![](https://cf03.ickimg.com/bbsimages/202006/03faa4a7f97fcbf0bd4d331ae758e601.png) **实测一个纽扣电池可以玩一个小时**,续航也不是特别长。 **游戏可以在这里下载**[GitHub](https://github.com/andyhighnumber/Attiny-Arduino-Games "GitHub") 提供一个示例代码给大家: (有点长欸,芯吧客没有代码框滚动功能 将就点吧) ```cpp /@@* 2015 / 2016 / 2017 * Frogger game by Andy Jackson - Twitter @andyhighnumber * * Special thanks to @senkunmusahi, who created the artwork bitmaps in the game using https://www.riyas.org/2013/12/online-led-matrix-f... * * Inspired by https://www.riyas.org/2013/12/online-led-matrix-f... and includes some code from the #AttinyArcade games on that site * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution. * This software is supplied without warranty of any kind. * * Controls: * On the standard AttinyArcade: * LEFT and RIGHT buttons move the frog across * BOTH BOTTONS TOGETHER move the frog forwards * * HIGHLY RECOMMENDED: * On custom hardware (see schematic in folder where you found this file) there is an additional button to move frog forward * * Also, from standby.... * Press and hold left button to turn sound on and off * Press and hold left button with the right button held to reset high score * * This sketch is using the screen control and font functions written by Neven Boyanov for the https://www.riyas.org/2013/12/online-led-matrix-f... project * Source code and font files available at: https://www.riyas.org/2013/12/online-led-matrix-f... * **Note that this highly size-optimised version requires modified library functions (which are in this source code file) * and a modified font header * * Sleep code is based on this blog post by Matthew Little: * https://www.riyas.org/2013/12/online-led-matrix-f... */ #include
#include "font6x8AJ2.h" #include
#include
#include
// needed for the additional interrupt // Uncomment this #define to make the logs smaller (/thinner) //#define SMALLLOGS // Make click delay an even number - it gets halved and then used in an integer comparison #define CLICKDELAY 120 // The basline speed - higher number is slower #define MOVEBASE 1000 #define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT) #define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT) // Routines to set and clear bits (used in the sleep code) #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // Defines for OLED output #define SSD1306XLED_H #define SSD1306_SCL PORTB4 // SCL, Pin 4 on SSD1306 Board - for webbogles board #define SSD1306_SDA PORTB3 // SDA, Pin 3 on SSD1306 Board - for webbogles board #define SSD1306_SA 0x78 // Slave address // Function prototypes // Drawing functions - adapted from those at https://www.riyas.org/2013/12/online-led-matrix-f... void ssd1306_init(void); void ssd1306_xfer_start(void); void ssd1306_xfer_stop(void); void ssd1306_send_byte(uint8_t byte); void ssd1306_send_command(uint8_t command); void ssd1306_send_data_start(void); void ssd1306_send_data_stop(void); void ssd1306_setpos(uint8_t x, uint8_t y); void ssd1306_fillscreen(uint8_t fill_Data); void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]); void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]); // Custom draw functions - allow for extra functionality like inverse display void sendBlock(byte, bool); void sendByte(byte, bool); // Other generic functions for games (both originated in code from webboggles.com and the sleep code is by Matthew Little - see above) void beep(int,int); void system_sleep(void); void doNumber (int,int,int); // Game functions void playFrogger(void); void levelUp(int); void moveBlocks(void); void initScreen(void); void drawDocks(void); void drawLives(void); void displayTitle(void); void resetDock(byte); void checkCollision(void); // Global variables - yes I know all these global vars is a lazy way to code but it makes it easier to prevent stack overflows when you're working with 512 bytes! // Most of these are initialised in the main game function (playFrogger()) int watchDog; // Counts drawing cycles so I can shut the game down if there's inactivity - battery saver! boolean stopAnimate; // this is set to 1 when a collision is detected int lives; // Lives in the game - this can go negative to end the game, which is why it's a signed variable bool frogDocks[5]; // Tracks which frog docks are full (at the top of the screen) bool flipFlop; // Used in routines that flip-flop between two states (left and right) bool flipFlopShift; // Same as previous one byte frogColumn; // Column location of frog (there are 16 altogether) byte frogRow; // Row locaiton of frog (there are 8, but 0 is the frog docks at the top and 7 is the start row) byte frogLeftLimit; // Left limit of frog travel on start row (changes as digits in score increases) byte frogRightLimit; // Right limit of frog travel on start row (changes as lives decrease as there's then more space) byte level; // Level - starts at 1 byte blockShiftL; // Number of pixels to shift the left-going rows by byte blockShiftR; // Number of pixels to shift the right-going rows by int interimStep; // Used as timer for incremental movements int moveDelay; // How long to wait until the next movement of logs etc - changes as levels increase to make the game go faster int dockedFrogs; // How many frogs are in the docks at the top unsigned long clickBase; // Timer for debounce boolean clickLock; // For debounce routine int score; // Obvious I hope int topScore; // High score boolean newHigh; // Is there a new high score? boolean mute = 0; // Mute the speaker byte grid[6][16]; // Grid for items like logs, crocs, cars and lorries byte frogMode; // Represents the frog direction bool moveForward=0; // Captures when the 'forward' button is pressed bool moveLeft=0; // Captures when the 'left' button is pressed bool moveRight=0; // Captures when the 'right' button is pressed // Bitmaps created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-f... static const byte bitmaps[15][8] PROGMEM = { // Frogs {0x83, 0xDC, 0x7A, 0x3F, 0x3F, 0x7A, 0xDC, 0x83}, {0x99, 0xBD, 0xDB, 0x7E, 0x7E, 0x3C, 0xE7, 0x81}, {0x81, 0xE7, 0x3C, 0x7E, 0x7E, 0xDB, 0xBD, 0x99}, #ifdef SMALLLOGS // Small logs {0x1C, 0x22, 0x41, 0x55, 0x55, 0x51, 0x43, 0x61}, {0x69, 0x6B, 0x43, 0x61, 0x45, 0x45, 0x61, 0x65}, {0x45, 0x55, 0x41, 0x5D, 0x63, 0x5D, 0x22, 0x1C}, #else // Bigger logs {0x3C, 0x7E, 0xD7, 0xB5, 0xAD, 0xBF, 0xFF, 0xED}, {0xAD, 0xAD, 0xFF, 0xB7, 0xF5, 0xBF, 0xB7, 0xAD}, {0xED, 0xBD, 0xC3, 0xBD, 0xA5, 0xBD, 0x42, 0x3C}, #endif // Trucks {0x00, 0x7F, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55}, {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, {0x41, 0x7F, 0x22, 0x7F, 0x7F, 0x63, 0x22, 0x1C}, // Crocs {0x41, 0x63, 0x46, 0x6E, 0x7C, 0x7E, 0x7A, 0x3E}, {0xBC, 0xFE, 0x7E, 0x3E, 0xBE, 0xBE, 0xFC, 0x7C}, {0x78, 0x38, 0x38, 0x38, 0x70, 0x60, 0x60, 0x40}, // Cars {0x00, 0x1C, 0x22, 0x63, 0x7F, 0x7F, 0x22, 0x22}, {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C}, {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C} }; // Opening artwork created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-f... static const byte titleBmp[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0x7C, 0x06, 0x73, 0x59, 0x43, 0x06, 0x3C, 0x38, 0x30, 0x30, 0x38, 0x3E, 0x26, 0x7B, 0x59, 0x43, 0x06, 0x7C, 0xF0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFF, 0xCF, 0x01, 0x00, 0x00, 0x30, 0x60, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x01, 0xCF, 0xFE, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFE, 0x86, 0x0E, 0x0E, 0x1C, 0x18, 0x31, 0x7F, 0xFE, 0xFC, 0x1C, 0x18, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x18, 0x1C, 0xFC, 0xFE, 0x7F, 0x39, 0x18, 0x1C, 0x0E, 0x0E, 0xC6, 0xFE, 0x3C, 0x00, 0x01, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xC0, 0xC0, 0x80, 0x03, 0x07, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xF0, 0x00, 0x00, 0xF8, 0xFC, 0x0F, 0x03, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x03, 0x01, 0x00, 0x04, 0x06, 0x0F, 0x0F, 0x06, 0x06, 0x03, 0x03, 0x03, 0x63, 0x73, 0x33, 0x3B, 0xFF, 0xFF, 0x7F, 0x3F, 0x38, 0xF0, 0xC0, 0x00, 0xF0, 0xF8, 0x3F, 0x7F, 0xFF, 0xFF, 0x3B, 0x33, 0x63, 0x63, 0x03, 0x03, 0x03, 0x06, 0x06, 0x0F, 0x0F, 0x06, 0x00, }; // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt if (clickLock == 0) { moveLeft = 1; clickLock = 1; clickBase = millis(); } } void playerIncFrogger(){ // PB2 pin button interrupt if (clickLock == 0) { moveRight = 1; clickLock = 1; clickBase = millis(); } } void displayTitle(void) { int incr = 0; for(int lxn = 2; lxn < 7; lxn++) { ssd1306_setpos(85,lxn); ssd1306_send_data_start(); for(int lxn2 = 0; lxn2 < 40; lxn2++) { ssd1306_send_byte(pgm_read_byte(&titleBmp[incr])); incr++; } ssd1306_send_data_stop(); } } // Arduino stuff - setup void setup() { DDRB = 0b00000010; // set PB1 as output (for the speaker) PCMSK = 0b00000001; // pin change mask: listen to portb bit 1 GIMSK |= 0b00100000; // enable PCINT interrupt sei(); // enable all interrupts } // Arduino stuff - loop void loop() { ssd1306_init(); ssd1306_fillscreen(0x00); // The lower case character set is seriously compromised because I've had to truncate the ASCII table // to release space for executable code. // There is no z in the table as this isn't used anywhere in the text here and most of the // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail) ssd1306_char_f6x8(0, 2, "F R O G G E R"); ssd1306_char_f6x8(0, 4, "andy jackson"); // see comments above ! ssd1306_setpos(0,1); for (int incr = 0; incr < 80; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00111000); ssd1306_send_data_stop(); } ssd1306_setpos(0,3); for (int incr = 0; incr < 80; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00011100); ssd1306_send_data_stop(); } displayTitle(); ssd1306_char_f6x8(0, 6, "inspired by"); ssd1306_char_f6x8(0, 7, "webboggles.com"); delay(1500); ssd1306_char_f6x8(0, 6, "artwork by "); ssd1306_char_f6x8(0, 7, "zsenkunmusashi"); // see comments above - f has been replaced by @ in the ASCII table long startT = millis(); long nowT =0; boolean sChange = 0; while(digitalRead(0) == HIGH) { nowT = millis(); if (nowT - startT > 2000) { sChange = 1; if (digitalRead(2) == HIGH) { EEPROM.write(0,0); EEPROM.write(1,0); ssd1306_char_f6x8(8, 0, "-HIGH SCORE RESET-"); } else if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(31, 0, "- SOUND ON -"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(2000); ssd1306_init(); ssd1306_fillscreen(0x00); playFrogger(); topScore = EEPROM.read(0); topScore = topScore << 8; topScore = topScore | EEPROM.read(1); newHigh = 0; if (score > topScore) { topScore = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } ssd1306_fillscreen(0x00); ssd1306_char_f6x8(11, 1, "----------------"); ssd1306_char_f6x8(11, 2, "G A M E O V E R"); ssd1306_char_f6x8(11, 3, "----------------"); ssd1306_char_f6x8(37, 5, "SCORE:"); doNumber(75, 5, score); if (!newHigh) { ssd1306_char_f6x8(21, 7, "HIGH SCORE:"); doNumber(88, 7, topScore); } delay(1000); if (newHigh) { ssd1306_fillscreen(0x00); ssd1306_char_f6x8(10, 1, "----------------"); ssd1306_char_f6x8(10, 3, " NEW HIGH SCORE "); ssd1306_char_f6x8(10, 7, "----------------"); doNumber(50,5,topScore); for (int i = 700; i>200; i = i - 50){ beep(30,i); } delay(1200); } } system_sleep(); } void doNumber (int x, int y, int value) { char temp[10] = {0,0,0,0,0,0,0,0,0,0}; itoa(value,temp,10); ssd1306_char_f6x8(x, y, temp); } void ssd1306_init(void){ DDRB |= (1 << SSD1306_SDA); // Set port as output DDRB |= (1 << SSD1306_SCL); // Set port as output ssd1306_send_command(0xAE); // display off ssd1306_send_command(0x00); // Set Memory Addressing Mode ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7 ssd1306_send_command(0x81); // Set COM Output Scan Direction ssd1306_send_command(0xCF); // ---set low rowumn address ssd1306_send_command(0xA1); // ---set high rowumn address ssd1306_send_command(0xC8); // --set start line address ssd1306_send_command(0xA6); // --set contrast control register ssd1306_send_command(0xA8); ssd1306_send_command(0x3F); // --set segment re-map 0 to 127 ssd1306_send_command(0xD3); // --set normal display ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64) ssd1306_send_command(0xD5); // ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content ssd1306_send_command(0xD9); // -set display offset ssd1306_send_command(0xF1); // -not offset ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency ssd1306_send_command(0x12); // --set divide ratio ssd1306_send_command(0xDB); // --set pre-charge period ssd1306_send_command(0x40); // ssd1306_send_command(0x20); // --set com pins hardware configuration ssd1306_send_command(0x02); ssd1306_send_command(0x8D); // --set vcomh ssd1306_send_command(0x14); // 0x20,0.77xVcc ssd1306_send_command(0xA4); // --set DC-DC enable ssd1306_send_command(0xA6); // ssd1306_send_command(0xAF); // --turn on oled panel } void ssd1306_xfer_start(void){ DIGITAL_WRITE_HIGH(SSD1306_SCL); // Set to HIGH DIGITAL_WRITE_HIGH(SSD1306_SDA); // Set to HIGH DIGITAL_WRITE_LOW(SSD1306_SDA); // Set to LOW DIGITAL_WRITE_LOW(SSD1306_SCL); // Set to LOW } void ssd1306_xfer_stop(void){ DIGITAL_WRITE_LOW(SSD1306_SCL); // Set to LOW DIGITAL_WRITE_LOW(SSD1306_SDA); // Set to LOW DIGITAL_WRITE_HIGH(SSD1306_SCL); // Set to HIGH DIGITAL_WRITE_HIGH(SSD1306_SDA); // Set to HIGH } void ssd1306_send_byte(uint8_t byte){ uint8_t i; for(i=0; i<8; i++) { if((byte << i) & 0x80) DIGITAL_WRITE_HIGH(SSD1306_SDA); else DIGITAL_WRITE_LOW(SSD1306_SDA); DIGITAL_WRITE_HIGH(SSD1306_SCL); DIGITAL_WRITE_LOW(SSD1306_SCL); } DIGITAL_WRITE_HIGH(SSD1306_SDA); DIGITAL_WRITE_HIGH(SSD1306_SCL); DIGITAL_WRITE_LOW(SSD1306_SCL); } void ssd1306_send_command(uint8_t command){ ssd1306_xfer_start(); ssd1306_send_byte(SSD1306_SA); // Slave address, SA0=0 ssd1306_send_byte(0x00); // write command ssd1306_send_byte(command); ssd1306_xfer_stop(); } void ssd1306_send_data_start(void){ ssd1306_xfer_start(); ssd1306_send_byte(SSD1306_SA); ssd1306_send_byte(0x40); //write data } void ssd1306_send_data_stop(void){ ssd1306_xfer_stop(); } void ssd1306_setpos(uint8_t x, uint8_t y) { if (y>7) return; ssd1306_xfer_start(); ssd1306_send_byte(SSD1306_SA); //Slave address,SA0=0 ssd1306_send_byte(0x00); //write command ssd1306_send_byte(0xb0+y); ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10 ssd1306_send_byte((x&0x0f)|0x01); // |0x01 ssd1306_xfer_stop(); } void ssd1306_fillscreen(uint8_t fill_Data){ uint8_t m,n; for(m=0;m<8;m++) { ssd1306_send_command(0xb0+m); //page0-page1 ssd1306_send_command(0x00); //low rowumn start address ssd1306_send_command(0x10); //high rowumn start address ssd1306_send_data_start(); for(n=0;n<128;n++) { ssd1306_send_byte(fill_Data); } ssd1306_send_data_stop(); } } void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){ uint8_t c,i,j=0; while(ch[j] != '\0') { c = ch[j] - 32; if (c >0) c = c - 12; if (c >15) c = c - 6; if (c>40) c=c-9; if(x>126) { x=0; y++; } ssd1306_setpos(x,y); ssd1306_send_data_start(); for(i=0;i<6;i++) { ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i])); } ssd1306_send_data_stop(); x += 6; j++; } } void system_sleep(void) { ssd1306_fillscreen(0x00); ssd1306_send_command(0xAE); cbi(ADCSRA,ADEN); // switch analog to digital converter off set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here sleep_enable(); sleep_mode(); // system actually sleeps here sleep_disable(); // system continues execution here when watchdog timed out sbi(ADCSRA,ADEN); // switch analog to digital converter on ssd1306_send_command(0xAF); } void beep(int bCount,int bDelay){ if (mute) return; for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2
= 0) { interimStep++; if (watchDog >= 500) lives = -1; // Stop the game if nothing's happening - maybe triggered in someone's pocket so this is to save battery! // Calculate left limit of frog movement so it doesn't hit the score frogLeftLimit = 1; if ((score / 10) % 10 != 0) frogLeftLimit++; if ((score / 100) % 10 != 0) frogLeftLimit++; if ((score / 1000) % 10 != 0) frogLeftLimit++; // Move stuff along if it's time to if (interimStep > moveDelay/8) { watchDog++; blockShiftL++; if (flipFlopShift == 1) flipFlopShift = 0; else flipFlopShift = 1; if (flipFlopShift == 1) blockShiftR++; if (blockShiftL == 7) { moveBlocks(); blockShiftL = 0; } if (blockShiftR == 7) { blockShiftR = 0; } interimStep = 0; checkCollision(); if (stopAnimate == 0) { drawGameScreen(frogMode); drawFrog(frogMode,0); } } // Handle input from 'jump' button (the other two buttons are captured in the interrupt routines) if (analogRead(0) < 940 && clickLock == 0) { moveForward = 1; watchDog = 0; // reset the watchdog so the game doesn't end! clickLock = 1; clickBase = millis(); } // Handle moving left if(moveLeft == 1 && millis() > clickBase + CLICKDELAY/2) { watchDog = 0; // reset the watchdog so the game doesn't end! moveLeft = 0; if (digitalRead(2) == HIGH) moveForward = 1; else { drawFrog(0,0); // de
lete the frog // move the frog, checking it isn't jumping off the edge of the screen if ((frogRow == 7 && frogColumn > frogLeftLimit) || (frogRow < 7 && frogColumn > 0)) { frogColumn --; } else if (frogRow < 7) stopAnimate = 1; frogMode = 2; // pointing left } } // Handle moving right if(moveRight == 1 && millis() > clickBase + CLICKDELAY/2){ watchDog = 0; // reset the watchdog so the game doesn't end! moveRight = 0; if (digitalRead(0) == HIGH) moveForward = 1; else { drawFrog(0,0); // he bitmaps } else if (col >= crocStartColumn) { grid[row][col] = 4+stepMode+stepShift; // if you're on row zero (top row of logs) and you are above where crocs should be drawm, draw logs ... } else grid[row][col] = 10+stepMode; // .. otherwise draw crocs if (stepMode == 0) stepMode = 1; // we've drawn the left side now switch to central sections if (stepMode == 2) stepMode = 0; // we've drawn the end, now reset } } counter[row]--; // decrement the counter if (counter[row] <= gapCounter[row]) { counter[row] = initCounter[row]; // if we have gone negative enough to account for the gaps - reset the counter and start again } } } } // Display the frog void drawFrog(byte mode, bool frogDead) { if (frogRow > 6 || frogRow < 1 || frogDead == 1) { // don't draw the frog when it's on the road or on logs - because they are moving, that's handled in the main drawing routine below- exception is when you are animating frog death if (frogRow == 1 || frogRow == 3) { // these allow for the blocks being shifted when animating the frog death on rows with logs ssd1306_setpos(frogColumn*8 + 7 - blockShiftL,frogRow); } else if (frogRow == 2) { ssd1306_setpos(frogColumn*8 + blockShiftR,frogRow); } else { ssd1306_setpos(frogColumn*8,frogRow); } ssd1306_send_data_start(); sendBlock(mode,0); // draw the frog - mode is direction ssd1306_send_data_stop(); } } // Display the frog and all the moving items on the screen void drawGameScreen(byte mode) { bool inverse = 0; // Draw objects going left for (byte row = 0; row < 6; row+=2) { if (row >=0 && row < 3) inverse = 1; else inverse = 0; // draw everything (except the frog) in inverse video on the river rows (0,1,2) ssd1306_setpos(0,row+1); // +1 because row 0 here is actually row 1 on the screen ssd1306_send_data_start(); for (byte incr = 0; incr < 7-blockShiftL; incr++) if (grid[row][15] == 0) { // cover the tiny bit to the far left of the screen up to wherever the main blocks will be drawn (depends on how far they are shifted) sendByte(0,inverse); // draw an empty 8-bit line if there's nothing wrapping around } else { sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][1+blockShiftL+incr]), inverse); // pick the correct bit of whatever is wrapping from the right of the screen } for (byte col = 0; col < 15; col++) { if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) { sendBlock(mode,0); // if we are in a location with the frog, and it's on the logs, draw it - never invert it (hence zero as second parameter here) } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) { // frog is amongst the cars and needs drawing for (byte incr = 0; incr < blockShiftL; incr++) sendByte(0,0); // draw the blank space up to the frog sendBlock(mode,0); // draw frog for (byte incr = 0; incr < 7-blockShiftL; incr++) sendByte(0,0); // draw the blank space after the frog col++; // we've now drawn two columns so increment } else { sendBlock(grid[row][col],inverse); // draw the correct object for this space - it's not a frog ;) } } // fill in the bit to the right of the main blocks for (byte incr = 0; incr < blockShiftL; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse); ssd1306_send_data_stop(); } if (frogColumn == 0) drawFrog(mode,1); // this covers the exceptional case where the frog is in the far left colum, in which case the normal routine can't draw it when it's on the road // Draw objects going right - see comments above, works in basically the same way for (byte row = 1; row < 6; row+=2) { if (row > 0 && row < 3) inverse = 1; else inverse = 0; ssd1306_setpos(0,row+1); ssd1306_send_data_start(); for (byte incr = 0; incr < blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0, inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr+(8-blockShiftR)]),inverse); for (byte col = 0; col < 15; col++) { if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) { sendBlock(mode,0); } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) { for (byte incr = 0; incr < 7-blockShiftR; incr++) sendByte(0,0); sendBlock(mode,0); // draw frog for (byte incr = 0; incr < blockShiftR; incr++) sendByte(0,0); col++; } else { sendBlock(grid[row][col],inverse); } } for (byte incr = 0; incr < 7-blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse); ssd1306_send_data_stop(); } if (frogColumn == 0) drawFrog(mode,1); } // Send one byte to the screen void sendByte(byte fill, bool inverse) { if (inverse == 0) ssd1306_send_byte(fill); else ssd1306_send_byte(~fill); } // Send one block of 8 bytes to the screen - inverse means inverse video, for the river section void sendBlock(byte fill, bool inverse){ for (int incr = 0; incr < 8; incr++) { if (fill > 0) { if (inverse == 0) ssd1306_send_byte(pgm_read_byte(&bitmaps[fill-1][incr])); else ssd1306_send_byte(~pgm_read_byte(&bitmaps[fill-1][incr])); } else if (inverse ==0) ssd1306_send_byte(0); else ssd1306_send_byte(0xFF); } } // Draw the frog lives (in the right hand corner) void drawLives(void) { byte tempRow = frogColumn; byte tempCol = frogRow; frogRow = 7; for (int incr = 2; incr > 0; incr--) { frogColumn = 15-incr; drawFrog(0,1); } for (int incr = lives; incr > 0; incr--) { frogColumn = 15-incr; drawFrog(1,1); } frogRow = tempCol; frogColumn = tempRow; } // Draw the docks for the frog to land in at top of screen void drawDocks(void) { byte drawPos = 3; for (byte incr = 0; incr < 5; incr++) { ssd1306_setpos(drawPos,0); ssd1306_send_data_start(); ssd1306_send_byte(B11111111); ssd1306_send_byte(B00000001); ssd1306_send_byte(B00000001); if (frogDocks[incr] == 1) sendBlock(1,0); else for(byte lxn = 0; lxn < 8; lxn++) ssd1306_send_byte(B00000001); ssd1306_send_byte(B00000001); ssd1306_send_byte(B00000001); ssd1306_send_byte(B11111111); ssd1306_send_data_stop(); drawPos+= 24; } } // Set all the frog docks to a single value void resetDock(byte value) { for (byte incr = 0; incr < 5;incr++) frogDocks[incr] = value; } // Handle what happens at the end of a level void levelUp(int number) { // Flash the frog docks delay(200); for (byte incr = 0; incr < 5; incr ++) { resetDock(0); drawDocks(); for (int i = 800; i>200; i = i - 200){ beep(20,i); } resetDock(1); drawDocks(); for (int i = 800; i>200; i = i - 200){ beep(20,i); } } delay(500); ssd1306_fillscreen(0x00); ssd1306_char_f6x8(35, 1, "---------"); ssd1306_char_f6x8(35, 3, " LEVEL "); ssd1306_char_f6x8(35, 5, "---------"); doNumber(77,3,number); delay(1500); ssd1306_fillscreen(0x00); } // Move all the items on the game screen (wrapping at the ends) and check for frog dropping off the end of the screen void moveBlocks(void) { int direct = 0; if (flipFlop == 1) flipFlop = 0; else flipFlop = 1; for (byte row = 0; row < 6; row++) { // Move the frog along and check to see whether it's gone off the screen, in which case it dies if (frogRow < 4 && frogRow > 0) { if (frogRow == row + 1) { if (direct == 1 && flipFlop == 1) { if (frogColumn >= 14) stopAnimate = 1; else frogColumn++; } else if (direct == 0) { if (frogColumn < 1) stopAnimate = 1; else frogColumn--; } } } if (direct == 0) { // move left byte temp = grid[row][0]; for (byte col = 0; col < 15; col++) { grid[row][col] = grid[row][col+1]; } grid[row][15] = temp; // wrap around direct = 1; } else { // move right if (flipFlop == 1) { byte temp = grid[row][15]; for (byte col = 15; col > 0; col--) { grid[row][col] = grid[row][col-1]; } grid[row][0] = temp; // wrap around } direct = 0; } } } ``` # 关于烧录 需要在arduino IDE首选项附加开发板中插入https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json 之后打开开发板管理搜索attiny并安装 ## 特别注意 烧录时要看代码中的注释说明 ![](https://cf03.ickimg.com/bbsimages/202006/afe52f3681f43ebd998f8143b0b3ef9d.png) # 结语 **玩得开心!**
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
1
)
☻
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交