Solucionado (ver solução)
Solucionado
(ver solução)
7
respostas

Resposta analisada e descritiva sobre o comportamento e a lógica da função "movimentaRaqueteOponente"

Congratulações à todos os amigos do fórum da Alura!

Venho aqui hoje tentar resolver a dúvida que algumas pessoas tiveram, incluindo eu, com relação ao comportamento e á lógica da seguinte função:

function movimentaRaqueteOponente() {
  velocidadeYOponente = yBolinha - yRaqueteOponente - raqueteComprimento / 2 - 30;
  yRaqueteOponente += velocidadeYOponente;
}

Percebi respostas diferentes nos tópicos anteriores e resolvi trazer a minha análise.

Primeiro irei analisar o comportamento da função para em seguida analisar a lógica da mesma.

Comportamento da função:

  1. A variável "velocidadeYOponente" não se trata de uma grandeza descritiva, visto que não foi explicitado, ainda, as variáveis do movimento, por exemplo:

Velocidade = ΔS / ΔT, onde ΔS = Deslocamento (m) e ΔT = Tempo (s). No nosso caso temos, ΔS = pixels e ΔT = milisegundo.

O objetivo da "velocidadeYOponente" é ser atribuída à "yRaqueteOponente", dando à "yRaqueteOponente" movimento; este que será emparelhado com o movimento da variável "yBolinha".

Para uma medida de comparação vamos pegar a variável "velocidadeYBolinha", declarada no início do código;

let velocidadeYBolinha = 6;

Essa variável não significa que a bolinha irá se deslocar 6 pixels no eixo y a cada segundo mas, que a bolinha irá "ganhar" 6 pixels no eixo y, quando a função "movimentaBolinha()" for executada, constantemente, em um período de tempo por enquanto indefinido.

Pela experiência e a observação entendi que esse período de tempo é uma taxa de variação entre 0,04 e 0,05 segundos para imprimir um movimento na tela.

Dessa forma, utilizando a variável "velocidadeYBolinha = 6; temos que, a velocidade média da bolinha partindo do ponto inicial até a borda inferior da tela é de aproximadamente: 0.066m/s.

Para chegar nesse resultado aproximado utilizei a função "setInterval()" para imprimir a contagem de tempo no console enquanto também utilizo a função "console.log()" para imprimir o valor de yBolinha no console, assim:

Dentro da função "setup":

function setup() {
  createCanvas(640, 400);

  setInterval(function momento() {
    console.log("1 segundo");
  }, 1000)

}

Dessa maneira iremos saber no console do p5.js sempre que se passar um segundo.

A outra função "console.log()" irá nos informar o valor da variável "yBolinha" e será declarada dentro da função "movimentaRaqueteOponente".

function movimentaRaqueteOponente() {
  velocidadeYOponente = yBolinha - yRaqueteOponente - raqueteComprimento / 2 - 30;
  yRaqueteOponente += velocidadeYOponente;

  console.log(yBolinha);
}

O resultado quando o jogo for executado é uma variação do valor de "yBolinha" sempre acrescido de +6:

206
212
218
224
230
236
242
248
254
260
266
272
278
284
290
296
302
308
314
320
326
332
338
344
350
356
362
368
374
1 segundo

Agora que já temos a velocidade média de "yBolinha" sabemos que nossa variável "velocidadeYOponente" irá receber uma velocidade de 0.066m/s, em um primeiro momento, mais a respectiva variação posicional no eixo y.

  1. O terceiro fator do cálculo atribuído à variável "velocidadeYOponente", que é a variável "raqueteComprimento", diferente do que comentaram em tópicos passados, não é aleatório; vejamos o seguinte cálculo:

Para raqueteComprimento = 10, temos:

 [...] - raqueteComprimento / 2 - 30;

Substituindo: - (10/2) -30 = -(5) - 30 = -35

  • Agora, como medida de comparação, se no lugar de "raqueteComprimento" usássemos "raqueteAltura", o resultado seria melhor?

