Игра Тетрис на светодиодной матрице


В продолжение топика Индикаторное табло на светодиодных матрицах.

Создадим на этой матрице игру Тетрис. Управление с помощью уже существующей аналоговой клавиатуры.
клавиатура

Переход в игру и обратно по кнопке red.
Создаем простенько
Назначение кнопок:
green левая, правая — фигура влево/вправо
green вверх — поворот фигуры но 90 градусов
white — увеличение скорости на 25 пунктов
red — выход из игры

void loop() {
  int valbutton;
  // go through the six channels of the digital pot:
  switch(tekgame)
   {
   case 0: game1();
      break;
   case 1: if(!game2())
          {clearDisplay();}
      break;
   case 2: game1();
        //game3();
      break;
   default: break;
   }
   // опрос клавиатуры
   valbutton=analogRead(keyboardPin);
   if(valbutton<1000)
     {buttonClick2(buttonClick1(valbutton));}

}
// обработка нажатия кнопки
int buttonClick1(int val)
 {
  if(val>650) {KEYS1.button=1;return 1;}
  if(val>600) {KEYS1.button=2;return 2;}
  if(val>530) {KEYS1.button=3;return 3;}
  if(val>450) {KEYS1.button=4;return 4;}
  if(val>300) {KEYS1.button=5;return 5;}
  if(val>200) {KEYS1.button=6;return 6;}
  return 0;
  }
   void buttonClick2(int val)
 {
   if(millis()-KEYS1.millisbutton[val]<100)
     return;
   KEYS1.millisbutton[val]=millis();
   KEYS1.button=val;
   switch(val) {
    case 1: OFFSET_DISPLAY1.deltaX=1;OFFSET_DISPLAY1.deltaY=0;
        FIGURA1.dX=FIGURA1.dX-1;                                        //
        if(PITON1.button!=4)
           PITON1.button=1;
        Serial.println("left");
        break;
    case 2:
        OFFSET_DISPLAY1.deltaX=0;OFFSET_DISPLAY1.deltaY=-1;  // game1
        FIGURA1.dT=1;                                        // game2 (поворот 90)
        if(PITON1.button!=3)
           PITON1.button=2;
        Serial.println("up");
       break;
    case 3: OFFSET_DISPLAY1.deltaX=0;OFFSET_DISPLAY1.deltaY=1;
        Serial.println("down");
        if(PITON1.button!=2)
           PITON1.button=3;
       break;
    case 4: OFFSET_DISPLAY1.deltaX=-1;OFFSET_DISPLAY1.deltaY=0;
        FIGURA1.dX=FIGURA1.dX+1;                                        //
        if(PITON1.button!=1)
           PITON1.button=4;
       Serial.println("right");
        break;
    case 5: OFFSET_DISPLAY1.deltaX=0;OFFSET_DISPLAY1.deltaY=0;
        // переключение на другую программу
        tekpoz1=(tekpoz1+1)%(sizeof(startpozdisplay1)-1);
        tekindex1=startpozdisplay1[tekpoz1];
        speed2=max(speed2-25,5);
        Serial.println("white");
        Serial.println(speed2);
       break;
    case 6:
        OFFSET_DISPLAY1.deltaX=0;OFFSET_DISPLAY1.deltaY=0;
        OFFSET_DISPLAY1.offsetX=0;OFFSET_DISPLAY1.offsetY=0;
        clearDisplay();
        tekgame=(tekgame+1)%3;
        Serial.println("red");
        break;
    default:
        break;
   }
 }

Для реализации игры создадим массив со всеми изображениями фигур для Тетриса arrFigure[][], массив указателей на новую фигуру при повороте, transFigure[], а также структуру FIGURA для сохранения информации о текущей фигуре(положении на экране, )

byte arrFigure[16][5]={
     {0,0,0,0,0},{15,0,0,0,4},{1,1,1,1,1},{3,3,0,0,2}, 
     {7,1,0,0,3},{7,4,0,0,3},{1,7,0,0,3},{4,7,0,0,3}, 
     {3,1,1,0,2},{3,2,2,0,2},{1,1,3,0,2},{2,2,3,0,2},
     {7,2,0,0,3},{2,3,2,0,2},{2,7,0,0,3},{1,3,1,0,2}};
byte transFigure[16]={0,2,1,3,9,8,10,11,5,7,4,6,13,14,15,12};
struct FIGURA    // структура для хранения фигуры на экране
{
  int driving;   // 1 – в движении
  int offsetX;   // смещение по x
  int offsetY;   // смещение по y
  int dX;      // изменение влево-вправо 
  int dY;      // dy
  int dT;      // 1 - поворот 90 градусов
  int c;  // номер фигуры
  int length;  // ширина фигуры
  long millis1;
};
FIGURA FIGURA1={0,3,20,0,0,0,0,0,0};

arrFigure[][5] — первые четыре байта —
построчно для четырех(max высота фигуры) строк значение строки буфера экрана displayArray[24]
для пустой строки, занятой только фигурой
пятый байт — ширина фигуры (при движении влево-вправо проще вычислениями следить за границей экрана, при повороте это делать сложнее,
поэтому введен этот параметр)
Новая фигура помещается в верхнюю невидимую часть буфера экрана (байты 20-23)

Добавление фигуры на поле выполняет процедура add_figura(). Фигура выбирается с помощью функции random(). В структуру FIGURA заносятся следующие начальные значения:
— offsetY – номер фигуры, выдаваемый функцией random();
— offsetX=3
— int offsetY=20 (верх буфера экрана);
— с — номер фигуры;
— length — ширина;
— FIGURA1.driving=1 – фигура движется.
и вывести изображение фигуры в буфер эграна. Данные при этом заносятся в верхнюю невидимую часть буфера экрана (байты 20-23 массива displayArray).

void add_figura()
 {
 //FIGURA1.figura=random(1,sizeof(transFigure));
 FIGURA1.figura=millis()%sizeof(transFigure);
 FIGURA1.dX=0;FIGURA1.dY=0;FIGURA1.dT=0;
 FIGURA1.offsetX=3;FIGURA1.offsetY=20;
 FIGURA1.millis1=millis();
 FIGURA1.length=arrFigure[FIGURA1.figura][4];
 for(int i=0;i<4;i++)
   {displayArray[FIGURA1.offsetY+i]=arrFigure[FIGURA1.figura][i]<<FIGURA1.offsetX;}
 FIGURA1.driving=1;  
 }

Рассмотрим как осуществляется движение фигуры вниз.
Через время установленное в переменной speed2 (меняется нажатием на кнопку white) происходит стирание из буфера экрана фигуры (процедура figura_clear()), вычисление нового положения фигуры, прорисовка (занесение в буфер экрана) фигуры в измененных коорднатах (процедура figura_driving()).

// очищение изображения фигуры
void  figura_clear(int y,int x)
  {
   for(int i=0;i<4;i++)
    displayArray[y+i]=displayArray[y+i]-(arrFigure[FIGURA1.figura][i]<<x);
   }
// перемещение фигуры
void  figura_driving(int y,int x,int figura)
  {
   for(int i=0;i<4;i++)
    displayArray[y+i]=displayArray[y+i]+(arrFigure[figura][i]<<FIGURA1.offsetX);
  }

Перед изменением положения фигуры (вниз, влево, вправо, поворот) необходимо производить проверку на столкновение с другими (уже неподвижными фигурами) и выход из границ. Для движения влево, вправо, поворот изменение положения производится до движения вниз. Для движения вниз при положительной проверке на столкновение происходит останов фигуры (FIGURA1.driwing=0). Процедуры проверки на столкновение collision1() и collision2()

// контроль столкновения с кирпичиками поворот,в сторону
 boolean collision1(int yd,int figura,int figurapr,int dx,int ddx)
  {
  byte opand,disp;
  boolean collision=false;
  for(int i=0;i<4;i++) // по высоте фигуры - 4
    {
    disp=displayArray[yd+i];
    disp=disp-(arrFigure[figurapr][i]<<dx);  
    opand=disp & (arrFigure[figura][i]<<ddx); 
    if(opand>0)
      {collision=true;}
    } 
   return collision; 
  }
  // контроль столкновения с кирпичиками вниз
 boolean collision2(int yd,int figura,int figurapr,int dx,int ddx)
  {
  byte opand,disp;
  boolean collision=false;
  for(int i=0;i<4;i++) // по высоте фигуры - 4
    {
    disp=displayArray[yd+i];
    if(i>0)
      disp=disp-(Figure[figurapr][i-1]<<dx);  
    opand=disp & (arrFigure[figura][i]<<dx); 
    if(opand>0)
      {collision=true;}
    } 
   return collision; 
  }

И код главной поцедуры — game2()

// игра тетрис
 boolean game2()
  {
   if(FIGURA1.driving==0)
     add_figura(); 
   if((millis()-FIGURA1.millis1)>speed2 && FIGURA1.driving>0)
     {
     //figura_clear(FIGURA1.offsetY);  
     // по X
     if(FIGURA1.dX!=0)
        {
        int dxpr=FIGURA1.dX;
        int offxpr=FIGURA1.offsetX+dxpr;
        offxpr=max(offxpr,0);
        offxpr=min(offxpr,8-FIGURA1.length);       
        if(!collision1(FIGURA1.offsetY,FIGURA1.figura,FIGURA1.figura,FIGURA1.offsetX,offxpr))
         {
        figura_clear(FIGURA1.offsetY,FIGURA1.offsetX);
        FIGURA1.offsetX=FIGURA1.offsetX+FIGURA1.dX;
        FIGURA1.offsetX=max(FIGURA1.offsetX,0);
        FIGURA1.offsetX=min(FIGURA1.offsetX,8-FIGURA1.length);       
        FIGURA1.dX=0;
        Serial.println(FIGURA1.offsetX);    
        figura_driving(FIGURA1.offsetY,0,FIGURA1.figura);
         }
        
        //figura_clear(FIGURA1.offsetY);
       }
     // поворот 90 градусов
     if(FIGURA1.dT>0)
        {
        if(!collision1(FIGURA1.offsetY,transFigure[FIGURA1.figura],FIGURA1.figura,FIGURA1.offsetX,FIGURA1.offsetX))
         {
         figura_clear(FIGURA1.offsetY,FIGURA1.offsetX);
         figura_driving(FIGURA1.offsetY,0,transFigure[FIGURA1.figura]);
         FIGURA1.figura=transFigure[FIGURA1.figura];
         FIGURA1.length=arrFigure[FIGURA1.figura][4];
         }
        FIGURA1.dT=0;
        //figura_clear(FIGURA1.offsetY);
       }
     // вниз по времени
     if(collision2(FIGURA1.offsetY-1,FIGURA1.figura,FIGURA1.figura,FIGURA1.offsetX,FIGURA1.offsetX))
       {FIGURA1.millis1=millis();FIGURA1.driving=0;
     if(FIGURA1.offsetY==20)
       return false;
     else
       return true;
     }
     figura_clear(FIGURA1.offsetY,FIGURA1.offsetX);  
     figura_driving(FIGURA1.offsetY-1,0,FIGURA1.figura);
     FIGURA1.offsetY=FIGURA1.offsetY-1;
     if(FIGURA1.offsetY<5)
       FIGURA1.driving=0;
  
     
        
    FIGURA1.dX=0;FIGURA1.dY=0;FIGURA1.dT=0;
    FIGURA1.millis1=millis();
      }
   return true;  
  }

Все — игра готова — смотрим видео

И скачать скетч здесь


0 комментариев на «“Игра Тетрис на светодиодной матрице”»

  1. Забавно вышло.

    Вообще-то название тетрис от «тетра», что значит четыре. Т.е. в игре используются все варианты фигур из четырех элементов. Там где больше — это уже китайтрис:) Так, для фактов

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна конкурс манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение