Leren programmeren animeren

Het is pakweg 2 jaar geleden dat ik met deze serie ben begonnen en daarmee de missie om jou, mijn trouwe lezer te leren programmeren. In mijn tweede blog hebben we samen een spel geprogrammeerd, maar omdat we samen tot de conclusie waren gekomen dat OrcadoInvaders iets te hoog gegrepen was voor je eerste introductie tot programmeren, was dat een tekstgebaseerd_computerspel spel. Herinner je je het weer? Ondertussen heb je al heel wat meer geleerd en ook zeker niet onbelangrijk is de P5js webeditor uitgekomen. Ik vermoed dat ik P5 wel eens eerder heb genoemd; het is de JavaScript bibliotheek die ik gebruik in de meeste van mijn code voorbeelden, waaronder TicTacToe en OrcadoInvaders. P5 is gebaseerd op processing.org een organisatie die net als ik, de ambitie heeft om meer mensen te leren programmeren. Maar als ik jou was zou ik mijn blog lezen, dan krijg je tenminste ooit nog uitgelegd wat 3-waarden-logica nou precies is en hoe dat werkt.

Maar niet vandaag. Vandaag gaan we weer samen een spelletje programmeren, maar nu met graphics. Na wat overwegingen wat nou een goed spel zou zijn om samen te maken is de keuze gevallen op ‘hooghouden’, er is een heel sub genre van dit spel op bijvoorbeeld funny games, maar volgens mij zijn die allemaal in flash, en gezien flash vanwege de vele veiligheidsproblemen binnenkort in geen enkele browsers meer draait, zijn dat allemaal niet echt goede voorbeelden. Gelukkig is het spelletje heel simpel; je hebt een bal, die valt naar beneden, als je er op klikt ‘schiet’ je hem omhoog, het doel is om de bal zo lang mogelijk hoog te houden voor dat die valt. We gaan dat doen in de hiervoor genoemde P5js webeditor.

Zodra je de webeditor opent zie je links de code en als je op de play-knop drukt zie je rechts het resultaat van de code. Klik maar eens op play, je ziet rechts een vierkant verschijnen. De code die daar voor zorgt staat links onder setup en draw. Setup wordt aan het begin 1x uitgevoerd en draw elke seconde 60x. In setup wordt een canvas aangemaakt van 400 breed en 400 hoog. Ik denk graag zo groot mogelijk dus dat mag je direct veranderen in:
createCanvas(windowWidth, windowHeight);
Hiermee maken we het canvas (waar we op gaan schilderen) zo breed en hoog als in het scherm past. In draw krijgt het canvas een grijze achtergrond kleur, dat is wat saai en aangezien hooghouden een voetbal spelletje is maken we dat meteen gras groen:
background(0,180,0);
Als het goed is, is het hele rechtervlak nu groen. Mocht dat nou niet zo zijn of als je ergens verderop de draad kwijt bent, dit is het eind resultaat, kan je spieken wat er mis is gegaan.

Ok, het gras is er, nu een bal. Daarvoor gaan we een object maken (ik heb je niet voor niets leren objectificeren). P5 ondersteunt om hiervoor een apart bestand aan te maken, en dat is eigenlijk mooier, maar voor de eenvoud hou ik het even in één bestand. Eerst maken we een bal functie die als ons object gaan dienen:

function Bal(){
}

De computer moet weten hoe groot de bal is en waar die op het scherm moet worden getekend, dus daarvoor maken we variabelen aan in de functie.

function Bal() {
  this.diameter = 50;
  this.x = 0;
  this.y = 0;
}

En de bal moet getekend worden, daarvoor maken we een functie aan:

function Bal() {
  this.diameter = 50;
  this.x = 0;
  this.y = 0;

  this.teken = function() {
    ellipse(this.x, this.y, this.diameter, this.diameter);
  };
}

In die functie staat nu dat we een ellips tekenen op coördinaten (0,0) en met de breedte en hoogte van de diameter. Aangezien een ellips met gelijke breedte en hoogte een perfecte cirkel is, gaat dit een cirkel tekenen. Om de bal te laten tekenen moeten we dit aanroepen in de draw functie. Eerst maken we de bal variabele aan in setup

function setup() {
  createCanvas(windowWidth, windowHeight);
  bal = new Bal();
}

Zodat we daarna in de draw functie hem kunnen laten tekenen.

function draw() {
  background(0,180,0);
  bal.teken()
}

Er verschijnt nu een witte kwart cirkel in de linker bovenhoek, laten we hem even in het midden zetten voor het gemak, verander daarvoor in het bal object de x en y waarden respectievelijk  windowWidth/2 en windowHeight/2. De bal staat nu midden in het scherm.

Nu gaan we wat leven in de bal brengen. Dat gaan we doen door zwaartekracht toe te voegen. In een hele volwassen versie van dit spel gebruiken we daarvoor een physics engine, dat is software die natuurkundige krachten nabootst, daarmee kunnen we rekening houden met het gewicht van de bal, de luchtweerstand, de gravitatie constante en dat soort dingen. Dat is echter overkill voor wat we aan het doen zijn, we gaan de bal een verticale snelheid geven, en de zwaartekracht verhoogt die snelheid neerwaarts. Eerst maar verticale snelheid aan het bal object toevoegen:

this.verticalesnelheid = 0;

En we maken een zwaartekracht functie die die snelheid beïnvloed en dan ook de y coördinaat van onze bal:

this.zwaartekracht = function() {
  this.verticalesnelheid += 0.2;
  this.y += this.verticalesnelheid;
};

Dan alleen nog zorgen dat de zwaartekracht toegepast wordt elke keer als we de bal tekenen, daarvoor zetten we onder bal.teken(), bal.zwaartekracht() en voilá de bal valt naar beneden. Klein probleempje, de bal verdwijnt al snel onder de grond. Dat is natuurlijk niet goed. We zullen in onze super eenvoudige versie van een physics engine de grond de bal laten stuiteren door een opwaartse kracht toe te voegen als de grond geraakt wordt, soort derde wet van Newton zeg maar:

if (this.y + this.diameter / 2 > windowHeight) {
  this.y = windowHeight - this.diameter / 2; // zorg dat de bal niet vastkomt in de grond
  this.verticalesnelheid *= -0.8;
} else {
  this.verticalesnelheid += 0.2;
}

En hup, de bal stuitert, het verdient geen nobelprijs voor de natuurkunde, of zelfs maar een voldoende op een middelbare school toets, maar dit is soort van hoe zwaartekracht werkt, en goed genoeg voor ons spelletje.

Nu moeten we de bal nog hoog kunnen houden, dat gaan we doen door er op te klikken. P5 heeft een ingebouwde functie om muisklikken te herkennen, alle functies die P5 ondersteunt kan je vinden in de referentie. We gaan gebruik maken va de touchStarted functie, in mijn ervaring werkt het dan goed met zowel muizen als met aanraakgevoelige schermen zoals mobiele telefoons.

function touchStarted() {
  bal.verticalesnelheid -= 20;
  return false;
}

Die return false wordt aangeraden in de reference dus dat doen we maar braaf. Het zal je inmiddels opgevallen zijn dat een negatieve snelheid de bal naar boven brengt en een positieve snelheid de bal naar beneden. Dat komt omdat het y-coördinaat 0 helemaal boven in het scherm ligt en het y-coördinaat met de schermhoogte helemaal onderin het scherm.

Dit werkt, alleen het maakt nu niet uit waar je klikt, dat maakt het een suf spelletje. Dit kan je nu makkelijk winnen door in je console met een interval muisklikken te genereren (je hebt immers leren automatiseren). We moeten dus kijken of er op de muis wordt geklikt. We hebben gelukkig ook de x en y coördinaten van de muis (of vinger) dus we hoeven alleen maar te controleren of de afstand tussen de muis en de bal gelijk of kleiner is dan de straal van de bal. Dit is normaal het moment dat ik me realiseer dat ik meer de straal dan de diameter gebruik en de code daar op aanpas, maar ik begin inmiddels de spreekwoordelijk prangende ogen van mijn redactrice in mijn rug te voelen gezien ik al ver over het aantal streefwoorden per blog ben (ik geef persoonlijk de ellenlange zinnen die ik maak de schuld, alsof ik een kommarecord probeer te verbreken…), dus dat mag je straks zelf doen. Dat is overigens ook de reden dat ik onze gezamenlijke flashback naar middelbare school wiskunde en de stelling va Pythagoras)  oversla (ja, daar hadden we nu eindelijk een keer ‘in het echt’ gebruik van kunnen maken) en gebruik maak van de euclidische afstandsfunctie die in P5 zit ingebakken:

if (dist(bal.x, bal.y, mouseX, mouseY) <= bal.diameter / 2) {
  bal.verticalesnelheid -= 20;
}

Op zich werkt het nu, maar het is wel een erg verticale aanpak, dat is wel heel erg agile, maar voor het spelletje niet echt leuk. Probeer zelf eens toe te voegen dat als je de bal links van het midden raakt hij naar rechts gaat en andersom (en vergeet niet van de zijkant muren te maken zodat je de bal niet kwijt raakt). Ik heb het in het eindresultaat zit, maar ik geloof dat je dit zonder spieken kan!

Als je dat gedaan hebt is het tijd voor het testen, dan blijkt dat het spelletje misschien iets te uitdagend is geworden. Met een klein beetje tweaken aan de gebruikte getallen en een scoreteller toe te voegen hebben we in de basis het spelletje. Dat valt echter ook buiten de scope van dit blog, maar dat moet je niet laten weerhouden om het zelf te doen en hoewel het het beste is om dat zonder hulp op te lossen hoeveel moeite je dat kost, kan ik me herinneren hoe frustrerend dat kon zijn toen ik net begon met programmeren, dus ook daarvoor een spiekbriefje.