Teríamos, nesse caso, - (90/2) -30 = - (45) - 30 = -75

Saberemos se esse resultado é melhor, assim como se a variável é aleatória, se verificarmos o ponto de colisão da raquete com a bola que veremos a seguir na explicação lógica.

Vejamos a continuação logo abaixo.

7 respostas
solução!

Lógica da função:

A grande dúvida dessa função é o porque de utilizarmos esses valores. Vejamos o código:

function movimentaRaqueteOponente() {
  velocidadeYOponente = yBolinha - yRaqueteOponente - raqueteComprimento / 2 - 30;
  yRaqueteOponente += velocidadeYOponente;
}

A minha análise da lógica desse código é de que a "velocidadeYOponente" é o que vai dar vida ao movimento no eixo y na variável "yRaqueteOponente".

Dessa forma, o movimento no eixo y de "yRaqueteOponente" deve ser igual ou aproximado ao movimento de "yBolinha" para que a raquete do oponente consiga acertar a bola.

Para que o movimento da raquete do oponente seja igual ou aproximado ao movimento da bolinha devemos atribuir á variável "velocidadeYOponente" a velocidade que vimos anteriormente.

Se dermos à variável "velocidadeYOponente" o mesmo movimento no eixo y de "yBolinha" a raquete sempre irá acertar a bolinha; é por isto do primeiro valor do cálculo.

Agora, porque subtraímos o valor da variável "yRaqueteOponente"?

Porque a raquete precisa sempre acompanhar o movimento da bolinha. Vejamos este exemplo:

Temos a bolinha na posição inicial (300, 200) e a raquete do oponente na posição inicial (590, 200). Nos primeiros instantes da execução do código, quando a função "movimentaBolinha" for executada, a bolinha percorrerá 6 pixels nos eixos x e y. O cálculo atribuído na variável "velocidadeYOponente" será:

 velocidadeYOponente = yBolinha - yRaqueteOponente - raqueteComprimento / 2 - 30;

Assim, yBolinha == 206, yRaqueteOponente == 200, raqueteComprimento == 10; substituindo os valores, temos:

velocidadeYOponente = 206 - 200 - (10 / 2) - 30 =>

*=> velocidadeYOponente = 206 - 235 = -29. *

Isso significa que nos instantes iniciais da execução do código a variável "yRaqueteOponente" irá ser acrescida de -29.

Temos: 200 - 29 = 171 que é a posição no eixo y que a raquete irá se posicionar.

Se não diminuíssemos o valor de "yRaqueteOponente" no cálculo, quando racionalizássemos teríamos:

200(yBolinha) - (10(raqueteComprimento) / 2) - 30 = 165

Dessa maneira, o valor 165 iria ser atribuído em seguida na variável "yRaqueteOponente" fazendo a raquete subir até a posição (590, 365) que não é o comportamento que desejamos.

[É preciso (é o por isso dos valores não serem aleatórios) que o cálculo atribuído à "velocidadeYOponente" desconsidere, isto é, anule, a posição inicial da bolinha com a posição da raquete para que o resultado do cálculo seja sempre a subtração de 6 (ou o valor escolhido como "velocidadeYBolinha") mais a área de colisão na raquete. ]

Aqui encerro a minha análise, espero que estas reflexões sejam úteis para alguém.

Poderia abstrair um pouco mais e explicar o ponto de colisão da bolinha com a raquete, mas isso acredito que o professor irá trabalhar melhor nos próximos vídeos.

Agradeço à todos aqueles que "clicaram na mesma tecla" porque a partir disto entendi parte do funcionamento do código.

Grande abraço e bons estudos.

Atenciosamente. Carlos Henrique.

(08/02/2022 - 15:29h.)

Então,

consegui o mesmo movimento apenas fazendo a função assim:

function movimentaOponenteRaquete2 (){

  yOponenteRaquete = yBolinha - alturaRaquete/2;
}

