цифровая электроника
вычислительная техника
встраиваемые системы

 




Делаем игру Space Invader на основе Arduino и OLED-дисплея

Автор: Mike(admin) от 23-11-2022, 03:55

Сегодня мы сделаем вполне веселый и интересный проект на Arduino, а именно игру, причем игру весьма популярную когда-то на 8-битных компьютерах и приставках – Space Invader. Для Arduino доступно несколько версий игр Space Invader, но мы попытаемся сделать наиболее приближенную к оригиналу версию. Мы задействуем экран большего размера, поэтому возьмем 2,7-дюймовый OLED. Благодаря этому мы увеличим количество рядов и столбцов, чтобы они соответствовали оригинальному Taito Space Invaders.


Делаем игру Space Invader на основе Arduino и OLED-дисплея

В качестве основной платы мы возьмем Arduino Nano Every, так как нам понадобится расширенная область памяти для дополнительных инопланетян на экране. Также данный Nano имеет весьма приличную тактовую частоту 20 МГц.


Схема подключения компонентов для реализации игры Space Invader на основе Arduino приведена далее. Здесь для управления используются три кнопки, а звуковое сопровождение осуществляется с помощью зуммера.


Делаем игру Space Invader на основе Arduino и OLED-дисплея

Вот как это может выглядеть в сборе.


Делаем игру Space Invader на основе Arduino и OLED-дисплея

Код программы представлен далее.



#include <SPI.h>
#include <U8g2lib.h>
#include <EEPROM.h>
 
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#define OLED_CLK 13
#define OLED_MOSI 11
#define OLED_CS 3
#define OLED_DC 2
#define OLED_RST 8

U8G2_SSD1325_NHD_128X64_F_4W_HW_SPI display(U8G2_R0, OLED_CS, OLED_DC, OLED_RST);

#define FIRE_BUT 6
#define RIGHT_BUT 5
#define LEFT_BUT 4

#define MOTHERSHIP_HEIGHT 3
#define MOTHERSHIP_WIDTH 7
#define MOTHERSHIP_SPEED 1
#define MOTHERSHIP_SPAWN_CHANCE 150    
#define DISPLAY_MOTHERSHIP_BONUS_TIME 20

#define ALIEN_HEIGHT 3                            
#define NUM_ALIEN_COLUMNS 11                      
#define NUM_ALIEN_ROWS 5
#define X_START_OFFSET 26
#define SPACE_BETWEEN_ALIEN_COLUMNS 2
#define LARGEST_ALIEN_WIDTH 5
#define SPACE_BETWEEN_ROWS 6
#define INVADERS_DROP_BY 2                         
#define INVADERS_SPEED 10                          
#define INVADER_HEIGHT 3                            
#define EXPLOSION_GFX_TIME 10                      
#define AMOUNT_TO_DROP_BY_PER_LEVEL 4             
#define LEVEL_TO_RESET_TO_START_HEIGHT 4          
#define ALIEN_X_MOVE_AMOUNT 1                    
#define CHANCEOFBOMBDROPPING 40                     
#define BOMB_HEIGHT 4
#define BOMB_WIDTH 2
#define MAXBOMBS 3                                
#define CHANCE_OF_BOMB_DAMAGE_TO_LEFT_OR_RIGHT 20   
#define CHANCE_OF_BOMB_PENETRATING_DOWN 1          
 
#define TANKGFX_WIDTH 5
#define TANKGFX_HEIGHT 3
#define PLAYER_X_MOVE_AMOUNT 2                     
#define LIVES 3                                    
#define PLAYER_EXPLOSION_TIME 10                     
#define PLAYER_Y_START 61
#define PLAYER_X_START 20
#define BASE_WIDTH 8               
#define BASE_HEIGHT 6
#define BASE_Y 53
#define NUM_BASES 4
 
#define MISSILE_HEIGHT 4
#define MISSILE_WIDTH 1
#define MISSILE_SPEED 3                         
 
#define ACTIVE 0
#define EXPLODING 1
#define DESTROYED 2

unsigned long lastMillis;
unsigned long frameCount;
unsigned int framesPerSecond;

#define NOTELENGTH 1                              
 
 
 const unsigned char MotherShipGfx [] PROGMEM = {
  B01111100,
  B10111010,
  B11101110
};
 
const unsigned char InvaderTopGfx [] PROGMEM = {
  B00000010,
  B00000111,
  B00000101,
};
 
const unsigned char InvaderTopGfx2 [] PROGMEM = {
  B00000101,
  B00000111,
  B00000010,
};
 
const unsigned char PROGMEM InvaderMiddleGfx []=
{
  B00001010,
  B00011111,
  B00010101,
};
 
