Floppy Pong in p5.js (deel 1)

Flappy Pong bouwen

Deze post is een Google translate vertaling van het Engels naar het Nederlands, en van Java/Processing naar JavaScript/p5.js. Het originele artikel vindt je hier: https://www.toptal.com/game/ultimate-guide-to-processing-simple-game

Om te beginnen advisier ik om de online p5 web editor te gebruiken, ik zou even aanmelden (gratis) zodat je je werk ook kan opslaan, hier is de link https://editor.p5js.org/

https://editor.p5js.org/

Stap # 1: Opzetten van het spel

De eerste stap is om ons project te initialiseren. Om te beginnen zullen we onze set-up schrijven. Daarna zullen we verschillende schermen behandelen (beginscherm, spelscherm, game over scherm etc.). Dus de vraag rijst, hoe zorgen we ervoor dat de juiste pagina op het juiste tijdstip wordt verwerkt?

Het volbrengen van deze taak is vrij eenvoudig. We zullen een (globale) variabele aanmaken (dmv van het var) welke bijhoud wat het actieve scherm is. Vervolgens tekenen we de inhoud van het juiste scherm, afhankelijk van deze variabele. In de draw functie hebben we een if-instructie die de variabele controleert aan de hand van de waarde het juiste scherm laat zien.

Om een scherm te wisselen, moeen we de waarde van de variable gaan aanpassen. Dus onze eerste code ziet er als volgt uit:

/********* VARIABELEN *********/

// Variable welke bepaalt wel scherm er getoond
// moet worden
//
// 0: startScherm
// 1: gameScherm
// 2: gameOverScherm

var welkScherm = 0;

/********* SETUP FUNCTIE *********/

function setup() {
  createCanvas(500, 500);
}


/********* DRAW FUNCTIE *********/

function draw() {
  // Bepaal welk scherm getekend moet worden
  if (welkScherm == 0) {
    startScherm();
  } else if (welkScherm == 1) {
    gameScherm();
  } else if (welkScherm == 2) {
    gameOverScherm();
  }
}


/********* De verschillende schermen *********/

function startScherm() {
  // code voor start scherm
}
function gameScherm() {
  // code voor het game scherm
}
function gameOverScherm() {
  // code voor het game over scherm
  background(0);
}


/********* INVOER *********/

function mousePressed() {
  // als we in het start scherm getoond wordt en de muis knop wordt ingedrukt,
  // dan laten we het startScherm zien
  if (welkScherm==0) {
    startGame();
  }
}


/********* HULP FUNCTIES *********/

// Functie die wordt uitgevoerd als het spelletje moet beginnnen 
function startGame() {
  welkScherm=1;
}

Dit kan in het begin eng lijken, maar we hebben alleen de basisstructuur opgebouwd en verschillende delen gescheiden met commentaarblokken.

Zoals je kunt zien, definiëren we een andere functie voor ieder scherm (startScherm, gameScherm en gameOverScherm). In de draw functie controleren we de waarde van de welkSchermvariabele en worde de juiste functie aangroepen.

In de functie mousePressed(){...}gedeelte luisteren we naar muisklikken. Als de welkScherm variable 0 is (dus startScherm wordt getoond) en de mouse knop is ingedrukt, dan veranderen we de waarde van welkScherm naar 1, zodat het gameScherm wordt getoond.

Om dat te doen, zullen we de startScherm()methode bewerken . Hier gaan we:

function startScherm() {
  // code voor start scherm
  background(0);
  fill(255);
  textSize(32);
  textAlign(CENTER);
  text("Klik om te starten", height/2, width/2);
}

Ons eerste scherm heeft nu een zwarte achtergrond en een eenvoudige tekst, “Klik om te starten” in het midden van het scherm. Maar wanneer we klikken, gebeurt er niets. We hebben nog geen inhoud voor ons gamescherm gespecificeerd.

De methode gameScherm()bevat nu nog geen code, dus blijft de inhoud van het oude scherm gewoon staan). Laten we beginnen met het implementeren van het gameScherm door eerst de achtergrond wit te maken.

function gamecherm() {
  // code voor het game over scherm
  background(255);
}

Na deze wijziging zult u merken dat de achtergrond wit wordt en de tekst verdwijnt.

Stap # 2: De bal creëren en zwaartekracht implementeren