Não sei se eu coloquei algo diferente no meu código, mas continuou acompanhando o movimento da bolinha.

Não diminui por 30, pq ela só faz dá um desconto para bater mais embaixo.

Esse - 30 não faz com que o computador erre em algum momento, mas apenas que bata em posição diferente.

Talvez se eu colocar uma Random para dar um numero aleatório no lugar do -30 irá fazer com o que o oponente erre.

Esperto ter ajudado. Sucesso na sua jornada!!

//variáveis da bolinha
let xBolinha = 300;
let yBolinha = 200;
let diametro = 20;
let raio = diametro / 2;

//variáveis da velocidade de bolinha
let velocidadeXBolinha = 10;
let velocidadeYBolinha = 10;

//variáveis das raquetes
let comprimentoRaquete = 10;
let alturaRaquete = 90;

//variaveis minha raquete
let xMinhaRaquete = 5;
let yMinhaRaquete = 150 ;

//variaveis raquete oponente
let xOponenteRaquete = 585;
let yOponenteRaquete = 150;
let velocidadeYOponente;
let erroOponente;

function setup() {
  createCanvas(600, 400);
}

let colidiu = false;

function draw() {
  background(0);
  mostraBolinha ();
  movimentaBolinha ();
  verificaColisaoBorda ();

  desenhaRaquete(xMinhaRaquete, yMinhaRaquete);
  movimentaMinhaRaquete();
  colisaoRaqueteBiblioteca(xMinhaRaquete, yMinhaRaquete);

  desenhaRaquete(xOponenteRaquete, yOponenteRaquete);
  movimentaOponenteRaquete2();
  //movimentaOponenteRaquete();
  colisaoRaqueteBiblioteca(xOponenteRaquete, yOponenteRaquete); 
}

function mostraBolinha (){

  circle(xBolinha, yBolinha, diametro);
}

function desenhaRaquete(x, y){

  rect(x, y, comprimentoRaquete, alturaRaquete)
}

function movimentaBolinha (){

  xBolinha += velocidadeXBolinha;
  yBolinha += velocidadeYBolinha;
}

function movimentaMinhaRaquete (){

  if (keyIsDown(UP_ARROW)) {
    yMinhaRaquete -= 10;
  }
  if (keyIsDown(DOWN_ARROW)) {
    yMinhaRaquete += 10;
  }
}

function movimentaOponenteRaquete(){
  velocidadeYOponente = yBolinha - yOponenteRaquete - erro;
  yOponenteRaquete += velocidadeYOponente;

}

function movimentaOponenteRaquete2(){

  yOponenteRaquete = yBolinha - alturaRaquete/2 - erroOponente;
}


function verificaColisaoBorda (){

  if (xBolinha+raio > width ||
    xBolinha-raio < 0){
      velocidadeXBolinha *= -1;
  }
  if (yBolinha+raio > height ||
    yBolinha-raio < 0){
      velocidadeYBolinha *= -1;
  }
}

function verificaColisaoMinhaRaquete (){

  if (xBolinha-raio < xMinhaRaquete + comprimentoRaquete &&
      yBolinha-raio < yMinhaRaquete + alturaRaquete &&
      yBolinha+raio > yMinhaRaquete){
        velocidadeXBolinha *= -1;
  }
}

function colisaoRaqueteBiblioteca(x, y) {

  colidiu = 
  collideRectCircle(x, y, comprimentoRaquete, alturaRaquete, xBolinha, yBolinha, raio);
  if (colidiu) {
     velocidadeXBolinha *= -1; 
  }

}

Então,

Eu criei um random para por no lugar do -30 para quando gerasse um número específico o oponente erraria.

let erroOponente = Math.round(Math.random()*170);

function movimentaOponenteRaquete2(){
      yOponenteRaquete = (yBolinha+40)-erroOponente;
}

Consegui fazer com que gerasse um número para determinar se a raquete vai pegar ou não a bolinha.

O ideal seria que a cada jogada fosse gerado um novo valor para a variável erroOponente e assim seria determinado se a raquete ia pegar ou não na bolinha.