const unsigned char PROGMEM InvaderMiddleGfx2 [] = {
  B00010101,
  B00011111,
  B00001010,
};
 
const unsigned char PROGMEM InvaderBottomGfx [] = {
  B00001010,
  B00010101,
  B00011011,
};
 
const unsigned char PROGMEM InvaderBottomGfx2 [] = {
  B00011011,
  B00010101,
  B00001110,
};
 
static const unsigned char PROGMEM ExplosionGfx [] = {
  B00010101,
  B00001010,
  B00010101,
};
 
const unsigned char PROGMEM TankGfx [] = {
  B00000100,
  B00011111,
  B00011111,
};
 
static const unsigned char PROGMEM MissileGfx [] = {
  B00000001,
  B00000001,
  B00000001,
  B00000001,
};
 
static const unsigned char PROGMEM AlienBombGfx [] = {
  B00000010,
  B00000001,
  B00000010,
  B00000001,
};
 
static const unsigned char PROGMEM BaseGfx [] = {
  B00111100,
  B01111110,
  B11111111,
  B11111111,
  B11100111,
  B11000011,
};
 
 
 
struct GameObjectStruct  {
    signed int X;
    signed int Y;  
    unsigned char Status;
};
 
 
struct BaseStruct {
    GameObjectStruct Ord;
    unsigned char Gfx[6];
};
 
 
struct AlienStruct  {
  GameObjectStruct Ord;
  unsigned char ExplosionGfxCounter;
};
 
struct PlayerStruct  {
    GameObjectStruct Ord;
    unsigned int Score;
    unsigned char Lives;
    unsigned char Level;
    unsigned char AliensDestroyed;         
    unsigned char AlienSpeed;                
    unsigned char ExplosionGfxCounter;
};

AlienStruct  Alien[NUM_ALIEN_COLUMNS][NUM_ALIEN_ROWS];
AlienStruct MotherShip;
GameObjectStruct AlienBomb[MAXBOMBS];
BaseStruct Base[NUM_BASES];
 
static const int TOTAL_ALIENS=NUM_ALIEN_COLUMNS*NUM_ALIEN_ROWS;

byte AlienWidth[]={5,5,5,5,5};
 
 
char AlienXMoveAmount=2;  
signed char InvadersMoveCounter;
bool AnimationFrame=false;
 
signed char MotherShipSpeed;
unsigned int MotherShipBonus;
signed int MotherShipBonusXPos;
unsigned char MotherShipBonusCounter;
 
PlayerStruct Player;
GameObjectStruct Missile;
 
unsigned int HiScore;
bool GameInPlay=false;
 
const unsigned char Music[] =  {
  160,100,80,62
};
 
unsigned char MusicIndex;               
unsigned MusicCounter;                     
                                    
bool ShootCompleted=true;            
 
 
void setup() 
{
  display.begin();
  display.clear();
  display.setBitmapMode(1);
  
  InitAliens(0); 
  InitPlayer();
  
  pinMode(RIGHT_BUT, INPUT_PULLUP);
  pinMode(LEFT_BUT, INPUT_PULLUP);
  pinMode(FIRE_BUT, INPUT_PULLUP);
 
  display.setFont(u8g2_font_t0_11b_tf);
  display.setDrawColor(1);         
  
  EEPROM.get(0,HiScore);
  if(HiScore==65535)                                      
  { 
    HiScore=0;
    EEPROM.put(0,HiScore);
  }
}
 
void loop()  
{  
  if(GameInPlay)                                             
  {
    Physics();
    UpdateDisplay();
  }
  else  
    AttractScreen();
}

const uint8_t digit[10][5] PROGMEM = {
   B00000111,    B00000101,    B00000101,    B00000101,    B00000111,
   B00000100,    B00000100,    B00000100,    B00000100,    B00000100,
   B00000111,    B00000100,    B00000111,    B00000001,    B00000111,
   B00000111,    B00000100,    B00000111,    B00000100,    B00000111,
   B00000101,    B00000101,    B00000111,    B00000100,    B00000100,
   B00000111,    B00000001,    B00000111,    B00000100,    B00000111,
   B00000111,    B00000001,    B00000111,    B00000101,    B00000111,
   B00000111,    B00000100,    B00000100,    B00000100,    B00000100,
   B00000111,    B00000101,    B00000111,    B00000101,    B00000111,   
   B00000111,    B00000101,    B00000111,    B00000100,    B00000111,
  };

 
void draw_digit(int16_t x, int16_t y, uint8_t n) {
display.drawXBMP(x, y, 3, 5, digit[n % 10] );
}

