[Arduino] 一個完整的俄羅斯方塊遊戲範例


  • wbo

    範例打包在此:wbTetrisV2.zip

    LCD 圖型處理 Library 打包在此:Adafruit-GFX-Library.zip     Adafruit-ST7735-Library-WiFiboy.zip 

    原始程式:wbTetrisV2.ino

    <p>/***************************************************
      Tetris Demo -- WiFiBoy for Arduino
      
      v1.0 -- Sep 28, 2016 tklua@wifiboy.org
      v1.01 -- Oct 28, 2016 for WiFiBoy v1.1 new key-def
      v1.02 -- Jun 5, 2017 for WiFiBoy v2
     ****************************************************/</p>
    <p>#include <Adafruit_GFX.h>    // Core graphics library
    #include <Adafruit_ST7735.h> // Hardware-specific library, optimized for WiFiBoy
    #include <SPI.h>
    #include <Ticker.h></p>
    <p>#define TFT_CS     15
    #define TFT_DC     2</p>
    <p>Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC);</p>
    <p>#include "wbTetrisMelody.h"
    #include "wbTetrisColors.h"</p>
    <p>char blk_t[28][4] = { // seven shapes
      {0,10,20,30},{0,1,2,3},{0,10,20,30},{0,1,2,3},
      {1, 10, 11, 12},{0,10,11,20},{0,1,2,11},{1,10,11,21},
      {0, 1, 10, 11},{0,1,10,11},{0,1,10,11},{0,1,10,11},
      {0,10,20, 21},{0,1,2,10},{0,1,11,21},{2,10,11,12},
      {1,11,20,21},{0,10,11,12},{0,1,10,20},{0,1,2,12},
      {0,1,11,12},{1,10,11,20},{0,1,11,12},{1,10,11,20},
      {1,2,10,11},{0,10,11,21},{1,2,10,11},{0,10,11,21}
    };
    char board[20][10], offboard[20][10];
    uint16 cx, cy, rot, smode, fall_time, fall_limit, stage_limit;
    uint16 ctype, nctype, level, pts, pn, i, j, k, pos, px, py, last_key, cline;
    char levelspeed[12]={12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};</p>
    <p>void ICACHE_FLASH_ATTR draw_preview()
    {
      tft.fillRect(85, 35, 28, 28, wbBLACK);
      for(i=0; i<4; i++) {
        pos = blk_t[nctype*4][i];
        py = pos/10;
        px = pos%10;
        tft.fillRect(85+px*7, 35+py*7, 6, 6, bcolor[nctype+1]);
      }
    }</p>
    <p>void ICACHE_FLASH_ATTR update_score()
    {
      tft.fillRect(85, 80, 50, 15, wbBLACK);
      draw_number(cline, 85, 80, 1);
      if (cline > 150) level = 9;
      else if (cline > 120) level=8;
      else if (cline > 100) level=7;
      else if (cline > 80) level=6;
      else if (cline > 50) level=5;
      else if (cline > 25) level=4;
      else if (cline > 15) level=3;
      else if (cline > 10) level=2;
      else if (cline > 5) level=1;
      fall_limit = stage_limit = levelspeed[level];
      tft.fillRect(85, 110, 50, 15, wbBLACK);
      draw_number(level+1, 85, 110, 1);
      tft.fillRect(85, 140, 50, 15, wbBLACK);
      draw_number(pts, 85, 140, 1);
    }</p>
    <p>int ICACHE_FLASH_ATTR check_line()
    {
      int pt;
      for(i=19; i>0; i--) {
        pt=0;
        for(j=0; j<10; j++) if (board[i][j]!=0) pt++; else break;
        if (pt==10) {
          for(j=i; j>0; j--)
            for(k=0; k<10; k++) board[j][k]=board[j-1][k];
          return 1;
        }
      }
      return 0;
    }</p>
    <p>void ICACHE_FLASH_ATTR bfall()
    {
      for(i=0; i<4; i++) {
        pos = (cy+1)*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (pos < 200) { board[py-1][px]=0;
        } else {
          for(j=0; j<4; j++) {
            pos = (cy)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          pn=0;
          while(check_line()) { // check all stacked line
            cline++;
            update_score();
            pts+=10*(2^pn);
            pn++;
          }
          if (pn) { sfxn=5; sfxc=0; }
          else { sfxn=2; sfxc=0; }
          ctype=nctype;
          nctype=rand()%7;
          pts+=10;
          cx = 4;
          cy = 0;
          rot=rand()%4;
          for(j=0; j<4; j++) {
            pos = (cy)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          draw_preview();
          return;
        }
      }
      cy++;
      for(i=0; i<4; i++) {
        pos = (cy)*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (board[py][px]!=0) {
          for(j=0; j<4; j++) {
            pos = (cy-1)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          if (cy==1) {
            smode=2;
            tft.fillRect(2, 55, 77, 27, wbRED);
            tft.fillRect(5, 58, 71, 21, wbBLACK);
            tft.setTextColor(wbWHITE);
            draw_string("Game Over", 14, 65, 1);
            sfxn=6; sfxc=0;
            return;
          }
          pn=0;
          while(check_line()) {
            cline++;
            update_score();
            pts+=10*(2^pn);
            pn++;
          }
          if (pn) { sfxn=5; sfxc=0; }
          else { sfxn=2; sfxc=0; }
          ctype=nctype;
          nctype=rand()%7;
          pts+=10;
          cx = 4;
          cy = 0;
          rot=rand()%4;
          for(j=0; j<4; j++) {
            pos = (cy)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          draw_preview();
          return;
        }
      }
      for(i=0; i<4; i++) {
        pos = (cy)*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        board[py][px]=ctype+1;
      }
    }</p>
    <p>void ICACHE_FLASH_ATTR check_left()
    {
      for(i=0; i<4; i++) {
        pos = cy*10 + cx-1 + blk_t[ctype*4+rot][i];
        px = pos%10;
        if (px>cx+4) return; //left bound
      }
      for(i=0; i<4; i++) {
        pos = cy*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        board[py][px]=0; // clear block
      }
      for(i=0; i<4; i++) {
        pos = cy*10 + cx-1 + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (board[py][px]!=0) {
          for(j=0; j<4; j++) {
            pos = cy*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          return;
        }
      }
      cx--;
      for(i=0; i<4; i++) {
        pos = cy*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        board[py][px]=ctype+1;
      }
    }</p>
    <p>void ICACHE_FLASH_ATTR check_right()
    {
      for(i=0; i<4; i++) {
        pos = (cy)*10 + cx+1 + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (px>cx-1) {
          board[py][px-1]=0;
        } else {
          for(j=0; j<4; j++) {
            pos = (cy)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          return;
        }
      }
      for(i=0; i<4; i++) {
        pos = (cy)*10 + cx+1 + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (board[py][px]!=0) {
          for(j=0; j<4; j++) {
            pos = (cy)*10 + cx + blk_t[ctype*4+rot][j];
            py = pos/10;
            px = pos%10;
            board[py][px]=ctype+1;
          }
          return;
        }
      }
      cx++;
      for(i=0; i<4; i++) {
        pos = (cy)*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        board[py][px]=ctype+1;
      }
    }</p>
    <p>int ICACHE_FLASH_ATTR check_rotate()
    {
      for(i=0; i<4; i++) {
        pos = cy*10 + cx + blk_t[ctype*4+rot][i];
        py = pos/10;
        px = pos%10;
        if (pos<200) {
          if (board[py][px]!=0) return -2;
          if (px < cx) {
            cx--;
            return -1;
          }
          if (px > cx+4) {
            cx++;
            return -1;
          }
        }
      }
      return 0;
    }
     
    uint16 color1, color2, color3, color4, sa_count;</p>
    <p>void splash_animation()
    {
      color4 = color3;
      color3 = color2;
      color2 = color1;
      color1 = bcolor[rand()%7+1]; // scrolling effect
      tft.fillRect(49, 65, 14, 14, color1);
      tft.fillRect(49, 80, 14, 14, color2);
      tft.fillRect(49, 95, 14, 14, color3);
      tft.fillRect(64, 95, 14, 14, color4);
    }</p>
    <p>void draw_number(uint16_t num, uint8_t x, uint8_t y, uint8_t size)
    {
      tft.setCursor(x, y);
      tft.setTextSize(size);
      tft.print(num);
    }</p>
    <p>void draw_string(char *str, uint8_t x, uint8_t y, uint8_t size)
    {
      tft.setCursor(x, y);
      tft.setTextSize(size);
      tft.print(str);
    }</p>
    <p>void draw_splash()
    {
      tft.fillRect(0, 0, 128, 160, wbBLACK);
      tft.setTextColor(wbWHITE);
      draw_string("WiFiBoy for Arduino", 7, 0, 1);
      tft.setTextColor(wbYELLOW);
      draw_string("Tetris", 10, 25, 3);
      tft.setTextColor(wbGREEN);
      //draw_string("one", 1, 132, 1);
      //draw_string("two", 104, 132, 1);
      draw_string("Play", 1, 144, 1);
      //draw_string("players", 82, 144, 1);
      tft.fillRect(49, 65, 14, 14, color1=wbRED);
      tft.fillRect(49, 80, 14, 14, color2=wbYELLOW);
      tft.fillRect(49, 95, 14, 14, color3=wbBLUE);
      tft.fillRect(64, 95, 14, 14, color4=wbGREEN);
      sa_count = 0;
    }</p>
    <p>void draw_board()
    {
      for(i=0; i<20; i++)
        for(j=0; j<10; j++)
          if (offboard[i][j]!=board[i][j]) // dirty rectangle check
            tft.fillRect(j*8+1,i*8,7,7, bcolor[offboard[i][j]=board[i][j]]);
    }</p>
    <p>void refresh_cb()
    {
      switch(smode) {
        case 0: // for splash
          sa_count++;
          if (sa_count>15) {
            splash_animation();
            sa_count=0;
          }
          break;
        case 1: draw_board(); break; // update board (dirty-rectangle argorithm)
        case 2: break; // end of game
        default: break;
      }
    }</p>
    <p>void clear_board()
    {
      for(i=0; i<20; i++)
        for(j=0; j<10; j++) board[i][j]=0;
    }</p>
    <p>void gameloop_cb()
    {
      uint16 key;
      int ret;</p>
    <p>  switch(smode) {
        case 0: // game menu
          key=analogRead(A0)/240;
          if (key==1) key=2;
          if (digitalRead(5)==0) key=1;
          if (digitalRead(0)==0) key=3;
          switch(key) {
            case 2: 
            case 1: break;
            case 0: // game start
              smode=1;
              tft.fillRect(0, 0, 128, 160, wbBLACK);
              tft.drawFastHLine(0,159,80,wbRED);
              tft.drawFastVLine(0,0,160,wbRED);
              tft.drawFastVLine(80,0,160,wbRED);
              tft.setTextColor(wbWHITE);
              draw_string("lines", 84, 65, 1);
              draw_string("level", 84, 95, 1);
              draw_string("score", 84, 125, 1);
              tft.setTextColor(wbYELLOW);
              draw_string("Tetris", 85, 0, 1);
              draw_string("Game", 105, 12, 1);
              cline=0;
              level=0;
              pts=0;
              fall_limit = stage_limit = levelspeed[level];
              cx=4; cy=0; rot=rand()%4;
              ctype=rand()%7;
              nctype=rand()%7;
              for(i=0; i<4; i++) {
                pos = (cy)*10 + cx + blk_t[ctype*4+rot][i];
                py = pos/10;
                px = pos%10;
                board[py][px]=ctype+1; // draw blocks
              }
              last_key=0;
              draw_preview();
              update_score();
              analogWrite(4,500); // enable pwm, start sound engine
              melody_c=0;
              break;
            case 3:
              analogWrite(4,0); // disable pwm, stop sound engine
              break;
          }
          break;
        case 1: // game playing
          key=analogRead(A0)/240;
          if (key==1) key=2;
          if (digitalRead(5)==0) key=1;
          if (digitalRead(0)==0) key=3;
          switch(key) {
            case 2: last_key=1; break;
            case 1: last_key=2; break;
            case 0:
              fall_limit=1;
              last_key=4;
              break;
            case 3: last_key=3; break;
            case 4: // release all keys
              switch (last_key) {
                case 1: check_left(); last_key=0; fall_limit=stage_limit; break;
                case 2: check_right(); last_key=0; fall_limit=stage_limit; break;
                case 3:
                  for(i=0; i<4; i++) {
                    pos = cy*10 + cx + blk_t[ctype*4+rot][i]; 
                    py = pos/10;
                    px = pos%10;
                    board[py][px]=0; // clean blocks
                  }
                  rot++;
                  if (rot>3) rot=0;
                  while(1) {
                    ret = check_rotate();
                    if (ret==-2) {
                      rot++;
                      if (rot>3) rot=0;
                    } else if (ret==0) break; // pick next legal rotation
                  }
                  for(i=0; i<4; i++) {
                    pos = cy*10 + cx + blk_t[ctype*4+rot][i];
                    py = pos/10;
                    px = pos%10;
                    board[py][px]=ctype+1; // draw blocks
                  }
                  last_key=0;
                  break;
                case 4:
                  fall_limit=stage_limit;
                  last_key=0;
                  break;
                default: break;
              }
              break;
          }
          fall_time++;
          if (fall_time > fall_limit) {
            bfall();
            fall_time=0;
          }
          break;
        case 2: // end of game
          key=analogRead(A0)/240;
          if (digitalRead(0)==0) key=3;
          switch(key) {
            case 0:
            case 1:
            case 2: break;
            case 3:
              smode=0;
              clear_board();
              draw_splash();
              analogWrite(4,0);
              melody_c=0;
              break;
          }
          break;
      }
    }</p>
    <p>Ticker soundEngine; // melody and sound-effect sequencer (player)
    Ticker gameLooper;  // game state controller
    Ticker Refresher;   // display controller</p>
    <p>inline uint16_t swapcolor(uint16_t x) { return (x << 11) | (x & 0x07E0) | (x >> 11); }</p>
    <p>void setup() {
      tft.initR(INITR_GREENTAB); // this means ST7735S driver used in WiFiBoy  
      tft.fillScreen(ST7735_BLACK);
      tft.setTextWrap(false);
      draw_splash();
      for(i=0; i<8; i++) bcolor[i] = swapcolor(bcolor[i]);
      soundEngine.attach_ms(50, melody_cb);
      analogWrite(4,0); // stop sound (pwm)
      no_music=0;
      no_sfx=0;
      gameLooper.attach_ms(20, gameloop_cb); // 50fps
      Refresher.attach_ms(20, refresh_cb); // 50fps
      pinMode(0, INPUT);
      pinMode(5, INPUT);
    }</p>
    <p>void loop() {
      // nothing to do here, we use Ticker to do concurrent jobs
    }</p>
    

    其他檔案:

    wbTetrisColor.h

    <p>#define wbBLACK   0x0000
    #define wbBLUE    0x001F
    #define wbRED     0xF800
    #define wbGREEN   0x07E0
    #define wbCYAN    0x07FF
    #define wbMAGENTA 0xF81F
    #define wbYELLOW  0xFFE0
    #define wbWHITE   0xFFFF</p>
    <p>uint16 bcolor[8]={
      wbBLACK,
      wbBLUE,
      wbRED,
      wbGREEN,
      wbCYAN,
      wbMAGENTA,
      wbYELLOW,
      wbWHITE
    };</p>
    

    wbTetrisMelody.h

    <p>#define NOTE_B0  31
    #define NOTE_C1  33
    #define NOTE_CS1 35
    #define NOTE_D1  37
    #define NOTE_DS1 39
    #define NOTE_E1  41
    #define NOTE_F1  44
    #define NOTE_FS1 46
    #define NOTE_G1  49
    #define NOTE_GS1 52
    #define NOTE_A1  55
    #define NOTE_AS1 58
    #define NOTE_B1  62
    #define NOTE_C2  65
    #define NOTE_CS2 69
    #define NOTE_D2  73
    #define NOTE_DS2 78
    #define NOTE_E2  82
    #define NOTE_F2  87
    #define NOTE_FS2 93
    #define NOTE_G2  98
    #define NOTE_GS2 104
    #define NOTE_A2  110
    #define NOTE_AS2 117
    #define NOTE_B2  123
    #define NOTE_C3  131
    #define NOTE_CS3 139
    #define NOTE_D3  147
    #define NOTE_DS3 156
    #define NOTE_E3  165
    #define NOTE_F3  175
    #define NOTE_FS3 185
    #define NOTE_G3  196
    #define NOTE_GS3 208
    #define NOTE_A3  220
    #define NOTE_AS3 233
    #define NOTE_B3  247
    #define NOTE_C4  262
    #define NOTE_CS4 277
    #define NOTE_D4  294
    #define NOTE_DS4 311
    #define NOTE_E4  330
    #define NOTE_F4  349
    #define NOTE_FS4 370
    #define NOTE_G4  392
    #define NOTE_GS4 415
    #define NOTE_A4  440
    #define NOTE_AS4 466
    #define NOTE_B4  494
    #define NOTE_C5  523
    #define NOTE_CS5 554
    #define NOTE_D5  587
    #define NOTE_DS5 622
    #define NOTE_E5  659
    #define NOTE_F5  698
    #define NOTE_FS5 740
    #define NOTE_G5  784
    #define NOTE_GS5 831
    #define NOTE_A5  880
    #define NOTE_AS5 932
    #define NOTE_B5  988</p>
    <p>uint16_t tetris[] = {
      NOTE_E4, NOTE_E1, NOTE_E4, NOTE_E2, NOTE_B3, NOTE_E1, NOTE_C4, NOTE_E2,
      NOTE_D4, NOTE_E1, NOTE_D4, NOTE_E2, NOTE_C4, NOTE_E1, NOTE_B3, NOTE_D2,
      NOTE_A3, NOTE_A1, NOTE_A3, NOTE_A2, NOTE_A3, NOTE_A1, NOTE_C4, NOTE_A2,
      NOTE_E4, NOTE_A1, NOTE_E4, NOTE_A2, NOTE_D4, NOTE_A1, NOTE_C4, NOTE_A2,
      NOTE_B3, NOTE_GS1, NOTE_B3, NOTE_GS2, NOTE_B3, NOTE_GS1, NOTE_C4, NOTE_GS2,
      NOTE_D4, NOTE_E1, NOTE_D4, NOTE_E2, NOTE_E4, NOTE_E1, NOTE_E4, NOTE_E2,
      NOTE_C4, NOTE_A1, NOTE_C4, NOTE_A1, NOTE_A3, NOTE_A1, NOTE_A3, NOTE_A2,
      NOTE_A3, NOTE_A1, NOTE_A3, NOTE_A2, NOTE_A3, NOTE_B1, NOTE_A3, NOTE_C2,
      NOTE_D4, NOTE_D1, NOTE_D4, NOTE_D2, NOTE_D4, NOTE_D1, NOTE_F4, NOTE_D2,
      NOTE_A4, NOTE_D1, NOTE_A4, NOTE_D2, NOTE_G4, NOTE_D1, NOTE_F4, NOTE_D2,
      NOTE_E4, NOTE_C1, NOTE_E4, NOTE_C2, NOTE_E4, NOTE_C1, NOTE_C4, NOTE_C2,
      NOTE_E4, NOTE_C1, NOTE_E4, NOTE_C2, NOTE_D4, NOTE_C1, NOTE_C4, NOTE_C2,
      NOTE_B3, NOTE_GS1, NOTE_B3, NOTE_GS2, NOTE_B3, NOTE_GS1, NOTE_C4, NOTE_GS2,
      NOTE_D4, NOTE_E1, NOTE_D4, NOTE_E2, NOTE_E4, NOTE_E1, NOTE_E4, NOTE_E2,
      NOTE_C4, NOTE_A1, NOTE_C4, NOTE_A2, NOTE_A3, NOTE_A1, NOTE_A3, NOTE_A2,
      NOTE_A3, NOTE_A1, NOTE_A3, NOTE_A2, NOTE_A3, NOTE_A1, NOTE_A3, NOTE_G1
    };</p>
    <p>uint16_t sfx[8][8]={
        {NOTE_C4, 0, 0, 0, 0, 0, 0, 0},
        {NOTE_C4, NOTE_E4, NOTE_G4, NOTE_C5, 1, 1, 1, 1},
        {NOTE_C2, NOTE_G2, 1, 1, 1, 1, 1, 1},
        {NOTE_C3, 0, 0, 0, 0, 0, 0, 0},
        {NOTE_C4, 0, 0, 0, 0, 0, 0, 0},
        {NOTE_C3, NOTE_B2, NOTE_A2, NOTE_G2, NOTE_F2, NOTE_E2, 1, 1},
        {NOTE_C4, NOTE_G4, NOTE_E4, NOTE_C4, NOTE_G3, NOTE_E3, NOTE_C3, 1},
        {NOTE_B5, 0, 0, 0, 0, 0, 0, 0}
    };</p>
    <p>uint16_t melody_c;
    uint16_t sfxn, sfxc;
    uint16_t period;
    uint8_t no_sfx, no_music;</p>
    <p>void melody_cb()
    {
      if ((melody_c % 2)==1) {
        if (sfxn != 0) {
          period=50000/sfx[sfxn][sfxc];
          sfxc++;
          if (sfxc>7 || period>10000) sfxn=0;
          //analogWrite(4,300); 
        } else { period=10; } //analogWrite(4,0); }
      } else { period=tetris[melody_c/2]; }//analogWrite(4,300); }</p>
    <p>  analogWriteFreq(period*1.5);</p>
    <p>  if (++melody_c>255) melody_c=0;
    }</p>
    


看起來你的連線到 WiFiBoy.Club 已經遺失,請稍等一下我們嘗試重新連線。