O problema é que eu não consegui fazer com que tempos em tempos seja gerado outros valores para a variável erro. Não sei porquê o SetInterval não funcionou no P5 como acontece no Sublime.

Espeto ter ajudado. Sucesso na sua jornada!!

Excelente iniciativa Diógenes!

Criei esse tópico para resolver as dúvidas daqueles que não entenderam a lógica e o comportamento da função "movimentaRaqueteOponente" feita pelo instrutor.

Claro que haverão diversas outras maneiras de se obter o mesmo comportamento com códigos diferentes; aí que está a beleza da programação e a possibilidade de nos reinventarmos.

Chamamos isso de refatoração.

Faz muito bem tentarmos entender nossos erros e as maneiras mais fáceis de se resolver algo complexo.

Agora sobre a sua implementação:

let erroOponente = Math.round(Math.random()*170);

function movimentaOponenteRaquete2(){
      yOponenteRaquete = (yBolinha+40)-erro;
}

Acredito que você não percebeu e colocou um nome incorreto na subtração de seu cálculo.

 yOponenteRaquete = 9yBolinha+40)-erro;

O correto seria colocar:


 yOponenteRaquete = 9yBolinha+40)-erroOponente;

Logo em seguida, no final desse curso o instrutor implementa a solução para o erro do oponente e há vários comentários legais no fórum e além sobre isso.

Uma possível solução do seu problema seria declarar a sua variável "let erroOponente" no início do seu código, sem atribuir valores, deixando para atribuir o " = Math.round(Math.random()*170);" dentro das funções "verificaColisaoBorda" e da "colisaoMinhaRaqueteBiblioteca".

Assim, sempre que a bolinha colidir ou com as raquetes ou com a borda sempre será gerado um número aleatório.

Boas pesquisas e continue desenvolvendo.

Att.

Carlos Henrique.

Então,

Na verdade não errei, eu quem digitei errado no fórum, pq resolvi mudar o nome da variável de 'erro' para 'erroOponente'', mas esqueci de arrumar no segundo momento em que ela aparece no código. Esse código, sem esse erro, dá certo, mas apenas para 1 momento.

Tentei fazer o que me indicou, mas a raquete fica maluca.

Se puder, copie meu código e tente ver o que está dando de errado. Vou deixar o código com o erroOponente no lugar e o que me indicou a fazer.

Desde já agradeço.

//variáveis da bolinha
let xBolinha = 300;
let yBolinha = 200;
let diametro = 20;
let raio = diametro / 2;

//variáveis da velocidade de bolinha
let velocidadeXBolinha = 10;
let velocidadeYBolinha = 10;

//variáveis das raquetes
let comprimentoRaquete = 10;
let alturaRaquete = 90;

//variaveis minha raquete
let xMinhaRaquete = 5;
let yMinhaRaquete = 150 ;

//variaveis raquete oponente
let xOponenteRaquete = 585;
let yOponenteRaquete = 150;
let velocidadeYOponente;
let erroOponente;

function setup() {
  createCanvas(600, 400);
}

let colidiu = false;

function draw() {
  background(0);
  mostraBolinha ();
  movimentaBolinha ();
  verificaColisaoBorda ();

  desenhaRaquete(xMinhaRaquete, yMinhaRaquete);
  movimentaMinhaRaquete();
  colisaoRaqueteBiblioteca(xMinhaRaquete, yMinhaRaquete);

  desenhaRaquete(xOponenteRaquete, yOponenteRaquete);
  movimentaOponenteRaquete2();
  //movimentaOponenteRaquete();
  colisaoRaqueteBiblioteca(xOponenteRaquete, yOponenteRaquete); 
}

function mostraBolinha (){

  circle(xBolinha, yBolinha, diametro);
}

function desenhaRaquete(x, y){

  rect(x, y, comprimentoRaquete, alturaRaquete)
}