Nu gaan we aan het spelscherm werken. We zullen eerst onze bal maken. We moeten variabelen definiëren voor de coördinaten, kleur en grootte, omdat we die waarden later wellicht willen wijzigen. Als we bijvoorbeeld de maat van de bal willen vergroten als de speler hoger scoort, zodat het spel moeilijker zal zijn. We zullen de grootte moeten veranderen, dus het zou een variabele moeten zijn. We zullen ook de snelheid van de bal bepalen, nadat we de zwaartekracht hebben geïmplementeerd.

Laten we eerst het volgende toevoegen (let wel je moet niet letterlijk alles toevoegen, de …  moet je niet intypen, deze geven aan dat er bestaande code is, ook de void setup() { bestaat al. Deze keer geven we nog precies aan wat je moet intypen, verderop zul je dat zelf moeten uitzoeken.

...                     <-- niet over typen
var balX;
var balY;
var balStraal=6;
...                     <-- niet over typen
function setup() {      <-- niet over typen, bestaat al
  ...                   <-- niet over typen
  balX=200;
  balY=100;
}                       <- niet over typen, bestaat al
...                     <-- niet over typen
function gameScherm() { <-- niet over typen
     ...                <-- niet over typen
  tekenBal();
}                       <-- niet over typen, bestaat al

...                     <-- niet over typen

function tekenBal() {
  fill(0);
  ellipse(balX, balY, balStraal * 2);
}

 

We definieerden de coördinaten als globale variabelen, creëerden een methode om de bal te tekenen, aangeroepen vanuit de gameScherm functie. Het enige waar je op moet letten, is dat we de coördinaten hebben geïnitialiseerd , maar we hebben ze gedefinieerd setup(). De reden dat we dat deden, was dat we wilden dat de bal begint met een vierde van links en een vijfde van boven. Er is geen speciale reden waarom we dat willen, maar dat is een goed punt voor de start van de bal. Dus we moesten de widthen heightvan de schets dynamisch krijgen. De schetsgrootte wordt gedefinieerd in setup(), na de eerste regel. widthen heightzijn niet ingesteld voor setup()runs, daarom konden we dit niet bereiken als we de variabelen bovenaan definieerden.

Zwaartekracht

In ons programma modeleren/simuleren we de werkelijkheid, we willen het zo echt mogelijk laten lijken zonder dat we precies alle natuurkundige invloeden moeten berekenen.

Het implementeren van de zwaartekracht is eigenlijk het makkelijke deel. Er zijn slechts een paar trucjes. Als eerste laten we de bal bewegen onder invloed van de zwaartekracht

....
var zwaarteKracht = 0.1;
var verticaleBalSnelheid = 0;

...

function gameScherm() {
  // code voor het game scherm
  background(255);
  
  // bereken de nieuwe snelheid 
  verticaleBalSnelheid = verticaleBalSnelheid + zwaarteKracht;
  
  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid;
  
  tekenBal();
}

De variabele die we hebben gedefinieerd als zwaarteKrachtis slechts een numerieke waarde (maar  is ook een comma getal,  zodat we decimale waarden kunnen gebruiken, niet alleen gehele getallen, die we verticaleBalSnelheidaan elke lus toevoegen (het balletje gaat dus steeds sneller vallen door de zwaartekracht).

En verticalBalSnelheidis de verticale snelheid van de bal, die wordt elke opgeteld aan het Y-coördinaat van de bal ( balY) gameScherm wordt uitgevoerd (deze wordt ongeveer 60 x per seconde).

Als alles goed is ziet je code er als volgt uit:

/********* VARIABELEN *********/

// Variable welke bepaalt wel scherm er getoond
// moet worden
//
// 0: startScherm
// 1: gameScherm
// 2: gameOverScherm

var welkScherm = 0;

var balX;
var balY;
var balStraal=6;

var zwaarteKracht = 0.1;
var verticaleBalSnelheid = 0;

/********* SETUP FUNCTIE *********/

function setup() {
  createCanvas(500, 500);
  balX=200;
  balY=100;  
}


/********* DRAW FUNCTIE *********/

function draw() {
  // Bepaal welk scherm getekend moet worden
  if (welkScherm == 0) {
    startScherm();
  } else if (welkScherm == 1) {
    gameScherm();
  } else if (welkScherm == 2) {
    gameOverScherm();
  }
}


/********* De verschillende schermen *********/

function startScherm() {
  // code voor start scherm
  background(0);
  fill(255);
  textSize(32);
  textAlign(CENTER);
  text("Klik om te starten", height/2, width/2);
}

function gameScherm() {
  // code voor het game scherm
  background(255);
  
  // bereken de nieuwe snelheid 
  verticaleBalSnelheid = verticaleBalSnelheid + zwaarteKracht;
  
  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid;
  
  tekenBal();
}

function gameOverScherm() {
  // code voor het game over scherm
  background(255);
}


/********* INVOER *********/

function mousePressed() {
  // als we in het start scherm getoond wordt en de muis knop wordt ingedrukt,
  // dan laten we het startScherm zien
  if (welkScherm==0) {
    startGame();
  }
}


/********* HULP FUNCTIES *********/

// Functie die wordt uitgevoerd als het spelletje moet beginnnen 
function startGame() {
  welkScherm=1;
}

function tekenBal() {
  fill(0);
  ellipse(balX, balY, balStraal * 2);
}


Het balletje valt nu van het scherm, we willen graag dat het balletje gaat stuiteren als deze de bovenkant of onderkant van het scherm raakt.

Voor de onderkant moeten we controleren of balY mag nooit groter worden dan scherm hoogte – de straal van de bal(height– balStraal), als dit wel het geval is dan moet de bal gaan stuiteren.

Om de bal te laten stuiteren, verplaatsen we de bal naar de exacte locatie waar hij moest stuiteren en vermenigvuldigde hij de verticale snelheid ( verticalBalSnelheid) met -1(vermenigvuldigen met -1 verandert het teken).

Wanneer de snelheidswaarde een minteken heeft, wordt het toevoegen van Y-coördinaat de snelheid balY + (-verticalBalSnelheid), wat is balY - verticalBalSnelhei. Dus de bal verandert onmiddellijk van richting met dezelfde snelheid.

Een bal stuitert eindeloos op een racket.

Deze check moeten we direct uitvoeren nadat de nieuw balY positie is berekend.

function gameScherm() {
  ......

  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid

  // stuiter als de bal de onderkant raakt
  if ( balY  > ( height - balStraal ) ) {
    // set bal precies op de rand
    balY = height - balStraal;
    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;
  }
  ....

}

Hetzelfde moeten we doen om de bal aan de bovenkant te laten stuiteren, maar hier moeten we controleren of de balY positie niet kleiner wordt dan de balStraal.

Onderstaande code kunnen we direct uitvoeren nadat de vorige check is uitgevoerd, de complete code van het gameScherm ziet er dan als volgt uit:

function gameScherm() {
  // code voor het game scherm
  background(255);
  
  // bereken de nieuwe snelheid 
  verticaleBalSnelheid = verticaleBalSnelheid + zwaarteKracht;
   
  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid;
  
  // stuiter als de bal de onderkant raakt
  if ( balY  > ( height - balStraal ) ) {
    // set bal precies op de rand
    balY = height - balStraal;
    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;
  }
  
  // stuiter als de bal de onderkant raakt
  if ( balY  <  balStraal ) {
    // set bal precies op de rand
    balY = balStraal;
    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;
  }
  
  tekenBal();
}

Om te controleren of de stuiter functie aan de bovenkant goed werkt kunnen we de verticalBalSnelheid even op -10 zetten, zodat het balletje eerst naar boven gaat en de bovenkant raakt (en gaat stuiteren), het resultaat ziet er dan als volgt uit:

Er is echter een probleem met ons animatieproces – de bal blijft stuiteren. Als dit een scenario uit de echte wereld was, zou de bal elke keer dat hij een oppervlak raakte, te maken hebben gehad met luchtweerstand en wrijving (waardoor de bal langzamer gaat bewegen en steeds minder hoog gaat stuiteren en uiteindlijk stil komt te vallen). Dat is het gedrag dat we willen hebben voor het animatieproces van onze game, dus het implementeren hiervan is eenvoudig. We voegen het volgende toe:

 

...
var stuiterWeerstand = 0.1;
var luchtWeerstand =  0.001;
...


function gameScherm() {
  // code voor het game scherm
  background(255);
  
  // bereken de nieuwe snelheid 
  verticaleBalSnelheid = verticaleBalSnelheid + zwaarteKracht;
   
  // Invloed van de luchtweerstand
  verticaleBalSnelheid = verticaleBalSnelheid - luchtWeerstand * verticaleBalSnelheid;  
  
  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid;
  
  // stuiter als de bal de onderkant raakt
  if ( balY  > ( height - balStraal ) ) {
    // set bal precies op de rand
    balY = height - balStraal;
    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;
    
    // verlaag snelheid door stuiter
    verticaleBalSnelheid = verticaleBalSnelheid - stuiterWeerstand * verticaleBalSnelheid;
  }
  
  // stuiter als de bal de onderkant raakt
  if ( balY  <  balStraal ) {
    // set bal precies op de rand
    balY = balStraal;

    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;

    // verlaag snelheid door stuiter
    verticaleBalSnelheid = verticaleBalSnelheid - stuiterWeerstand * verticaleBalSnelheid;
   
  }
  tekenBal();
}

En nu produceert ons animatieproces dit:

Zoals de naam al doet vermoeden, stuiterWeerstand de oppervlaktewrijving en luchtWeerste de wrijving van lucht. Het is dus duidelijk dat het stuiterWeerstandmoet worden toegepast elke keer dat de bal een oppervlak raakt. luchtWeerstandmoet echter constant van toepassing zijn.

Dus nadat de verticalBalSnelheid opnieuw wordt uitgerekent, gaan we de snelheid  aanpassen om rekenening te houden met de luchtweerstand (deze is heel klein,  bij iedere berekening wordt de snelheid met 1/1000 verlaagd ) .

verticaleBalSnelheid = verticaleBalSnelheid – luchtWeerstand * verticaleBalSnelheid;

Iedere keer als er een stuiter plaatsvindt wordt de verticalBalSnelheid met 1/10 verlaagd, dus de berekening van de verticale snelheid bij een stuiter is als volgt:

verticaleBalSnelheid = verticaleBalSnelheid – stuiterWeerstand * verticaleBalSnelheid;

Als het goed is moet je code er (ongeveer) als volgt uit zien:

/********* VARIABELEN *********/

// Variable welke bepaalt wel scherm er getoond
// moet worden
//
// 0: startScherm
// 1: gameScherm
// 2: gameOverScherm

var welkScherm = 0;

var balX;
var balY;
var balStraal=6;

var zwaarteKracht = 0.1;
var verticaleBalSnelheid = -10;

var stuiterWeerstand = 0.1;
var luchtWeerstand =  0.001;

/********* SETUP FUNCTIE *********/

function setup() {
  createCanvas(500, 500);
  balX=200;
  balY=100;  
}


/********* DRAW FUNCTIE *********/

function draw() {
  // Bepaal welk scherm getekend moet worden
  if (welkScherm == 0) {
    startScherm();
  } else if (welkScherm == 1) {
    gameScherm();
  } else if (welkScherm == 2) {
    gameOverScherm();
  }
}


/********* De verschillende schermen *********/

function startScherm() {
  // code voor start scherm
  background(0);
  fill(255);
  textSize(32);
  textAlign(CENTER);
  text("Klik om te starten", height/2, width/2);
}

function gameScherm() {
  // code voor het game scherm
  background(255);
  
  // bereken de nieuwe snelheid 
  verticaleBalSnelheid = verticaleBalSnelheid + zwaarteKracht;
   
  // Invloed van de luchtweerstand
  verticaleBalSnelheid = verticaleBalSnelheid - luchtWeerstand * verticaleBalSnelheid;  
  
  // bereken de Y positie van de bal op basis van de vertical bal snelheid
  balY = balY + verticaleBalSnelheid;
  
  // stuiter als de bal de onderkant raakt
  if ( balY  > ( height - balStraal ) ) {
    // set bal precies op de rand
    balY = height - balStraal;
    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;
    
    // verlaag snelheid door stuiter
    verticaleBalSnelheid = verticaleBalSnelheid - stuiterWeerstand * verticaleBalSnelheid;
  }
  
  // stuiter als de bal de onderkant raakt
  if ( balY  <  balStraal ) {
    // set bal precies op de rand
    balY = balStraal;

    // richting bal omdraaien
    verticaleBalSnelheid = verticaleBalSnelheid * -1;

    // verlaag snelheid door stuiter
    verticaleBalSnelheid = verticaleBalSnelheid - stuiterWeerstand * verticaleBalSnelheid;
   
  }
  tekenBal();
}

function gameOverScherm() {
  // code voor het game over scherm
  background(255);
}


/********* INVOER *********/

function mousePressed() {
  // als we in het start scherm getoond wordt en de muis knop wordt ingedrukt,
  // dan laten we het startScherm zien
  if (welkScherm==0) {
    startGame();
  }
}


/********* HULP FUNCTIES *********/

// Functie die wordt uitgevoerd als het spelletje moet beginnnen 
function startGame() {
  welkScherm=1;
}

function tekenBal() {
  fill(0);
  ellipse(balX, balY, balStraal * 2);
}

 

In deel 2 gaan we verder met het tekenen en verplaatsen van het racket, stay tuned.

Flappy Pong in p5.js (deel 2)

This entry was posted in Javascript, p5js, software, Uncategorized. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *