O jogo mais simples do Arduino com tela 1602 - Parte # 2

PARTE # 2 do início ao fim


Continuamos a fazer um jogo no arduino e depois colocamos esse jogo no programa que estou fazendo para o carro e, com base em nosso conhecimento, criaremos um segundo jogo para se divertir e fazer a música certa.

Para entender o que precisamos escrever agora, precisamos fazer um plano para o que teremos e de que forma. Nós temos o personagem principal, ele tem dois quadros de animação, e nós já fizemos isso na última lição. Eu não comecei a mudar o sprite de salto para ele, não porque eu estava com preguiça, mas ainda não entendi o motivo. Em seguida, devemos ter uma designação de vida, apenas fazer corações seguidos não é interessante, e os números também são triviais, vamos fazer três pontos de vida e marcá-los com, por exemplo, o nível da bateria. E para a alegria dos olhos, um coração bate perto dessa bateria. O número de pontos por rodada, definido à esquerda da tela, puramente digital e o inimigo, teremos um cacto do mal, para começar.

Decidimos sobre os objetos e vamos desenhá-los, imediatamente todos os sprites e anotá-los.





Temos sprites de trabalho e já podemos imaginar como ficará na tela. Após a primeira lição, escreveremos no sistema binário, lembramos onde o zero está vazio e onde está a unidade, nosso pixel queima. Prossiga:

"--------------------------------------------------------------------------" //   : byte Player_1[SYMBOL_HEIGHT] = {B01110,B01110,B00100,B01110,B10101,B00100,B01110,B01010,}; //   : byte Player_2[SYMBOL_HEIGHT] = {B00000,B01110,B01110,B00100,B11111,B00100,B01110,B01010,}; // : byte Enemy_1[SYMBOL_HEIGHT] = {B00010,B00110,B10111,B10110,B11111,B00110,B00110,B11111,}; // : byte Heart_L[SYMBOL_HEIGHT] = {B00000,B01010,B11111,B11111,B11111,B01110,B00100,B00000,}; // : byte Heart_R[SYMBOL_HEIGHT] = {B00000,B00000,B01010,B11111,B01110,B00100,B00000,B00000,}; //  : byte Battery1[SYMBOL_HEIGHT] = {B01110,B11111,B11111,B11111,B11111,B11111,B11111,B11111,}; //  : byte Battery2[SYMBOL_HEIGHT] = {B01110,B10001,B10011,B10111,B11111,B11111,B11111,B11111,}; //  : byte Battery3[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10011,B10111,B11111,B11111,}; //  : byte Battery4[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10001,B10001,B10001,B11111,}; "--------------------------------------------------------------------------" 

Agora temos sprites, e é hora de reviver tudo. Primeiro, vamos pensar sobre quais recursos adicionais precisamos. Sabendo que o arduino não funciona perfeitamente e, em alguns lugares, é até muito caprichoso, começamos a tentar simplificar o máximo possível a vida desse microcontrolador. Não sobrecarregue e, ao mesmo tempo, exija retorno total. Então, introduzimos essas funções adicionais que viverão suas próprias vidas e farão tudo o que precisamos:

- animação do coração
- animação de herói
- verificação de dano do herói
- movimento do cacto do mal
- acumulação de pontos (por enquanto, a cada segundo +1 e depois mudar)
- atualizando a tela (mas isso não é exato, provavelmente removeremos essa função e adicionaremos outra, não gostei da tela piscando, quero estabilidade). Posteriormente, substituímos essa função pela exclusão da localização anterior do herói, isso removerá a cintilação real da tela e zerar o vilão estará dentro do script maligno, acho que haverá uma ou duas linhas lá.
- controle remoto
- configurações de loop e configuração

Queremos que tenhamos uma animação do batimento cardíaco. Trazendo-o para uma função separada e forçando-nos a viver nossa própria vida separada, será mais fácil acompanhar o trabalho e, no futuro, será mais fácil editar, pois temos tudo, bem ou quase tudo em um só lugar. Esse código pode ser exibido em loop () e comentado, mas eu pessoalmente o dividia em funções separadas, você não está procurando o código na lista inteira e sabe que uma função separada controla elementos individuais do nosso jogo.

Agora, vou escrever trechos de código, conectá-los no final e obter um script completo, agora explicarei a essência e a idéia para você e, em seguida, montaremos o quebra-cabeça e apreciaremos o resultado.
Antes de iniciar o próprio código, explicarei alguns pontos.

lcd.createChar // este é um comando para acessar a tela e usar uma das células da memória para registrar novos caracteres. O número da célula e, separado por vírgula, o nome da variável com informações, são escritos entre colchetes.

Controlaremos a renderização por meio de uma variável digital, usando quatro dígitos, para que a animação seja correta. Se quiséssemos fazer o coração bater aqui e ali, a variável bool comum nos serviria. Mas minha idéia é diferente, um grande golpe e dois curtos, então parecerá mais interessante.

 "--------------------------------------------------------------------------" void HeartHit () //    { if (HeartControl == 0 || HeartControl == 2){lcd.createChar(1, Heart_L);} //      ,        if (HeartControl == 1 || HeartControl == 3){lcd.createChar(1, Heart_R);} //      ,        if (currentMillis - HeartHitBigCheck >= HeartHitBig) { //     if (currentMillis - HeartHitLightCheck >= HeartHitLight) { //    HeartHitLightCheck = currentMillis; //      if (HeartControl<3){HeartControl++;} //      ,           else {HeartControl = 0; HeartHitBigCheck = currentMillis;} //     ,          } } } "--------------------------------------------------------------------------" 

Mais uma vez, quero focar sua atenção neste código:
lcd.createChar (x, y); atribuição à célula de memória “x” a partir de (0 ... 7) dados para exibição na tela “y”

Vá em frente =)

Agora, temos um código que cria o efeito de um batimento cardíaco interessante, ele não faz nada de útil, apenas mostra =)

Além disso, com base nisso, criaremos uma animação de nosso herói, haverá enormes vantagens, quanto mais aprofundarmos este artigo, mais você aprenderá meu estilo de pensar e, para mim, menos precisará explicar e se concentrar mais no código e na construção da ideia. Vou tentar explicar mais nos comentários do script para escrever menos fora do script.
Então, vamos começar o personagem principal e criar outra função para isso:

 "--------------------------------------------------------------------------" //             ().     =)  ,         . void PlAn () //     { If (JumpB == true && GGpozY == 0){ //    (      )   =     . if (currentMillis - JumpUPCheck >= JumpUP) { //     0.8f JumpB = false; GGclear (); GGpozY = 1; //   ,         .  = ;      ();     =  ; } } if (AnimPlayer == 1){lcd.createChar(0, Player_1);} //    ,        if (AnimPlayer == 2){lcd.createChar(0, Player_2);} //    ,        if (AnimPlayer < 2) //    ,     ,            { lcd.setCursor(GGpozX, GGpozY); //    lcd.write(0); //  } if (currentMillis - AnimatedTimeCheck >= AnimatedTime) { //   AnimatedTimeCheck = currentMillis; //   if (AnimPlayer == 2){AnimPlayer = 1;} //        else{AnimPlayer = 2;} //   ,  . } } void GGclear () //    { lcd.setCursor(GGpozX, GGpozY); //   lcd.print(" "); //  } "--------------------------------------------------------------------------" 

Agora, o cronômetro, hum, ou melhor, nossos pontos que serão concedidos, vamos apenas escrever um cronômetro e assumir que esses são pontos.

 "--------------------------------------------------------------------------" void timer () //   { if (currentMillis - DHTTimeRCheck >= DHTTimeR) //       { DHTTimeRCheck = currentMillis; //   Timer_z ++; //      lcd.setCursor(0, 0); //       lcd.print(Timer_z); //     } } --------------------------------------------------------------------------" 

Isso é tudo. Quanto mais longe, mais fácil.

Agora, resta trabalhar o nosso cacto.

Sua tarefa é simples, parecer, percorrer todo o caminho da direita para a esquerda e tentar tocar o herói para causar danos. Com o dano, tudo fica mais fácil, um toque - um golpe. Até agora, aumentando a complexidade, não faremos. Deixe o cacto se mover a uma velocidade de 0,5f (complexidade, essa já será sua tarefa =)) ou, em russo, meio segundo é um passo.

Vamos dar uma olhada em como esse pedaço de código ficará:

 "--------------------------------------------------------------------------" void enemy_go () //   { if (Emeny_check_1 == 0) //       ,            ,        ,            { Emeny_control = random (100); //  ,     ,    ,  ,    ,   . if (Emeny_control == 1) { //   = 1  100   . Emeny_check_1 = 1; //  ,  ,    bool      ,    ,       hitON = false; //          } } if (Emeny_check_1 == 1) //   ,     { if (currentMillis - TimeBlinkCheck >= TimeBlink) //  0.5f { TimeBlinkCheck = currentMillis; //   lcd.createChar(2, Enemy_1); //   2   lcd.setCursor(E1pozX, E1pozY); // 1   lcd.print(" "); //  E1pozX--; //     lcd.setCursor(E1pozX, E1pozY); // 2  lcd.write(2); //  if (E1pozX <= 0) //      { lcd.setCursor(0,1); //     lcd.print(" "); //  Emeny_control = 0; //   Emeny_check_1 = 0; //      E1pozX = 16; // - \/ \/ \/ E1pozY = 1; // -         } } } } "--------------------------------------------------------------------------" 

Permanece um pouco e, após o teste de cada peça, já posso definir o script completo montado, configurado e pronto para o teste em seus dispositivos.

Portanto, agora temos um dos scripts mais importantes em linha, este é um script de verificação de danos e a inclusão de um jogador na ausência de vida. Não há nada sobrenatural no script. Portanto, podemos começar a desmontá-lo (a propósito, se você fosse cuidadoso, perceberia que quando criamos o coração, não o exibimos na tela, então agora você verá onde coloquei essa parte do código):

 "--------------------------------------------------------------------------" void check_hit_gg_1 () //   { if (E1pozX == GGpozX && E1pozY == GGpozY && hitON == false){ //     Y ,   ,    LifeCheck -= 1; //    hitON = true; //           if (LifeCheck <= 0){ //      AnimPlayer = 50; //  loop () Emeny_check_1 = 50; //     lcd.clear(); //  lcd.setCursor(3, 0); //  lcd.print("GAME OVER"); //    } } else { // !         … lcd.setCursor(13, 0); //    … lcd.write(1); //   lcd.setCursor(14, 0); lcd.print("="); //    lcd.setCursor(15, 0); lcd.write(3); //   } } "--------------------------------------------------------------------------" 

Este código é muito simples, sua principal função é verificar e aguardar até que todas as condições sejam atendidas para interromper o jogo e nos dizer que perdemos.

Agora, a última função é gerenciamento. De fato, se analisarmos o código acima, parecerá mais fácil do que simples para nós. Desde a primeira lição, retiramos os códigos do controle remoto, escrevi todos eles, eles ficam assim:

* CH- 0xFFA25D
* CH 0xFF629D
* CH+ 0xFFE21D
* << 0xFF22DD
* >> 0xFF02FD
* >|| 0xFFC23D
* - 0xFFE01F
* + 0xFFA857
* EQ 0xFF906F
* 0 0xFF6897
* 100+ 0xFF9867
* 200+ 0xFFB04F
* 1 0xFF30CF
* 2 0xFF18E7
* 3 0xFF7A85
* 4 0xFF10EF
* 5 0xFF38C7
* 6 0xFF5AA5
* 7 0xFF42BD
* 8 0xFF4AB5
* 9 0xFF52AD


Código do botão _ (atenção!) (Os códigos do controle remoto podem variar)

Quem não leu, aconselho a ler a 1ª parte.

Algo semelhante, você o terá e poderá configurar facilmente tudo o que precisa.

Agora vamos criar o algoritmo mais simples, com base no que já sabemos e nosso jogo ganhará vida.

 "--------------------------------------------------------------------------" void IRCheck () //    { if ( irrecv.decode( &results )) //   ,  { if (results.value == 0xFF18E7 && GGpozY == 1){ //    « 2 »    «» GGclear (); //     GGpozY = 0; //    2  ( ) JumpB = true; //        JumpUPCheck = currentMillis; //       } // 2 if (results.value == 0xFF10EF && GGpozX >= 0){ //             GGclear (); //      GGpozX -= 1; //     } // 4 if (results.value == 0xFF5AA5 && GGpozX <= 15){ //          GGclear (); //    GGpozX += 1; //   } // 6 if (results.value == 0xFF6897){ // 0 //  ,    … lcd.clear(); //   AnimPlayer = 1; //    LifeCheck = 3; //   Timer_z = 0; //   GGpozX = 8; // \/ \/ \/ GGpozY = 1; //       Emeny_check_1 = 0; //     E1pozX = 16; // \/ \/ \/ E1pozY = 1; //    . } irrecv.resume(); //    } "--------------------------------------------------------------------------" 

Conclusão do código escrito acima:
botão 2 é um salto
botão 4 é um passo para a esquerda
botão 6 é um passo para a direita
o botão 0 reinicia o jogo e o reinicia

Agora, resta para nós configurar o setup & loop, tudo já está chegando ao fim. Criamos todas as funções adicionais e tudo o que nos restava era colar e adicionar todas as bibliotecas. Eu acho que veremos as variáveis ​​e as principais configurações de configuração \ loop já no código geral, então vamos começar, e então você precisa ctrl + c & ctrl + v e isso é tudo =) e mais desenvolvimento independente nessa direção, se é claro que você é Eu gostei

 "--------------------------------------------------------------------------" #include <IRremote.h> //   #include <Wire.h> // i2P  #include <LiquidCrystal_I2C.h> //  1602 LiquidCrystal_I2C lcd(0x3F,16,2); //   int AnimPlayer = 1; //   int GGpozX = 8; //     int GGpozY = 1; //     int Emeny_check_1 = 0; //   int Emeny_control = 0; //           int E1pozX = 16; //    int E1pozY = 1; //    int HeartControl = 0; //    int LifeCheck = 3; //   long Timer_z = 0; //   long AnimatedTime = 300; //     long AnimatedTimeCheck = 0; //   long HeartHitBig = 800; //     long HeartHitLight = 250; //     long HeartHitBigCheck = 0; //     long HeartHitLightCheck = 0; //     long BatteyBlink = 200; //      1  long BatteyBlinkCheck = 0; //   long JumpUP = 800; //     long JumpUPCheck = 0; //   long DHTTimeR = 1000; //  long DHTTimeRCheck = 0; //   long TimeBlink = 500; //    long TimeBlinkCheck = 0; //   long currentMillis = 0; //    bool JumpB = false; //    bool BatteryB = false; //     bool hitON = false; //    decode_results results; //      IRrecv irrecv(A0); //      enum { SYMBOL_HEIGHT = 8 }; byte Player_1[SYMBOL_HEIGHT] = {B01110,B01110,B00100,B01110,B10101,B00100,B01110,B01010,}; byte Player_2[SYMBOL_HEIGHT] = {B00000,B01110,B01110,B00100,B11111,B00100,B01110,B01010,}; byte Enemy_1[SYMBOL_HEIGHT] = {B00010,B00110,B10111,B10110,B11111,B00110,B00110,B11111,}; byte Heart_L[SYMBOL_HEIGHT] = {B00000,B01010,B11111,B11111,B11111,B01110,B00100,B00000,}; byte Heart_R[SYMBOL_HEIGHT] = {B00000,B00000,B01010,B11111,B01110,B00100,B00000,B00000,}; byte Battery1[SYMBOL_HEIGHT] = {B01110,B11111,B11111,B11111,B11111,B11111,B11111,B11111,}; byte Battery2[SYMBOL_HEIGHT] = {B01110,B10001,B10011,B10111,B11111,B11111,B11111,B11111,}; byte Battery3[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10011,B10111,B11111,B11111,}; byte Battery4[SYMBOL_HEIGHT] = {B01110,B10001,B10001,B10001,B10001,B10001,B10001,B11111,}; void setup() { Serial.begin(9600); //   irrecv.enableIRIn(); //   lcd.init(); //   Wire.begin(); //   lcd.backlight();//    } void loop() { currentMillis = millis(); //   IRCheck (); //    if (AnimPlayer < 3){ //   ,   ,    if (LifeCheck == 3) {lcd.createChar(3, Battery1);} //   if (LifeCheck == 2) {lcd.createChar(3, Battery2);} //   if (LifeCheck == 1) {//  ,  1   if (BatteryB == false){lcd.createChar(3, Battery3);} //    if (BatteryB == true){lcd.createChar(3, Battery4);} //   if (currentMillis - BatteyBlinkCheck >= BatteyBlink) {BatteyBlinkCheck = currentMillis; //  if (BatteryB == false) {BatteryB = true;} else {BatteryB = false;}} //  } timer(); //  check_hit_gg_1 (); //   PlAn(); //   HeartHit (); //    enemy_go(); //   } } void IRCheck () //    { if ( irrecv.decode( &results )) //   ,  { if (results.value == 0xFF18E7 && GGpozY == 1){ //    « 2 »    «» GGclear (); //     GGpozY = 0; //    2  ( ) JumpB = true; //        JumpUPCheck = currentMillis; //       } // 2 if (results.value == 0xFF10EF && GGpozX >= 0){ //             GGclear (); //      GGpozX -= 1; //     } // 4 if (results.value == 0xFF5AA5 && GGpozX <= 15){ //          GGclear (); //    GGpozX += 1; //   } // 6 if (results.value == 0xFF6897){ // 0 //  ,    … lcd.clear(); //   AnimPlayer = 1; //    LifeCheck = 3; //   Timer_z = 0; //   GGpozX = 8; // \/ \/ \/ GGpozY = 1; //       Emeny_check_1 = 0; //     E1pozX = 16; // \/ \/ \/ E1pozY = 1; //    . } irrecv.resume(); //    } } void timer () //   { if (currentMillis - DHTTimeRCheck >= DHTTimeR) //       { DHTTimeRCheck = currentMillis; //   Timer_z ++; //      lcd.setCursor(0, 0); //       lcd.print(Timer_z); //     } } //             ().     =)  ,         . void PlAn () //     { if (JumpB == true && GGpozY == 0){ //    (      )   =     . if (currentMillis - JumpUPCheck >= JumpUP) { //     0.8f JumpB = false; GGclear (); GGpozY = 1; //   ,         .  = ;      ();     =  ; } } if (AnimPlayer == 1){lcd.createChar(0, Player_1);} //    ,        if (AnimPlayer == 2){lcd.createChar(0, Player_2);} //    ,        if (AnimPlayer < 2) //    ,     ,            { lcd.setCursor(GGpozX, GGpozY); //    lcd.write(0); //  } if (currentMillis - AnimatedTimeCheck >= AnimatedTime) { //   AnimatedTimeCheck = currentMillis; //   if (AnimPlayer == 2){AnimPlayer = 1;} //        else{AnimPlayer = 2;} //   ,  . } } void GGclear () //    { lcd.setCursor(GGpozX, GGpozY); //   lcd.print(" "); //  } void enemy_go () //   { if (Emeny_check_1 == 0) //       ,            ,        ,            { Emeny_control = random (100); //  ,     ,    ,  ,    ,   . if (Emeny_control == 1) { //   = 1  100   . Emeny_check_1 = 1; //  ,  ,    bool      ,    ,       hitON = false; //          } } if (Emeny_check_1 == 1) //   ,     { if (currentMillis - TimeBlinkCheck >= TimeBlink) //  0.5f { TimeBlinkCheck = currentMillis; //   lcd.createChar(2, Enemy_1); //   2   lcd.setCursor(E1pozX, E1pozY); // 1   lcd.print(" "); //  E1pozX--; //     lcd.setCursor(E1pozX, E1pozY); // 2  lcd.write(2); //  if (E1pozX <= 0) //      { lcd.setCursor(0,1); //     lcd.print(" "); //  Emeny_control = 0; //   Emeny_check_1 = 0; //      E1pozX = 16; // - \/ \/ \/ E1pozY = 1; // -         } } } } void check_hit_gg_1 () //   { if (E1pozX == GGpozX && E1pozY == GGpozY && hitON == false){ //     Y ,   ,    LifeCheck -= 1; //    hitON = true; //           if (LifeCheck <= 0){ //      AnimPlayer = 50; //  loop () Emeny_check_1 = 50; //     lcd.clear(); //  lcd.setCursor(3, 0); //  lcd.print("GAME OVER"); //    } } else { // !         … lcd.setCursor(13, 0); //    … lcd.write(1); //   lcd.setCursor(14, 0); lcd.print("="); //    lcd.setCursor(15, 0); lcd.write(3); //   } } void HeartHit () //    { if (HeartControl == 0 || HeartControl == 2){lcd.createChar(1, Heart_L);} //      ,        if (HeartControl == 1 || HeartControl == 3){lcd.createChar(1, Heart_R);} //      ,        if (currentMillis - HeartHitBigCheck >= HeartHitBig) { //     if (currentMillis - HeartHitLightCheck >= HeartHitLight) { //    HeartHitLightCheck = currentMillis; //      if (HeartControl<3){HeartControl++;} //      ,           else {HeartControl = 0; HeartHitBigCheck = currentMillis;} //     ,          } } } "--------------------------------------------------------------------------" 

Conectando. Criamos o código, mas aqui está como coletar os fios e o que e onde colar, eu não expliquei isso para você. Embora eu tenha certeza de que mais de 80% dos que decidem ler este artigo já sabem disso, mas quanto a mim, o artigo não estará completo se eu não fornecer o máximo de informações.



A5 Display 1602 - SCL
A4 Display 1602 -
Sensor SDA A0 IR

Agora, estou trabalhando na criação de uma tecla de rádio para um carro baseado em arduino, e na tela 1602 quero exibir os dados do sensor de umidade e temperatura fora do carro (já que estarei em Moscou em breve, procurando um novo emprego, precisarei saber o que está acontecendo do lado de fora do carro está muito frio lá), um relógio, um voltímetro de bateria e coloco jogos lá (eu ainda tenho 4 pinos livres, mas ainda preciso pressioná-los lá) para poder cutucar este jogo ou o segundo que planejo em um semáforo fazer como correr velho bom bolso prist 2000 ca de onde o ponto foi apenas para contornar o obstáculo e música para colocar em um fundo do Rock n Roll Racing. Ugh classic =) e cole tudo de maneira ideal em um arduino sem usar armazenamento de dados externo (puro desafio), mas eu já usei ~ 60% e se o código do jogo levar no máximo de 15% a 20%,então a música ... ah ... haverá problemas com isso, pesa muito, precisa ser otimizada, já comecei a coletar a faixa e consegui reduzir o peso em quase 10 vezes, mas cometi erros na construção de notas e tons e agora tenho que fazer tudo de novo. Provavelmente, escreverei aqui a versão completa com uma descrição do projeto em que estou trabalhando atualmente. Espero que alguém tenha me dado algo novo em que pensar, se tudo estiver bem e na hora certa, continuarei escrevendo artigos. Alguma pergunta? Escreva nos comentários.Espero que alguém tenha me dado algo novo em que pensar, se tudo estiver bem e na hora certa, continuarei escrevendo artigos. Alguma pergunta? Escreva nos comentários.Espero que alguém tenha me dado algo novo em que pensar, se tudo estiver bem e com o tempo, continuarei escrevendo artigos. Alguma pergunta? Escreva nos comentários.

Todos os inscritos, como, seguem os novos artigos.

Obrigado a todos pela atenção, ciao cacau!

PS: Ainda não consigo gravar o desempenho do jogo em vídeo, a câmera do telefone morreu e é muito triste. Mas vou criar uma coisa e adicionar um vídeo mais tarde ao artigo.

A primeira parte do artigo ->

Source: https://habr.com/ru/post/pt425367/


All Articles