function movimentaBolinha (){

  xBolinha += velocidadeXBolinha;
  yBolinha += velocidadeYBolinha;
}

function movimentaMinhaRaquete (){

  if (keyIsDown(UP_ARROW)) {
    yMinhaRaquete -= 10;
  }
  if (keyIsDown(DOWN_ARROW)) {
    yMinhaRaquete += 10;
  }
}

function movimentaOponenteRaquete(){
  velocidadeYOponente = yBolinha - yOponenteRaquete - erro;
  yOponenteRaquete += velocidadeYOponente;

}

function movimentaOponenteRaquete2(){

  yOponenteRaquete = yBolinha - alturaRaquete/2 - erroOponente;
}


function verificaColisaoBorda (){

  if (xBolinha+raio > width ||
    xBolinha-raio < 0){
      velocidadeXBolinha *= -1;
  }
  if (yBolinha+raio > height ||
    yBolinha-raio < 0){
      velocidadeYBolinha *= -1;
  }
  erroOponente = Math.round(Math.random()*170);
}


function verificaColisaoMinhaRaquete (){

  if (xBolinha-raio < xMinhaRaquete + comprimentoRaquete &&
      yBolinha-raio < yMinhaRaquete + alturaRaquete &&
      yBolinha+raio > yMinhaRaquete){
        velocidadeXBolinha *= -1;
  }
}

function colisaoRaqueteBiblioteca(x, y) {

  colidiu = 
  collideRectCircle(x, y, comprimentoRaquete, alturaRaquete, xBolinha, yBolinha, raio);
  if (colidiu) {
     velocidadeXBolinha *= -1; 
  }
  erroOponente = Math.round(Math.random()*170);

}

Excelente.

Segue a implementação do seu código...

function colisaoRaqueteBiblioteca(x, y) {

  colidiu = 
  collideRectCircle(x, y, comprimentoRaquete, alturaRaquete, xBolinha, yBolinha, raio);
  if (colidiu) {
     velocidadeXBolinha *= -1; 
  }
  erroOponente = Math.round(Math.random()*170);

}

Na linha 115 retirei a atribuição da variável "erroOponente" e coloquei na linha 114, dentro do bloco de instruções da estrutura "if", assim:


function colisaoRaqueteBiblioteca(x, y) {

  colidiu = 
  collideRectCircle(x, y, comprimentoRaquete, alturaRaquete, xBolinha, yBolinha, raio);
  if (colidiu) {
     velocidadeXBolinha *= -1;
    erroOponente = Math.round(Math.random()*170);
  }

}

Para diminuir a aleatoriedade também retirei essa atribuição da função "verificaColisaoBorda".

function verificaColisaoBorda (){

  if (xBolinha+raio > width ||
    xBolinha-raio < 0){
      velocidadeXBolinha *= -1;
  }
  if (yBolinha+raio > height ||
    yBolinha-raio < 0){
      velocidadeYBolinha *= -1;
  }
}

Dessa maneira temos como resultado o seguinte código:

//variáveis da bolinha
let xBolinha = 300;
let yBolinha = 200;
let diametro = 20;
let raio = diametro / 2;

//variáveis da velocidade de bolinha
let velocidadeXBolinha = 10;
let velocidadeYBolinha = 10;

//variáveis das raquetes
let comprimentoRaquete = 10;
let alturaRaquete = 90;

//variaveis minha raquete
let xMinhaRaquete = 5;
let yMinhaRaquete = 150 ;

//variaveis raquete oponente
let xOponenteRaquete = 585;
let yOponenteRaquete = 150;
let velocidadeYOponente;
let erroOponente;

function setup() {
  createCanvas(600, 400);
}

let colidiu = false;

function draw() {
  background(0);
  mostraBolinha ();
  movimentaBolinha ();
  verificaColisaoBorda ();

  desenhaRaquete(xMinhaRaquete, yMinhaRaquete);
  movimentaMinhaRaquete();
  colisaoRaqueteBiblioteca(xMinhaRaquete, yMinhaRaquete);

  desenhaRaquete(xOponenteRaquete, yOponenteRaquete);
  movimentaOponenteRaquete2();
  //movimentaOponenteRaquete();
  colisaoRaqueteBiblioteca(xOponenteRaquete, yOponenteRaquete); 
}

function mostraBolinha (){

  circle(xBolinha, yBolinha, diametro);
}

function desenhaRaquete(x, y){

  rect(x, y, comprimentoRaquete, alturaRaquete)
}

function movimentaBolinha (){

  xBolinha += velocidadeXBolinha;
  yBolinha += velocidadeYBolinha;
}

function movimentaMinhaRaquete (){

  if (keyIsDown(UP_ARROW)) {
    yMinhaRaquete -= 10;
  }
  if (keyIsDown(DOWN_ARROW)) {
    yMinhaRaquete += 10;
  }
}

function movimentaOponenteRaquete(){
  velocidadeYOponente = yBolinha - yOponenteRaquete - erro;
  yOponenteRaquete += velocidadeYOponente;

}

function movimentaOponenteRaquete2(){

  yOponenteRaquete = yBolinha - alturaRaquete/2 - erroOponente;
}


function verificaColisaoBorda (){

  if (xBolinha+raio > width ||
    xBolinha-raio < 0){
      velocidadeXBolinha *= -1;
  }
  if (yBolinha+raio > height ||
    yBolinha-raio < 0){
      velocidadeYBolinha *= -1;
  }
}


function verificaColisaoMinhaRaquete (){

  if (xBolinha-raio < xMinhaRaquete + comprimentoRaquete &&
      yBolinha-raio < yMinhaRaquete + alturaRaquete &&
      yBolinha+raio > yMinhaRaquete){
        velocidadeXBolinha *= -1;
  }
}

function colisaoRaqueteBiblioteca(x, y) {

  colidiu = 
  collideRectCircle(x, y, comprimentoRaquete, alturaRaquete, xBolinha, yBolinha, raio);
  if (colidiu) {
     velocidadeXBolinha *= -1;
    erroOponente = Math.round(Math.random()*170);
  }

}

Além dessas implementações é possível "brincar" com a função "Math.random()" visando chegar em uma configuração interessante de erro.

Observações:

Ainda existe um certo desvio da raquete no momento da colisão com a bolinha. Não interfere no desempenho do jogo mas, esteticamente ainda não está perfeito.

Outro detalhe é o bug que surge quando a bolinha acaba colidindo entre a borda e alguma das raquetes; esse erro ainda não consegui resolver. Como a raquete do oponente recebe o movimento da bolinha, menos a altura da raquete dividido por dois, menos um número aleatório, a raquete do oponente acaba "dançando" junto da bolinha entre a raquete e a borda.

Por enquanto o código está funcionando, me contento com esse resultado para um curso de introdução, mas, posteriormente, irei implementar as devidas mudanças.

Fique à vontade para comentar aqui no fórum em qualquer dúvida que surgir, Diógenes.

Vamos buscando sempre o progresso.

Até mais.

Carlos Henrique.

Antes de ver que tinha respondido eu consegui fazer o código e vim posta o resultado.

Ficou diferente do seu, mas seguiu a mesma lógica. Com o vídeo do professor eu vi que o melhor local para chamar a função calculaChanceErro, que gera um novo valor para a variável "chanceErro" (novo nome para "erroOponente"), é quando bate na minha raquete e no lado da minha borda do jogo.

Tive que desmembrar a função "verificaColisaoBorda" para eu colocar o "calculaChanceErro" somente quando colidisse com com o lado da minha borda do jogo.

Caso queira ver como ficou código final:

https://cursos.alura.com.br/forum/topico-jogo-com-o-oponente-que-realmente-tem-chances-de-errar-pois-e-um-numero-aleatorio-e-quem-determina-isso-201513

Agradeço a paciência e o retorno com as respostas.

Abraço.

Sucesso na sua jornada!!