Ping-Pongend het weekend in!

Deze week weer is wat anders geen nieuws bericht of een in-depth artikel over een nieuwe feature van een programmeertaal maar een fun artikeltje over hoe je je eigen spelletje kan maken. We maken hierbij gebruik van canvas en javascript. Het schermpje met de vallende code op onze homepagina was erg leuk maar ik vond het tijd voor iets nieuws daarom dacht ik we zetten er een spelletje neer.
published: Oct 9, 2020

Ik zat vandaag te zoeken waar ik deze week over zou gaan schrijven en ik wilde even door onze oude blog artikelen bladeren. Toen ik op onze homepage kwam zag ik weer de vallende code à la The Matrix en hoewel ik het nog steeds ontzettend cool vond dacht ik misschien is het tijd voor iets nieuws. 

De gedachte was altijd dat we daar steeds andere dingen voor gingen gebruiken. Toen dacht ik dat is wel een leuk projectje om op te pakken en om tegelijkertijd er een artikel over te schrijven. Ik dacht dat het wel leuk zou zijn om er een spelletje neer te zetten en als echte gamer zijnde moest ik gelijk denken aan een van de eerste spelletjes. PONG! 

Hieronder zie je het complete spelletje en daaronder ga ik stap voor stap uitleggen hoe je het zelf kan maken.

2

Ik ben begonnen met mijn html. Eerst heb ik een canvas element aangemaakt die geef ik een width en height mee. Naast mn game veld wilde ik ook wat knoppen om het spel te beginnen /pauzeren of resetten. Ik maak gebruik van bootstrap classes om ze stylen.

HTML

<canvas id=”pong” width=”600″ height=”400″></canvas>
<div class=”btn-group”>
       <button class=”btn btn-primary” id=”start”>Start</button>
       <button class=”btn btn-primary” id=”pause”>Pause</button>
       <button class=”btn btn-primary” id=”reset”>Reset</button>
</div>

2

Na de HTML komt de JavaScript. Het eerste wat we daar moeten doen is het canvas element ophalen en de getContext method aanroepen op het canvas element zodat we er dingen op kunnen gaan tekenen. Wet halen het canvas element op met document.getElementById() en die zetten we in een variabele. Vervolgens maken we een nieuwe variabele aan om op de canvas de getContext method aan te roepen.

JS

// selecteer het canvas element

const canvas = document.getElementById(“pong”);

// roep de getContext method aan op de canvas variabele zodat we erop kunnen gaan tekenen

const ctx = canvas.getContext(‘2d’);

2

Het volgende wat moet doen is de objecten definiëren die je nodig hebt. We hebben een paddle nodig voor ons zelf en voor de computer. Verder hebben we nog een bal en een net nodig. Deze objecten geven we een aantal waardes mee namelijk de x/y coördinaten waar er begonnen moet worden met tekenen. De width en hoogte van het object en bij de paddles houden we score bij en de bal krijgen we de snelheid van de bal en de radius.

JS

// Ball object
const ball = {
    x : canvas.width/2,
    y : canvas.height/2,
    radius : 10,
    velocityX : 5,
    velocityY : 5,
    speed : 7,
    color : “#4db078”
}

// Gebruiker paddle object
const user = {
    x : 0, // left side of canvas
    y : (canvas.height – 100)/2, // -100 the height of paddle
    width : 10,
    height : 100,
    score : 0,
    color : “#4db078”
}

// Computer paddle object
const com = {
    x : canvas.width – 10, // – width of paddle
    y : (canvas.height – 100)/2, // -100 the height of paddle
    width : 10,
    height : 100,
    score : 0,
    color : ‘#4db078’
}

// Net object
const net = {
    x : (canvas.width – 2)/2,
    y : 0,
    height : 10,
    width : 2,
    color : “#4db078”
}

2

Nu we onze objecten hebben kunnen we de tekenfuncties gaan maken. We gebruiken hiervoor de HTML canvas functie fillRect() /drawRect() en fillStyle voor.

JS

// teken een vierkant, deze gaan we gebruiken voor de paddles
function drawRect(x, y, w, h, color){
      ctx.fillStyle = color;
      ctx.fillRect(x, y, w, h);
}

// teken een circel deze gebruiken we voor het balletje
function drawArc(x, y, r, color){
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.arc(x,y,r,0,Math.PI*2,true);
      ctx.closePath();
      ctx.fill();
}

// teken het net
function drawNet(){
      for(let i = 0; i <= canvas.height; i+=15){
            drawRect(net.x, net.y + i, net.width, net.height, net.color);
      }
}

// draw score
function drawText(text,x,y, color){
      ctx.fillStyle = color;
      ctx.font = “20px fantasy”;
      ctx.fillText(text, x, y);
}

//teken de welkom tekst
function drawWelkomText(text,x,y, color){
      ctx.fillStyle = color;
      ctx.font = “12px Arial”;
      ctx.fillText(text, x, y);
}

2

Nu hebben we nog een reset functie nodig voor als er gescoord is en we moeten een event handler toevoegen zodat we weten waar onze muis op het canvas is ten opzichte van de top van het scherm zodat we de user paddle mee kunnen laten bewegen. Nadat er gescoord is laten we de ball weer in het midden beginnen.

JS

// luisteren naar de positie van de muis
canvas.addEventListener(“mousemove”, getMousePos);
function getMousePos(evt){
      let rect = canvas.getBoundingClientRect();
      user.y = evt.clientY – rect.top – user.height/2;
}

// waneer er gescoord is de ball resetten naar het midden
function resetBall(){
      ball.x = canvas.width/2;
      ball.y = canvas.height/2;
      ball.velocityX = -ball.velocityX;
      ball.speed = 4;
}

2

De volgende functie die we nodig hebben is een functie die kijkt of de ball in aanraking komt met de paddle. Dit doen we door elke keer als we de functie aanroepen de coördinaten van de paddle en de ball te vergelijken.

JS

// botsing detectie
function collision(b,p){
      p.top = p.y;
      p.bottom = p.y + p.height;
      p.left = p.x;
      p.right = p.x + p.width;
      b.top = b.y – b.radius;
      b.bottom = b.y + b.radius;
      b.left = b.x – b.radius;
      b.right = b.x + b.radius;
      return p.left < b.right && p.top < b.bottom && p.right > b.left && p.bottom > b.top;
}

2

De functie die nu komt is de belangrijkste dat is degene die al het rekenwerk voor ons gaat doen. Het eerste gedeelte kijkt of er gescoord wordt door te kijken of de x coördinaat van de bal negatief wordt. Het volgende gedeelte zorgt ervoor dat de bal beweegt door steeds te x en y coördinaten te veranderen. Daarna geven we de computer een simpele AI vorm dat hij zn paddle kan bewegen. Dan kijken we of de bal de y-as raakt en zo ja dan keren we de y snelheid negatief om zodat hij in de omgekeerde hoek terugkaatst. We zijn er nog niet dan kijken we of de bal de paddle van de computer of de speler raakt. Als hij een van die 2 raakt dan berekenen we met de vorige functie de botsing en daarna kijken we waar hij geraakt wordt wordt hij op de boven of onderkant geraakt dan kaatsen we hem terug in een hoek van 45 graden. En na elke keer dat hij geraakt wordt verhogen we de snelheid.

JS

// update functie, de functie die alle berekeningen maakt
function update(){
// Verander de score van de spelers
if( ball.x – ball.radius < 0 ){
     com.score++; resetBall();
}
else if( ball.x + ball.radius > canvas.width){
      user.score++;
      resetBall();
}

// the ball has a velocity
ball.x += ball.velocityX;
ball.y += ball.velocityY;

// computer plays for itself, and we must be able to beat it
// simple AI
com.y += ((ball.y – (com.y + com.height/2)))*0.1;

// when the ball collides with bottom and top walls we inverse the y velocity.
if(ball.y – ball.radius < 0 || ball.y + ball.radius > canvas.height){
      ball.velocityY = -ball.velocityY;
}

// we check if the paddle hit the user or the com paddle
let player = (ball.x + ball.radius < canvas.width/2) ? user : com;

// if the ball hits a paddle
if(collision(ball,player)){
      // we check where the ball hits the paddle
      let collidePoint = (ball.y – (player.y + player.height/2));
      // normalize the value of collidePoint, we need to get numbers between -1 and 1.
       // -player.height/2 < collide Point < player.height/2
      collidePoint = collidePoint / (player.height/2);

      // when the ball hits the top of a paddle we want the ball, to take a -45degees angle
       // when the ball hits the center of the paddle we want the ball to take a 0degrees angle
       // when the ball hits the bottom of the paddle we want the ball to take a 45degrees
       // Math.PI/4 = 45degrees
      let angleRad = (Math.PI/4) * collidePoint;

      // change the X and Y velocity direction
      let direction = (ball.x + ball.radius < canvas.width/2) ? 1 : -1;

      ball.velocityX = direction * ball.speed * Math.cos(angleRad);
      ball.velocityY = ball.speed * Math.sin(angleRad);

      // speed up the ball everytime a paddle hits it.
      ball.speed += 0.1;
}
}

2

In deze functie gaan we alle teken functies aanroepen die we eerder hebben gemaakt. Met wat parameters die we meegeven om ze op de juiste plek te tekenen.

JS

// teken functie, de functie die al het tekenen regelt
function render(){

      // leeg het canvas
      drawRect(0, 0, canvas.width, canvas.height, “#000”);

      // teken de speler score aan de linker kant
      drawText(user.score,canvas.width/4,canvas.height/5, “#4db078”);

      // teken de computer score aan de rechter kant
      drawText(com.score,3*canvas.width/4,canvas.height/5, “#4db078”);

      // teken het net
      drawNet();

      // teken de paddle van de speler
      drawRect(user.x, user.y, user.width, user.height, user.color);

      // teken de paddle van de computer
      drawRect(com.x, com.y, com.width, com.height, com.color);

      // teken de bal
      drawArc(ball.x, ball.y, ball.radius, ball.color);

}

2

De laatste paar functies die we gaan maken zijn de game functie die de teken functie en de reken functie aanroept. En een start onclick event die het spelletje start en aangeeft hoe vaak de gamefunctie aangeroepen wordt en een reset onclick event die de score en de ball reset. En als laatste een pauzeer event die het spelletje op pauze zet.

2

JS

//De game functie die de vorige 2 aanroept
function game(){
      update();
      render();
}

//Teken de welkom tekst
drawWelkomText(“Welkom bij 4BIS ping pong klik op start om te beginnen”,10,390, “#fff”);

var start = document.getElementById(‘start’);
var reset = document.getElementById(‘reset’);
var pause = document.getElementById(‘pause’);

//Onclick event om het spel te starten
start.onclick = function(){
      // Aantal frames per seconde
      let framePerSecond = 50;

      //Roep de game functie 50x per seconde aan
      var loop = setInterval(game,1000/framePerSecond);

      start.innerHTML = ‘Start’;
      start.setAttribute(‘disabled’, true);

      //Pauze event om het spel te pauzeren
      pause.onclick = function(){
            drawRect(0, 0, canvas.width, canvas.height, “#000”);
            // Teken pauze tekst
            drawText(‘PAUSE’,175,200, ‘#FFF’);
            clearInterval(loop);
            start.innerHTML = ‘Resume’;
            start.disabled = false;

      };
};

//Reset event om alles opnieuw te beginnen
reset.onclick = function(){
      user.score = 0;
      com.score = 0;
      resetBall();
};

2

Nou hebben we het JavaScript gedeelte gehad en dan voegen we nog een klein beetje CSS toe voor het welkom scherm.

CSS

#pong{
      display: block;
       margin: auto;
      width: 75%;
      height: 300px;
      position: relative;
      top:20px;
      background-image: url(“https://4bis.nl/wp-       content/uploads/2019/03/4BIS.InnovationsWIT.svg”);
      background-repeat: no-repeat;
      background-position: center;
      background-color: black;
      background-size: 75%;
}

Ik hoop dat je wat geleerd hebt en belangrijker er plezier mee gaat hebben en dat het je een gevoel van nostalgie brengt dat je een van de eerste spelletjes opnieuw hebt gecreeerd. Als je vragen hebt of hulp nodig hebt bij een project neem dan contact op met ons om de mogelijkheden te bespreken.


Deel 4 van de React Hooks serie useMemo & useCallback

Deel 4 van de React Hooks serie useMemo & useCallback

Deel 4 van de ReactHooks serie! We gaan het hebben over useCallback en over useMemo. Met deze hooks kunnen we waardes of callback memoizen dit kan nuttig zijn bij zware operaties of om te zorgen dat bepaalde componenten niet altijd mee rerenderen. Zeker omdat bij kleine applicatie het vaak efficienter is om niet te optimaliseren dan wel.

Lees meer
Redux de Intro!

Redux de Intro!

Deze week een korte introductie van de 4 basisbegrippen die je nodig hebt om Redux te begrijpen. We gaan kort uitleggen hoe state wordt aangepast in een Redux Store met behulp van Reducers en Actions. En we vertellen wat al deze begrippen doen en hoe ze werken in een Redux applicatie.

Lees meer

Software ontwikkeling op maat

Hoe werkt het?

Wat wonderen doet voor het ene bedrijf kan zinloos zijn voor een ander bedrijf. Daarom hebben we oplossingen op maat die het unieke karakter van jouw bedrijf respecteren. We werken in een positieve spiraal van testen, monitoren en verzamelen gegevens om precies te weten te komen wat voor jou werkt en wat niet. Het is onze ‘whole package’-mindset, een aandacht voor details die ons in staat stelt om elke keer jouw prestatiedoelstellingen te bereiken. Dus ontspan en geniet van de rit!