void print_int(int16_t x, int16_t y, uint16_t s, uint8_t digit) {
  x += 4 * (digit - 1);
  for (; digit > 0; digit--) {
    draw_digit(x, y, s % 10);
    x -= 4;
    s /= 10;
  }
}


void AttractScreen()
{
  display.clearBuffer();

  CentreText("Play",8); 
  CentreText("Space Invaders",20); 
  CentreText("Press Fire to start",32); 
  CentreText("Hi Score     ",44);   
  display.setCursor(80,44);
  display.print(HiScore);

  display.sendBuffer();
  
  if(digitalRead(FIRE_BUT)==0){
    GameInPlay=true;
    NewGame();
  }
 
  if((digitalRead(LEFT_BUT)==0)&(digitalRead(RIGHT_BUT)==0))
  {
    HiScore=0;
    EEPROM.put(0,HiScore);
  }
}
 
void Physics()  {  
  if(Player.Ord.Status==ACTIVE) {
    AlienControl(); 
    MotherShipPhysics();
    PlayerControl(); 
    MissileControl();
    CheckCollisions();
  }
}
 
unsigned char GetScoreForAlien(int RowNumber)
{
   switch (RowNumber)
   {
     case 0:return 30;
     case 1:return 20;
     case 2:return 10;
     case 3:return  5;
     case 4:return  2;

   }
}
 
void MotherShipPhysics()  {
  if(MotherShip.Ord.Status==ACTIVE)  {                  
    tone(10, ( MotherShip.Ord.X % 8)*500,200);
    MotherShip.Ord.X+=MotherShipSpeed;
    if(MotherShipSpeed>0)                             
    {
      if(MotherShip.Ord.X>=(SCREEN_WIDTH-26))  
      {
        MotherShip.Ord.Status=DESTROYED;
        noTone(10);
      }
    }
    else                                             
    {
      if(MotherShip.Ord.X+MOTHERSHIP_WIDTH<28)  
      {
        MotherShip.Ord.Status=DESTROYED;
        noTone(10);
      }
    }
    
  }
  else  {
  
    if(random(MOTHERSHIP_SPAWN_CHANCE)==1)
    {
      MotherShip.Ord.Status=ACTIVE;               
      if(random(2)==1)                          
      {
        MotherShip.Ord.X=(SCREEN_WIDTH-20-MOTHERSHIP_WIDTH);
        MotherShipSpeed=-MOTHERSHIP_SPEED;       
      }
      else
      {
        MotherShip.Ord.X=20;                    
        MotherShipSpeed=MOTHERSHIP_SPEED;           
      }
    }
  }
}
 
void PlayerControl()  {
  if((digitalRead(RIGHT_BUT)==0)&(Player.Ord.X+TANKGFX_WIDTH<(SCREEN_WIDTH-20)))
    Player.Ord.X+=PLAYER_X_MOVE_AMOUNT;
  if((digitalRead(LEFT_BUT)==0)&(Player.Ord.X>PLAYER_X_START))
    Player.Ord.X-=PLAYER_X_MOVE_AMOUNT;
  if((digitalRead(FIRE_BUT)==0)&(Missile.Status!=ACTIVE))  
  {
    tone(10, 1000,100);
    Missile.X=Player.Ord.X+(TANKGFX_WIDTH/2);
    Missile.Y=PLAYER_Y_START;
    Missile.Status=ACTIVE;
  }
}
 
void MissileControl()
{ 
  if(Missile.Status==ACTIVE)  
  {
    Missile.Y-=MISSILE_SPEED;
    if(Missile.Y+MISSILE_HEIGHT<0)      
      Missile.Status=DESTROYED;
  }
}
 
 
void AlienControl()
{
  if((InvadersMoveCounter--)<0)
  {
    bool Dropped=false;
    if((RightMostPos()+AlienXMoveAmount>=(SCREEN_WIDTH-18)) |(LeftMostPos()+AlienXMoveAmount<21))
    {
      AlienXMoveAmount=-AlienXMoveAmount;           
      Dropped=true;                                    
    }

    if((ShootCompleted)&(MotherShip.Ord.Status!=ACTIVE))  {
      tone(10, Music[MusicIndex],100);
      MusicIndex++;
      if(MusicIndex==sizeof(Music))
        MusicIndex=0;
      MusicCounter=NOTELENGTH;
    }

    for(int Across=0;Across<NUM_ALIEN_COLUMNS;Across++) 
    {
      for(int Down=0;Down<NUM_ALIEN_ROWS;Down++)
      {
        if(Alien[Across][Down].Ord.Status==ACTIVE)
        {
          if(Dropped==false)
            Alien[Across][Down].Ord.X+=AlienXMoveAmount;
          else
            Alien[Across][Down].Ord.Y+=INVADERS_DROP_BY;
        }
      }
    }  
    InvadersMoveCounter=Player.AlienSpeed;
    AnimationFrame=!AnimationFrame;                    
  }
  if(random(CHANCEOFBOMBDROPPING)==1)  
    DropBomb();
  MoveBombs();
}
 
 
void MoveBombs(){
  for(int i=0;i<MAXBOMBS;i++){
    if(AlienBomb[i].Status==ACTIVE)
      AlienBomb[i].Y+=1;
  }
}
 
 
void DropBomb()  {
  bool Free=false;
  unsigned char ActiveCols[NUM_ALIEN_COLUMNS];  
  unsigned char BombIdx=0;
  while((Free==false)&(BombIdx<MAXBOMBS)){
    if(AlienBomb[BombIdx].Status==DESTROYED)
      Free=true;
    else
      BombIdx++;
  }
  if(Free)  {
    unsigned char Columns=0;
    unsigned char ActiveColCount=0;
    signed char Row;
    unsigned char ChosenColumn;
    
    while(Columns<NUM_ALIEN_COLUMNS){
      Row=(NUM_ALIEN_ROWS-1);
      while(Row>=0)  {
        if(Alien[Columns][Row].Ord.Status==ACTIVE)
        {
          ActiveCols[ActiveColCount]=Columns;
          ActiveColCount++;
          break;
        }
        Row--;
      }
      Columns++;
    }
    ChosenColumn=random(ActiveColCount);
    Row=(NUM_ALIEN_ROWS-1);
    while(Row>=0)  {
      if(Alien[ActiveCols[ChosenColumn]][Row].Ord.Status==ACTIVE)  {
        AlienBomb[BombIdx].Status=ACTIVE;
        AlienBomb[BombIdx].X=Alien[ActiveCols[ChosenColumn]][Row].Ord.X+int(AlienWidth[Row]/2);
        AlienBomb[BombIdx].X=(AlienBomb[BombIdx].X-2)+random(0,4);
        AlienBomb[BombIdx].Y=Alien[ActiveCols[ChosenColumn]][Row].Ord.Y+4;        
        break;
      }
      Row--;
    }
  }
}
 
 
 
void BombCollisions()
{
  for(int i=0;i<MAXBOMBS;i++)
  {
    if(AlienBomb[i].Status==ACTIVE)
    {
      if(AlienBomb[i].Y>64)                     
        AlienBomb[i].Status=DESTROYED;
      else  
      {
          if(Collision(AlienBomb[i],BOMB_WIDTH,BOMB_HEIGHT,Missile,MISSILE_WIDTH,MISSILE_HEIGHT))
          {
              AlienBomb[i].Status=EXPLODING;
              Missile.Status=DESTROYED;
          }
          else
          {               
            if(Collision(AlienBomb[i],BOMB_WIDTH,BOMB_HEIGHT,Player.Ord,TANKGFX_WIDTH,TANKGFX_HEIGHT))
            {
               PlayerHit();
               AlienBomb[i].Status=DESTROYED;
            }
            else  
              BombAndBasesCollision(&AlienBomb[i]);
          }
       }
    }
  }  
}
 
void BombAndBasesCollision(GameObjectStruct *Bomb)  {

  for(int i=0;i<NUM_BASES;i++)
  {
      if(Collision(*Bomb,BOMB_WIDTH,BOMB_HEIGHT,Base[i].Ord,BASE_WIDTH,BASE_HEIGHT)) 
      {
        unsigned char X=Bomb->X-Base[i].Ord.X;
        signed char Bomb_Y=(Bomb->Y+BOMB_HEIGHT)-Base[i].Ord.Y;
        unsigned char Base_Y=0;
 
        while((Base_Y <= Bomb_Y) & (Base_Y<BASE_HEIGHT) & (Bomb->Status==ACTIVE))
        {
          unsigned char Idx=(Base_Y);                         
          unsigned char TheByte=Base[i].Gfx[Idx];        
          unsigned char Mask=B00000011;
          if (X>6) {X=6; };
          Mask=(Mask<<(X));
          TheByte=TheByte & Mask;
          
          if(TheByte>0) 
          { 
            Mask=~Mask; 
            Base[i].Gfx[Idx]=Base[i].Gfx[Idx] & Mask;
            if (random(CHANCE_OF_BOMB_PENETRATING_DOWN) == false) 
            Bomb->Status = EXPLODING;
          }
        else
          Base_Y++;
         }
        }
      }
    }
 
void MissileAndBasesCollisions()  {
  for(int i=0;i<NUM_BASES;i++)
  { 
      if(Collision(Missile,MISSILE_WIDTH,MISSILE_HEIGHT,Base[i].Ord,BASE_WIDTH,BASE_HEIGHT)) 
      {
        unsigned char X=Missile.X-Base[i].Ord.X;
        signed char Missile_Y=Missile.Y-Base[i].Ord.Y;
        signed char Base_Y=BASE_HEIGHT-1;
        while((Base_Y>=Missile_Y)&(Base_Y>=0)&(Missile.Status==ACTIVE))
        {
          unsigned char Idx=(Base_Y);
          unsigned char TheByte=Base[i].Gfx[Idx];
          unsigned char Mask = B00000011;
          if (X>6) {X=6; }
          Mask=(Mask<<(X));
          TheByte=TheByte & Mask;
          
          if(TheByte>0) 
          { 
            Mask=~Mask; 
            Base[i].Gfx[Idx]=Base[i].Gfx[Idx] & Mask;
           if(random(CHANCE_OF_BOMB_PENETRATING_DOWN)==false)   
              Missile.Status=EXPLODING;
          }
          else
            Base_Y--;
        }
      }
  }
}
 
void PlayerHit()  {
  Player.Ord.Status=EXPLODING;
  Player.ExplosionGfxCounter=PLAYER_EXPLOSION_TIME;
  Missile.Status=DESTROYED;
}
 
void CheckCollisions()
{
  MissileAndAlienCollisions();
  MotherShipCollisions();
  MissileAndBasesCollisions();
  BombCollisions(); 
  AlienAndBaseCollisions();
}
 
char GetAlienBaseCollisionMask(int AlienX, int AlienWidth,int BaseX)
{
  signed int DamageWidth;
  unsigned char LeftMask,RightMask,FullMask;
  
  LeftMask=B11111111;
  RightMask=B11111111;                
  if(AlienX>BaseX)  
  {                    
    DamageWidth=AlienX-BaseX;
    LeftMask>>=DamageWidth;          
  }
  
  if(AlienX+AlienWidth<BaseX+(BASE_WIDTH/2))
  {
    DamageWidth=(BaseX+(BASE_WIDTH/2))-(AlienX+AlienWidth);
    RightMask<<=DamageWidth;      
    
  }
  
  return ~(LeftMask & RightMask);
}
 
void DestroyBase(GameObjectStruct *Alien,BaseStruct *Base,char Mask,int BaseByteOffset)
{
  signed char Y;
  Y=(Alien->Y+ALIEN_HEIGHT)-Base->Ord.Y;
  if(Y>BASE_HEIGHT-1) Y=BASE_HEIGHT-1;
  for(;Y>=0;Y--){
    Base->Gfx[(Y)+BaseByteOffset]=Base->Gfx[(Y)+BaseByteOffset] & Mask;
  }
}
 
void AlienAndBaseCollisions()  
{
  unsigned char Mask;
  for(int row=(NUM_ALIEN_ROWS-1);row>=0;row--)
  {
    for(int column=0;column<NUM_ALIEN_COLUMNS;column++)
    {
      if(Alien[column][row].Ord.Status==ACTIVE)
      {
        for(int BaseIdx=0;BaseIdx<NUM_BASES;BaseIdx++)
        {          
          if(Collision(Alien[column][row].Ord,AlienWidth[row],ALIEN_HEIGHT,Base[BaseIdx].Ord,BASE_WIDTH,BASE_HEIGHT))
          {
            Mask=GetAlienBaseCollisionMask(Alien[column][row].Ord.X,AlienWidth[row],Base[BaseIdx].Ord.X);
            DestroyBase(&Alien[column][row].Ord,&Base[BaseIdx],Mask,0);
          }
        }
      }
    }
  }
}
 
void MotherShipCollisions()
{
  if((Missile.Status==ACTIVE)&(MotherShip.Ord.Status==ACTIVE))
  {
    if(Collision(Missile,MISSILE_WIDTH,MISSILE_HEIGHT,MotherShip.Ord,MOTHERSHIP_WIDTH,MOTHERSHIP_HEIGHT))
    {
      MotherShip.Ord.Status=EXPLODING;
      MotherShip.ExplosionGfxCounter=EXPLOSION_GFX_TIME;
      Missile.Status=DESTROYED;
      MotherShipBonus=random(4); 
      switch(MotherShipBonus)
      {
        case 0:MotherShipBonus=50;break;
        case 1:MotherShipBonus=100;break;
        case 2:MotherShipBonus=150;break;
        case 3:MotherShipBonus=300;break;
      }
      Player.Score+=MotherShipBonus;
      MotherShipBonusXPos=MotherShip.Ord.X;    
      if(MotherShipBonusXPos>100)                  
        MotherShipBonusXPos=100;       
      if(MotherShipBonusXPos<0)                 
        MotherShipBonusXPos=0;       
      MotherShipBonusCounter=DISPLAY_MOTHERSHIP_BONUS_TIME;        
    }
  }
}
 
 
void MissileAndAlienCollisions()
{
   for(int across=0;across<NUM_ALIEN_COLUMNS;across++)
  {
    for(int down=0;down<NUM_ALIEN_ROWS;down++)
    {
      if(Alien[across][down].Ord.Status==ACTIVE)
      {
        if(Missile.Status==ACTIVE)
        {
          if(Collision(Missile,MISSILE_WIDTH,MISSILE_HEIGHT,Alien[across][down].Ord,AlienWidth[down],INVADER_HEIGHT))
          {
              Alien[across][down].Ord.Status=EXPLODING;
             tone(10, 700,100);
              Missile.Status=DESTROYED;
              Player.Score+=GetScoreForAlien(down);
              Player.AliensDestroyed++;
              Player.AlienSpeed=((1-(Player.AliensDestroyed/(float)TOTAL_ALIENS))*INVADERS_SPEED);              
              if(Player.AliensDestroyed==TOTAL_ALIENS-2)
                if(AlienXMoveAmount>0)
                  AlienXMoveAmount=ALIEN_X_MOVE_AMOUNT*2;
                else
                  AlienXMoveAmount=-(ALIEN_X_MOVE_AMOUNT*2);
              if(Player.AliensDestroyed==TOTAL_ALIENS-1)
                if(AlienXMoveAmount>0)
                  AlienXMoveAmount=ALIEN_X_MOVE_AMOUNT*4;
                else
                  AlienXMoveAmount=-(ALIEN_X_MOVE_AMOUNT*4);
                  
              if(Player.AliensDestroyed==TOTAL_ALIENS)
                NextLevel(&Player);            
          }
        }
          if(Alien[across][down].Ord.Status==ACTIVE)
        {
          if(Collision(Player.Ord,TANKGFX_WIDTH,TANKGFX_HEIGHT,Alien[across][down].Ord,AlienWidth[down],ALIEN_HEIGHT))
             PlayerHit();  
          else 
          {
             if(Alien[across][down].Ord.Y+8>SCREEN_HEIGHT)
                 PlayerHit();                   
          }
        }
      }
    }
  }
}
 
bool Collision(GameObjectStruct Obj1,unsigned char Width1,unsigned char Height1,GameObjectStruct Obj2,unsigned char Width2,unsigned char Height2)
{
  return ((Obj1.X+Width1>Obj2.X)&(Obj1.X<Obj2.X+Width2)&(Obj1.Y+Height1>Obj2.Y)&(Obj1.Y<Obj2.Y+Height2));
}
 
int RightMostPos()  {
  int Across=NUM_ALIEN_COLUMNS-1;
  int Down;
  int Largest=0;
  int RightPos;
  while(Across>=0){
    Down=0;
    while(Down<NUM_ALIEN_ROWS){
      if(Alien[Across][Down].Ord.Status==ACTIVE)
      {
        RightPos= Alien[Across][Down].Ord.X+AlienWidth[Down];
        if(RightPos>Largest)
          Largest=RightPos;
      }
      Down++;
    }
    if(Largest>0)
      return Largest;
    Across--;
  }
  return 0;
}
 
int LeftMostPos()  {
  int Across=0;
  int Down;
  int Smallest=SCREEN_WIDTH*2;
  while(Across<NUM_ALIEN_COLUMNS){
    Down=0;
    while(Down<NUM_ALIEN_ROWS){
      if(Alien[Across][Down].Ord.Status==ACTIVE)
        if(Alien[Across][Down].Ord.X<Smallest)
          Smallest=Alien[Across][Down].Ord.X;
      Down++;
    }
    if(Smallest<SCREEN_WIDTH*2)
      return Smallest;
    Across++;
  }
  return 0;
}
 
void UpdateDisplay()  
{
  int i;
  
  display.clearBuffer();
  
  if(MotherShipBonusCounter>0)
  {
    print_int(MotherShipBonusXPos, 0, MotherShipBonus, 3);
    MotherShipBonusCounter--;   
  }

    print_int(0, 0, Player.Score, 5);                          
    print_int(0, 20, HiScore, 5);
    print_int(125, 0, Player.Lives, 1);
    display.drawXBMP(115, 2, TANKGFX_WIDTH, TANKGFX_HEIGHT, TankGfx );
    display.drawVLine(20,0,64 );
    display.drawVLine(109,0,64 );

    print_int(0, 59, framesPerSecond, 3);

    frameCount ++;
    if ((millis() - lastMillis) > (1000)) {
      framesPerSecond = (frameCount );
      frameCount = 0;
      lastMillis = millis();
    }

  for(i=0;i<MAXBOMBS;i++)  {
      if(AlienBomb[i].Status==ACTIVE)
        display.drawXBMP(AlienBomb[i].X, AlienBomb[i].Y, 2, 4 , AlienBombGfx);
      else  {
        if(AlienBomb[i].Status==EXPLODING)
         display.drawXBMP(AlienBomb[i].X-4, AlienBomb[i].Y, 5, 3, ExplosionGfx );
        AlienBomb[i].Status=DESTROYED;
      }
  }
  
  for(int across=0;across<NUM_ALIEN_COLUMNS;across++)
  {
    for(int down=0;down<NUM_ALIEN_ROWS;down++)
    {
      if(Alien[across][down].Ord.Status==ACTIVE){

        switch(down)  {
          case 0: 
            if(AnimationFrame)
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderTopGfx );
            else
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderTopGfx2 );
            break;
          case 1: 
            if(AnimationFrame)
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT,  InvaderMiddleGfx );
            else
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT,  InvaderMiddleGfx2 );
            break;
          case 2: 
            if(AnimationFrame)
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderMiddleGfx );
            else
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderMiddleGfx2);
            break;
          case 3: 
            if(AnimationFrame)
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderBottomGfx );
            else
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderBottomGfx2 );
          case 4: 
            if(AnimationFrame)
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderBottomGfx );
            else
              display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, AlienWidth[down],INVADER_HEIGHT, InvaderBottomGfx2 );
        }
      }
      else  {
        if(Alien[across][down].Ord.Status==EXPLODING){
          Alien[across][down].ExplosionGfxCounter--;
          if(Alien[across][down].ExplosionGfxCounter>0)  {
            tone(10, Alien[across][down].ExplosionGfxCounter*100,100);
            display.drawXBMP(Alien[across][down].Ord.X, Alien[across][down].Ord.Y, 5, 3,  ExplosionGfx );
          }
          else
            Alien[across][down].Ord.Status=DESTROYED;
        }
      }
    }
  }  
  
  if(Player.Ord.Status==ACTIVE)
    display.drawXBMP(Player.Ord.X, Player.Ord.Y, TANKGFX_WIDTH, TANKGFX_HEIGHT,  TankGfx );
  else {    
    if(Player.Ord.Status==EXPLODING)  {
        display.drawXBMP(Player.Ord.X, Player.Ord.Y, 5, 3,  ExplosionGfx );
        tone(10, Player.ExplosionGfxCounter*250,50);
      Player.ExplosionGfxCounter--;
      if(Player.ExplosionGfxCounter==0)  {
        Player.Ord.Status=DESTROYED;
        LoseLife();
      }
    }
  }
  if(Missile.Status==ACTIVE)
    display.drawXBMP(Missile.X, Missile.Y, MISSILE_WIDTH, MISSILE_HEIGHT , MissileGfx);
 
  if(MotherShip.Ord.Status==ACTIVE)
    display.drawXBMP(MotherShip.Ord.X, MotherShip.Ord.Y, MOTHERSHIP_WIDTH, MOTHERSHIP_HEIGHT, MotherShipGfx );
  else
  {
    if(MotherShip.Ord.Status==EXPLODING)  
    {
      tone(10, MotherShip.ExplosionGfxCounter*250,50);
      MotherShip.ExplosionGfxCounter--;
      if(MotherShip.ExplosionGfxCounter==0)  {
        MotherShip.Ord.Status=DESTROYED;
      }
    }
  }
  
  for(i=0; i <NUM_BASES; i++)  {    
    if (Base[i].Ord.Status == ACTIVE)
      display.drawXBM(Base[i].Ord.X, Base[i].Ord.Y, BASE_WIDTH, BASE_HEIGHT, Base[i].Gfx );
  }
  display.sendBuffer();
}
 
