Como utilizar Processing com Arduino: Parte 3

Processing-com-Arduino
Font Size

Olá, tudo bem? Hoje iremos finalizar a nossa série de posts sobre como utilizar a plataforma Processing com o  Arduino. Para isso, hoje utilizaremos o conceito de PWM representado pelos dois potenciômetros do nosso projeto. Mas o que construiremos hoje? Continue aqui que já já você irá descobrir!

Conhecendo o Projeto

No projeto de hoje, desenvolveremos um jogo muito conhecido pelo pessoal da velha guarda, você sabia que esse jogo foi um dos primeiros e mais lucrativos jogos da história? Legal né? Então por isso, hoje vamos construir o jogo: Pong.

Tela do Jogo Pong

Mas antes de começar, eu preciso dizer que você necessita ter visto os dois últimos posts sobre o  Processing. Isso porque nesses posts anteriores, eu abordei funcionalidade e conceitos de programação as quais não falarei novamente. Então logo abaixo estão os links dos posts anteriores, beleza?

Este jogo foi lançado na plataforma Atari, um dos mais comercializados do mundo, nesse console, as pessoas jogavam esse jogo com um joystick. Mas hoje, vamos utilizar potenciômetros para representar a movimentação da peça, já que ela se movimenta na vertical apenas.

Por este motivo, quando estamos trabalhando com potenciômetro, também estamos trabalhando com o conceito de PWM. Então você precisa estar familiarizado com esse conceito. Para lhe ajudar, vou deixarei abaixo o link do post sobre Servomotores, onde abordamos sobre PWM, show?

Componentes do Projeto

Vamos então dar início ao nosso projeto de hoje, para isso, você necessita dos seguintes componentes:

Esquema de Ligação

Agora, realize as ligações conforme a figura abaixo. Fique ligado com as entradas analógicas para não ligar errado, combinado?

Esquema de Ligação no Arduino

Código do Projeto

Vamos agora colocar para funcionar o nosso projeto, para isso, primeiro abra a sua IDE do Arduino e compile o seguinte código:

/* ====================================================================
Projeto: Jogo PONG com Arduino e Processing
Autor: Danilo Nogueira
Data: 05/02/2019
Domínio Público.
// ================================================================ */
// --- Definindo as Variáveis ---
 #define pino_Pot1 A0
 #define pino_Pot2 A2

// --- Definindo o setup() ---
void setup(){
  Serial.begin(9600);
}

// --- Definindo o loop() ---
void loop(){
  String val_pot = "";
  val_pot += analogRead(pino_Pot1);
  val_pot += '-';
  val_pot += analogRead(pino_Pot2);
  Serial.println(val_pot);
  //delay(10);
}
// ================================== FIM ==============================

Depois de compilar, abra a sua serial e movimente os potenciômetros. Se estiver aparecendo os valores, tudo certo. Caso não esteja comente o problema abaixo! Feito isso, abra o Processing e copie o código abaixo:

/* ========================================================================================================
Projeto: Jogo PONG com Arduino e Processing
Autor: Danilo Nogueira
Data: 05/02/2019
Domínio Público.
// ======================================================================================================== */
// --- Definindo as Variáveis ---
import processing.serial.*;  // Ativando a comunicação serial entre o Arduino e o Processing
Serial Porta;                // Porta COM que o Arduino está conectado.

// Variáveis para configurar o tamanho das barras e da bola do jogo
int[] position;       // Vetor para controlar cada posição dos potenciômetros
int alt_barra = 100;  // Altura da barra que é o jogador.
int larg_barra = 20;  // Largura dessa barra.
int dim_bola = 20;    // Tamanho da bola.

// Variáveis para alterar a posição dos jogadores e da bola na tela.
float pos_pot1,pos_pot2;  // Armazenar a posição do jogador na tela.
float x,y,v_x,v_y;        // Variáveis para trabalhar com as coord da bola na tela.
float init_x,init_y;      // Coordenadas iniciais de X e Y.

int rebater;   // Variavel para saber quando a tela está rebatendo nas bordas e nos jogadores

// Variáveis utilizadas para controlar o inicio do jogo a cada gol.
boolean start = false;
boolean init_start = true;

int ponto_jog1,ponto_jog2;  // Variável para armazenar o placar do jogo.

boolean win = false;
boolean title = true;
boolean pausa = false;

// --- Definindo o void setup() ---
void setup(){
  size(900,600);      // Aqui vamos colocar o tamanho da janela que queremos
  init_x = width/2;   // Coordenada inicial X centralizada no meio
  init_y = height/2;  // Coordenada inicial X centralizada no meio
  noCursor();         // Não deixa o ponteiro do mouse aparecer na tela.
  
  Porta = new Serial(this, "COM4", 9600);    // Aqui estamos realizando a comunicação serial, troque o "COM4" pela porta COM que você está utilizando
  Porta.bufferUntil('\n');  // As informações que ele irá armazenar vai ser atualizada a cada pular de linha.
  
  // Fonte que estamos utilizando
  textFont(loadFont("ArcadeClassic-48.vlw"),100);     // Aqui vamos carregar a fonte do jogo.
  textAlign(CENTER);
}

// --- Definindo o void draw() ---
void draw(){
 if(!pausa){ 
  if(win){    // Quando o jogo acabar, será escrito 'win' e 'lose' na tela
    placar();  // Função que calcula o placar do jogo
    
    if(ponto_jog1>ponto_jog2){  // Se o jogador 1 ganhar
      textAlign(CENTER,CENTER);
      textSize(130);
      text("WIN",width*0.25,height/2);
      text("LOSE",width*0.75,height/2);
      textSize(100);
    }
    else{    // Se o jogador 2 ganhar
      textAlign(CENTER,CENTER);
      textSize(130);
      text("LOSE",width*0.25,height/2);
      text("WIN",width*0.75,height/2);
      textSize(100);
    }
  }
  
  if(title){
    background(0);
    title_screen();
    fill(255);
  }
  
  // Quando a pessoa iniciar o jogo, acontece tudo que está dentro do if
  if(start){
    textSize(100);
    background(0);
    campo();    // Função que desenha o campo de jogo na tela.
    fill(255);
    placar();   // Função para mostrar o placar na tela.
    
    if(init_start){  // Local onde a bola deve iniciar na tela.
      x = init_x;
      y = init_y;
      v_x = random(2,3)*rnd_sign();
      v_y = random(2,3)*rnd_sign();
      rebater = 0;
      init_start = !init_start;
    }
    
    // posicionamento dos jogadores
    posiziona_pad();
    
    // movimentação da bola
    bola();
    }
 }
}


// Vamos realizar a leitura da serial e coletar os valores enviados.
void serialEvent (Serial Porta) {
  String stringa = Porta.readStringUntil('\n');
 
  if (stringa != null) {
    
    // trim = função que remove os espaços VAZIOS
    stringa = trim(stringa);
    
    // Salva a posição
    position = get_position(stringa);
  }
}

// Função que controla o sentido de movimentação da bola no inicio do jogo.
float rnd_sign(){
  float n = random(-1,1);
  if(n>=0){
    return 1.0;
  }
  else{
    return -1.0;
  }
}

//Função para desenhar o campo de jogo.
void campo(){
  stroke(255);
  strokeWeight(2);
  fill(255);
  rectMode(CORNER);
  rect(0,0,width,10);
  rect(0,height-10,width,10);
  strokeWeight(2);
  
  // Aqui estamos fazendo a simetria do campo.
  int segmenti=25;
  for(int i=0;i<segmenti;i++){
    if(i%2==0){
      
      // Linha de código para desenhar a linha do meio de campo.
      line(width/2,i*(height/segmenti),width/2,i*(height/segmenti)+(height/segmenti));
    }
  }
  noFill();
  ellipse(width/2,height/2,150,150);
}

// Função que configura o tamanho do título.
void title_screen(){
  stroke(255);
  strokeWeight(2);
  fill(255);
  rectMode(CORNER);
  rect(0,0,width,10);
  rect(0,height-10,width,10);
  strokeWeight(2);
  
  // Titulo do Jogo
  textAlign(CENTER,CENTER);
  textSize(200);
  text("BENS\nPONG",width/2,height*0.35);
  textSize(70);
  text("S : START      P : PAUSE",width/2,height*0.8);
}

// Vai recerber os valores dos potenciômetros enviados pela serial.
int[] get_position(String val_pot){
  return int(split(val_pot,'-'));
}

// Função para fazer a movimentação dos jogadores.
void posiziona_pad(){
  pos_pot1 = map(position[0],0,1023,10+alt_barra/2,height-(10+alt_barra/2));
  pos_pot2 = map(position[1],0,1023,10+alt_barra/2,height-(10+alt_barra/2));
  rectMode(CENTER);
  rect(20+larg_barra/2,pos_pot1,larg_barra,alt_barra,5);
  rect(width-(20+larg_barra/2),pos_pot2,larg_barra,alt_barra,5);
}

// Função para desenhar a movimentação da bola.
void bola(){
  fill(255);
  stroke(255);
  ellipse(x,y,dim_bola,dim_bola);
  x=x+v_x;
  y=y+v_y;
  
  //Colidir com os lados do campo
  if(y<10+dim_bola/2 || y>height-(10+dim_bola/2)){
    v_y=-v_y;
    
  }
  //Colidir com algum dos jogadores
  if((v_x<0 && x<20+larg_barra+dim_bola/2 && x>20+larg_barra/2 && y>=pos_pot1-alt_barra/2 && y<=pos_pot1+alt_barra/2) || (v_x>0 && x>width-(20+larg_barra+dim_bola/2) && x<width-(20+larg_barra/2) && y>=pos_pot2-alt_barra/2 && y<=pos_pot2+alt_barra/2)){
    if(rebater<20){
      v_x = -1.1*v_x;
      rebater++;
    }
    else{
      v_x = -v_x;
    }
  }
  
  // Pontos
  if(x<0){  // Se for menor que 0, significa que passou pelo jogador 1.
    ponto_jog2++;       // então é ponto do jogador 2
    init_start = true;  // Começa do meio a bola
  }
  if(x>width){   // Se for maior que a tela, significa que ela passou o jogador 2
    ponto_jog1++;        // Então é ponto do jogador 1.
    init_start = true;   // Começa com a bola no meio.
  }
}

// Função para calcular e mostrar o placar na tela.
void placar(){
  text(ponto_jog1,(width/2)*0.85,height/6);
  text(ponto_jog2,(width/2)*1.15,height/6);
  
  // Quando chegar em 5, acaba o jogo
  if(ponto_jog1 == 5){
    win = true;
    start = false;
  }
  if(ponto_jog2 == 5){
    win = true;
    start = false;
  }
}

// Função que realiza a leitura do teclado para saber qual tecla foi pressionada.
void keyPressed(){
  if(key == 's' && !pausa){  // Se clicar 'S' o jogo começa
    start = !start;
    init_start = true;
    ponto_jog1 = 0;
    ponto_jog2 = 0;
    title = !title;  
  }
  if(win){    // Se o jogo estiver terminado, aparece o placar e o vencedor.
    win = false;
    start = false;
    title = true;
  }
  if(key == 'p' && start){ // Se clicar 'p' o jogo é pausado
    pausa = !pausa;
  }
}
// ============================== FIM ========================================

Código do Projeto Comentado

Código Arduino

Vamos agora buscar entender ambos os códigos que utilizamos para realizar tal projeto! Começando pelo código do Arduino, temos:

#define pino_Pot1 A0

Nessa linha de código, estamos utilizando a diretiva #define para criar a variável que vai armazenar o pino analógico que o primeiro potenciômetro está conectado. Logo depois, repetimos o processo para o segundo potenciômetro. Caso ainda não tenha visto sobre diretivas no Arduino, temos um post falando sobre isso, aproveite e dê aquela conferida:

Em seguida, iniciamos a comunicação serial para poder comunicar com o Processing e enviar os dados do Arduino para ele. Nesse momento, já entramos no void loop(), onde vamos realizar a leitura dos potenciômetros e vamos armazena na string “val_pot”. Mas os valores dos potenciômetros estão separados por um (-) como você pode observar.

Por fim, é escrito no Monitor Serial do Arduino esses valores separados por (-). Em resumo, é isso que acontece no Arduino, vamos agora entender como o processing trabalha com esses dados.

Código Processing

Para começar a analisar o código utilizado no processing é de suma importância que você já tenha visto os dois últimos post a respeito dessa plataforma, isso porque estou a cada postagem, implementando funções novas, dessa forma, as funções já explicadas, eu não tomarei muito tempo explicando elas neste post, beleza?

Vamos iniciar então o entendimento do código. Começamos ele já inicializando a comunicação serial, pois será através dela que iremos fazer a comunicação com o nosso Arduino. Logo depois, criamos uma variável do tipo ‘Serial’ para poder armazenar qual a porta COM o Arduino está conectado, para poder ajudar na hora da comunicação.

Em seguida, criamos variáveis para determinar o tamanho das barras e da bola do jogo assim como variáveis para controlar a movimentação dos jogadores e da bola na tela. Entrando no bloco de função void setup(), temos dois comandos:

size(900,600);

Esse comando é utilizado para criar a janela que você visualiza quando executa o código. Esses valores dentro da função é o tamanho da janela que você quer criar, beleza?

noCursor()

Quando utilizamos esse comando, o mouse do ponteiro não irá aparecer na janela criada pelo programa, toda vez que você para ele pela janela, o mouse irá sumir. Por fim, quando entramos no void draw, temos todo o processo de criação do campo, o cálculo do placar e as condições da posição da bola na tela e das condições de gol ou não. Tudo bem explicado, mas caso não entenda alguma parte, não deixe de comentar abaixo, combinado?

Conclusão

Para finalizar o nosso post de hoje, deixo para vocês o vídeo do canal da AutoCore Robótica no Youtube, mostrando como ficou o resultado do projeto. Espero que você tenha gostado e caso queira mais posts sobre Processing comente abaixo, ok?

Mas e ai? Gostou? Caso tenha ficado algo incompleto para você, comenta abaixo 

Dicas?  Dúvidas?  Idéias de post? Críticas?  Só comentar também! 

Forte abraço!

Posts relacionados

Entenda a Principal Diferença entre Arduino e ESP32

por Danilo Nogueira
3 anos ago

Maker 101: Servos Motores

por autocore
6 anos ago

Criando aplicativos com App Inventor: Parte 2

por Danilo Nogueira
5 anos ago
Sair da versão mobile