TSM - Creează un joc: Ping Pong (II)

Ovidiu Mățan - Fondator @ Today Software Magazine

Vom continua în acest articol incursiunea noastră în realizarea unui joc casual folosind simple elemente de html și JS. Prima parte a acestui articol o puteți [citi](https://www.todaysoftmag.ro/article/4287/creeaza-un-joc-ping-pong-i ) în numărul 151 al revistei.

Paletele

Ne-am dat seama că interacțiunea celor care se joacă ar fi mult mai simplă de la același calculator dacă unul dintre ei folosește mouse-ul. Vom modifica în acest sens coordonatele Y ale paletei în funcție de coordonatele acestuia. De asemenea, am decis că este mai bine dacă ascundem cursorul când suntem în cadrul canvas-ului:

if (isMouse) {
   document.body.style.cursor = 
   'none';
    this.canvas.addEventListener
    ("mousemove",(e)=>{
      if (e.screenY<(
      this.canvas.height)) {
         this.y = e.screenY-h ;
      }
    });
    this.canvas
   .addEventListener("mouseup",(e)=>{
        console.log("fire");
        balls.push(new Ball(
        this.canvas,
        this.y+this.h/2,
        (this.x<=10)?3:-3,30));
    })
}

Se poate observa și crearea unei componente Ball în momentul în care se dă un click.

Metoda draw() este responsabilă cu desenarea paletei în poziția dată de mouse sau de apăsarea tastaturii.

draw(){
    this.ctx.beginPath();
    this.ctx.fillStyle = "yellow";
    this.ctx.fillRect(this.x-20,0,
    this.w+40,600);

    this.ctx.fillStyle =    
    this.#usedColor;

    this.ctx.fillRect(this.x,
    this.y, this.w,this.h);
}

Următoarele metode sunt importante în dinamica jocului pentru a determina contactul sau părăsirea suprafeței vizibile:

checkCollision(ball){
    if ((ball.x<=(this.x+this.w) 
    && ball.x>=(this.x) 
    && ball.y<=(this.y+this.h) 
    && ball.y>=(this.y)) ||
    ((ball.x+ball.size)>(this.x) 
    && (ball.x+ball.size)<=
   (this.x+this.w) 
    &&  ball.y<=(this.y+this.h) 
    && ball.y>=(this.y))){
        console.log("collision");
        return true;
   } else return false;
}

outsideBall(ball){
    return (Math.abs(
    this.x-ball.x)<5);
}

Mingea

Mingea sau mai bine spus proiectilul pătrat lansat de oricare dintre palete are pe lângă datele de inițializare din constructor, o metodă draw() ce desenează mingea. Aceasta este responsabilă și cu înaintarea acesteia pe suprafața de joc.

class Ball{
  constructor(canvas, y, step, size) {
    this.canvas = canvas;
    this.y=y;
    this.step = step;
    this.size=size
    this.ctx = this.canvas
    .getContext("2d");
    if (step>0){
      this.x=this.size*2;
    } else {
         this.x=this.canvas.width-this.size*2;
      }
   }

  hide(){
      this.ctx.fillStyle="yellow";
      this.ctx.fillRect(this.x,this.y, this.size,
      this.size);
    }
    draw() {
      this.hide();
      this.ctx.fillStyle = "#000";
      if (this.x<(this.canvas.width-this.size) 
      || this.x>0) {
         this.x=this.x+this.step;
         this.ctx.fillRect(this.x,this.y, 
         this.size,this.size);
        }
        return {
          x:this.x,
          y:this.y,
        }
    }
}
export {Ball}

Controlul

Odată inițializate paletele și pornită bucla de afișare, tot ce ne rămâne de făcut este să controlăm mingile lansate, să determinăm coliziunea și să modificăm scorul în cazul în care nu sunt prinse:

paddleLeft=factPaddle.simpleOrange(10,10,20,100, 40,38, false,5, "Player 1");
paddleRight=factPaddle.simpleOrange(770,500,20,100, 99,102, true,5, "Player 2");

startDrawingLoop();
...
function takePic() {
 if (!win) {
     paddleLeft.draw();
     paddleRight.draw();
     for (let i = 0; i < balls.length && !win; i++) {
       let ball = balls[i];
       let right = updateCollision(paddleRight, 
       paddleLeft, ball);

       let left = updateCollision(paddleLeft, 
       paddleRight, ball);

         if (right || left) {
           console.log("hide ball");
           ball.hide();
           balls.splice(i, 1);
           player1.textContent = paddleLeft.points;
           player2.textContent = paddleRight.points;
         } else {
             ball.draw();
         }
           win = checkWins(paddleLeft, paddleRight) 
           || checkWins(paddleRight, paddleLeft);
        }
    }
}
function checkWins(checkPaddle, otherPaddle){
    if (checkPaddle.points===0){
        alert(otherPaddle.name+" wins! Congrats !");
        window.location.reload();
        return true;
    } return false;
}

function updateCollision(checkPaddle, firedPaddle, ball){
    let collision=checkPaddle.checkCollision(ball);
    let outside=checkPaddle.outsideBall(ball);
    if (outside){
        firedPaddle.points--;
        console.log("points: ",firedPaddle.points);
    }
    return (outside || collision);
}

Ce urmează?

În următoarele versiuni, vom încerca să adăugăm mai mult realism mișcării mingii și să integrăm niște soluții bazate pe ML, astfel încât interacțiunea cu paletele să fie pe baza mișcării mâinilor în fața camerei. Vom înlocui astfel interacțiunea mouse-ului, respectiv a tastaturii.