void LoseLife()  {
  Player.Lives--;
  if(Player.Lives>0)  {
    DisplayPlayerAndLives(&Player);
    for(int i=0;i<MAXBOMBS;i++)  {
      AlienBomb[i].Status=DESTROYED;
      AlienBomb[i].Y=0;
    }
    Player.Ord.Status=ACTIVE;
    Player.Ord.X=PLAYER_X_START;
  }
  else  {
    GameOver();
  }
}
 
 
void GameOver()  {  
  GameInPlay=false;
  display.clearBuffer();
  CentreText("Player 1",8); 
  CentreText("Game Over",20);   
  CentreText("Score     ",32);   
  display.setCursor(72,32);
  display.print(Player.Score);
  if(Player.Score>HiScore)
  {
    CentreText("NEW HIGH SCORE!!!",44);
    CentreText("**CONGRATULATIONS**",56);    
  }
  display.sendBuffer();
  if(Player.Score>HiScore){    
    HiScore=Player.Score;
    EEPROM.put(0,HiScore);
    PlayRewardMusic();
  }
  delay(2500);  
}
 
 
void PlayRewardMusic()
{
  unsigned char Notes[] = { 26, 20, 18, 22, 20, 0, 26, 0, 26 };
  unsigned char NoteDurations[] = { 40, 20, 20, 40, 30, 50, 30, 10,30 };
  for(int i=0;i<9;i++)
  {
    tone(10, Notes[i]*10);
    delay(NoteDurations[i]*10);  
    noTone(10);                 
    delay(20);                  
  }
   noTone(10);
}
 
 
void DisplayPlayerAndLives(PlayerStruct *Player)  {
  display.clearBuffer();
  CentreText("  Player 1",8);
  CentreText("Score ",20);   
  display.print(Player->Score);
  CentreText("Lives ",32);   
  display.print(Player->Lives); 
  CentreText("Level ",44);   
  display.print(Player->Level);   
  display.sendBuffer();
  delay(2000);
  Player->Ord.X=PLAYER_X_START;
}
 
 
void CentreText(const char *Text,unsigned char Y)  {
  display.setCursor(int((SCREEN_WIDTH - display.getStrWidth(Text)) / 2.0), Y);
  display.print(Text);
}
 
 
void InitPlayer()  {
  Player.Ord.Y=PLAYER_Y_START;
  Player.Ord.X=PLAYER_X_START;
  Player.Ord.Status=ACTIVE;
  Player.Lives=LIVES;
  Player.Level=0;
  Missile.Status=DESTROYED;
  Player.Score=0;
}
 
 
void NextLevel(PlayerStruct *Player)  {

  int YStart;
  for(int i=0;i<MAXBOMBS;i++)  
    AlienBomb[i].Status=DESTROYED;
    AnimationFrame=false;
    Player->Level++;
    YStart=(MOTHERSHIP_HEIGHT +2) + ((Player->Level-1) % LEVEL_TO_RESET_TO_START_HEIGHT)*AMOUNT_TO_DROP_BY_PER_LEVEL;
    InitAliens(YStart);  
    AlienXMoveAmount=ALIEN_X_MOVE_AMOUNT;
    Player->AlienSpeed=INVADERS_SPEED;
    Player->AliensDestroyed=0;
    MotherShip.Ord.X=-MOTHERSHIP_WIDTH;
    MotherShip.Ord.Status=DESTROYED; 
    Missile.Status=DESTROYED;
    randomSeed(100);     
    InitBases();
    DisplayPlayerAndLives(Player);  
    MusicIndex=0;
    MusicCounter=NOTELENGTH;
}
 
 
void InitBases()  {
  
  byte TheByte;
  int Spacing=((SCREEN_WIDTH-32)-(NUM_BASES*BASE_WIDTH))/NUM_BASES;
  for(int i=0;i<NUM_BASES;i++)  
  {    
    for(int DataIdx=0;DataIdx<BASE_HEIGHT;DataIdx++)  
    {
        TheByte = pgm_read_byte(BaseGfx + DataIdx);
        Base[i].Gfx[DataIdx]=TheByte;
    }
    Base[i].Ord.X=17+(i*Spacing)+(i*BASE_WIDTH)+(Spacing/2);
    Base[i].Ord.Y=BASE_Y;
    Base[i].Ord.Status=ACTIVE;
  }
}
 
 
void NewGame(){
  InitPlayer();
  NextLevel(&Player);
}
 
void InitAliens(int YStart)  { 
  for(int across=0;across<NUM_ALIEN_COLUMNS;across++)  {
    for(int down=0;down<NUM_ALIEN_ROWS;down++)  {
      Alien[across][down].Ord.X=X_START_OFFSET+(across*(LARGEST_ALIEN_WIDTH+SPACE_BETWEEN_ALIEN_COLUMNS))-(AlienWidth[down]/2);  
      Alien[across][down].Ord.Y=YStart+(down*SPACE_BETWEEN_ROWS);
      Alien[across][down].Ord.Status=ACTIVE;
      Alien[across][down].ExplosionGfxCounter=EXPLOSION_GFX_TIME;
    }
  }
  MotherShip.Ord.Y=0;
  MotherShip.Ord.X=-MOTHERSHIP_WIDTH;
  MotherShip.Ord.Status=DESTROYED; 
}



© digitrode.ru


Теги: Arduino, игры, дисплеи




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий