logo CoderDojo

Web - Ontbijtkoekclicker

We gaan onze eigen versie van het spel Cookie Clicker maken met JavaScript en HTML!

Introductie

In deze instructie gaan we voornamelijk aan de slag met JavaScript. Het doel van de instructie is het maken van een spel dat je in je browser kan spelen. Het spel is gebaseerd op het bekende spel Cookie Clicker. Naast het coderen van de functionaliteit, kan je aan verschillende onderdelen van het spel een eigen draai geven!

Benodigdheden

Ten eerste is het handig om bekend te zijn met de concepten van een andere Dojo: Web - Development. Voor deze Dojo gebruiken we dezelfde editor als in de bovenstaande instructie.

Om meer tijd te kunnen besteden aan JavaScript en het personaliseren van het spel, beginnen we met een vooropgezette pagina. Deze bestaat uit de volgende bestanden: HTML, CSS en JavaScript. Deze zijn te downloaden via: Download bestanden. Zorg voordat je begint met de instructie dat je de pagina kan weergeven met je editor/plugin(s).

Extra: afbeeldingen lokaal opslaan

De afbeeldingen die we gebruiken als voorbeeld maken geen deel uit van de download. Dit zorgt dat je af en toe lange URLs in de code voorbij ziet komen. Deze URLs kan je eventueel vervangen door de afbeeldingen te downloaden. Zet gedownloade afbeeldingen in dezelfde directory als waar pagina.html zich bevind. Deze kan je vervolgens als volgt gebruiken:

<img src="VOORBEELDNAAM.jpg">

Overzicht van het spel

We beginnen met een samenvatting van het spel, zodat het duidelijk is waar we naartoe werken. De leukste manier om hier een idee over te krijgen is natuurlijk door het origineel te spelen.

Het spel bestaat uit pakweg vier verschillende onderdelen:

Relatie met de HTML code (1)

Het bestand pagina.html bevat een section met vier verschillende div tags:

<section>
	<!-- In deze div kan de speler op de Cookie klikken. --> 
	<div id="klikruimte"></div>
	<!-- In deze div laten we van elk type Clicker zien hoeveel we er hebben. --> 
	<div class="tierdisplay rows" id="actieveClickers"></div>
	<!-- In deze div kan de speler de verschillende Clickers kopen. --> 
	<div class="tierdisplay rows" id="clickerWinkel"></div>
	<!-- In deze div kan de speler Powerups voor de Clickers kopen. --> 
	<div class="tierdisplay table" id="powerupWinkel"></div>
</section>

Deze divs komen overeen met de onderdelen van het originele spel. Het belangrijkste onderdeel van de divs zijn de ids. Met behulp van de ids kunnen we deze divs bereiken en gebruiken in JavaScript.

Stel bijvoorbeeld dat we in het overzicht met actieve Clickers een Clicker toe willen voegen. Om te zorgen dat we vanuit de div met actieve Clickers opereren, bereiken we deze als volgt:

const actieveClickerDiv = document.getElementById("actieveClickers")

Dus: elk onderdeel van het spel komt overeen met een div in de HTML code! Als we in JavaScript iets aan de Powerups willen veranderen, zoeken we deze eerst op met de id!

Relatie met de HTML code (2)

We weten nu waar de onderdelen van het spel komen te staan, maar hoe zien die onderdelen er precies uit? En wat staat er in de bijbehorende divs?

Voor de meeste onderdelen is het antwoord hetzelfde: ze bestaan uit een lijst, met een segment voor elke rang (Engels: “tier”) Clicker. Als voorbeeld kunnen we naar de Powerups kijken:

<!-- In deze div kan de speler Powerups voor de Clickers kopen. --> 
<div class="tierdisplay table" id="powerupWinkel">
	<h3>Powerups</h3>

	<!-- Via deze div kan je de Deegroller Powerup kopen. Deze kan je als voorbeeld gebruiken. --> 
	<div class="tier0" onclick="koopPowerup(0)">
		<img class="icon" src="https://gartic.com.br/imgs/mural/__/__fera__/rolling-pin.png">
		<text>10$</text>
	</div>

	<div class="tier1" onClick="koopPowerup(1)">
	<img class="icon" src="???">
	<text>???$</text>
	</div>

	<div class="tier2" onClick="koopPowerup(2)">...</div>
	<div class="tier3" onClick="koopPowerup(3)">...</div>
	<div class="tier4" onClick="koopPowerup(4)">...</div>
</div>

De Deegroller Powerup (tier 0) is hier volledig uitgewerkt. Het belangrijkste onderdeel van dit voorbeeld is de class van de elementen in de lijst. De class werkt op een soortgelijke manier als de id, maar een class is niet uniek. Hierdoor kunnen we het class systeem voor alle drie de lijsten gebruiken die we gaan maken. Als iets met de goedkoopste Clicker te maken heeft, heeft het class="tier0". De een na goedkoopste Clicker heeft class="tier1", etc.

Als je een element zoekt, moet je nu echter wel zorgen dat je in de juiste lijst zoekt. De volgende code verkrijgt resultaten met class="tier0" uit alle lijsten (actieve Clickers, Clickers winkel, Powerup winkel).

const tierDiv = document.getElementsByClassName("tier0")

Dat is niet de bedoeling als we bijvoorbeeld iets aan de tier 0 Powerup willen veranderen. In plaats daarvan zorgen we dat we eerst naar de Powerups kijken, en vervolgens naar die van tier 0:

const actieveClickerDiv = document.getElementById("powerup")
const tierDiv = actieveClickerDiv.getElementsByClassName("tier0")[0]

Let op: omdat getElementsByClassName meerdere resultaten kan geven (in dit voorbeeld niet), kiezen we de eerste via [0].

Een klikbaar koekje

Aan het begin van het spel heb je nog geen Clickers en moet je handmatig je koekjes verdienen. Dit doe je door op de grote afbeelding van het koekje te klikken. Hier komen enkele dingen bij kijken:

Om dit te implementeren, moet je goed letten op de ids van de div- en h2 tags:

<div id="klikRuimte">
	<img src="https://www.pngall.com/wp-content/uploads/2016/07/Cookie-PNG.png" onClick="onClickCookie()">
	<br>
	<!-- In deze h2 tags laten we de huidige hoeveelheid geld zien, 
		en hoeveel geld we per seconde verdienen. --> 
		<h2 id="geld">Geld: 0$</h2>
		<h2 id="geldPerSeconde">Geld per seconde: 0$</h2>
</div>

Voor updateGeld heb je de h2 die het geld weergeeft nodig. Als je deze eenmaal gevonden hebt, kan je de inhoud van de h2 via innerText als volgt updaten:

geldH2.innerText = "Geld: " + hoeveelheid + "$"
Voorbeeldcode klikbaar koekje
let geld = 0

/* Voegt 1$ toe en update de pagina */ function onClickCookie() { geld = geld + 1

updateGeld(geld) <span style="color:#008000">/* Update de pagina! */</span>

}

/* Verander het geld bedrag dat op de pagina word weergegeven naar hoeveelheid. */ function updateGeld(hoeveelheid) { const geldH2 = document.getElementById("geld")

geldH2.innerText = <span style="color:#a31515">&#34;Geld: &#34;</span> + hoeveelheid + <span style="color:#a31515">&#34;$&#34;</span>

}

Eventueel kan je updateGeld ook direct gebruik laten maken van de globale variabele geld.

Clickers winkel

Als we eenmaal wat koekjes hebben, willen we het spel natuurlijk voor ons laten werken! Hiervoor gebruiken we Clickers, die je kan kopen bij de Clickers winkel. In pagina.html zien we een div met id=clickerWinkel; dit is waar we de Clickers te koop zullen zetten. In deze div vinden we de volgende HTML code:

<div class="tier1 ruimte" onClick="koopClicker(1)">
	<img class="icon" src="???">
	<text>???</text>
	<text>??? $</text>
</div> 

Hier kan je je eigen Clickers in zetten, met een plaatje en een prijs! Dit doe je door de ??? te vervangen zoals te zien is in het voorbeeld voor tier0. Je hoeft ze natuurlijk niet per se alle vijf toe te voegen.

Als je een paar Clickers hebt, kunnen we beginnen met het toevoegen van de functionaliteit. Laten we een paar doelen vaststellen:

Vooralsnog verdienen de Clickers dus nog geen koekjes voor ons, we houden enkel bij hoeveel we er van welke tier gekocht hebben.

Tip: omdat we het onderdeel Actieve Clickers nog niet hebben, is het lastig te zien of de winkel werkt. Het kan handig zijn om in clickerWinkel console.log(aantalClickers[tier]) te gebruiken, om te zien hoeveel je er hebt na aanschaf.

Voorbeeldcode Clicker winkel
const aantalClickers = [0, 0, 0, 0, 0]
const clickerKosten = [1, 10, 50, 100, 500]

let geld = 0 let maxAantalClickers = 14 /* Voor de basisfunctie ligt dit aan de grootte van je scherm. */

/* Koopt een Clicker, mits hier geld voor is en er nog ruimte is. Update de pagina. */ function koopClicker(tier) { if (aantalClickers[tier] >= maxAantalClickers) { return } if (geld < clickerKosten[tier]) { return }

geld = geld - clickerKosten[tier]
aantalClickers[tier] = aantalClickers[tier] + 1

<span style="color:#008000">/* Update de pagina! */</span>
updateGeld(geld)

}

Actieve Clickers inkomsten toevoegen

In deze sectie zorgen we dat de actieve Clickers geld genereren. Dit doen we door elke seconde het inkomen van alle actieve Clickers bij elkaar op te tellen. Het totaal voegen we toe aan de hoeveelheid koekjes die we al hebben. Een voorbeeld:

Clicker     Inkomen per seconde     Aantal     Totaal     
Kok144
Clicker tier 1212
Clicker tier 24312
Clicker tier 3000
Clicker tier 4000
Som:18

We hebben drie functies nodig:

Om de functie updateClickerGeld elke seconde uit te voeren kan je de volgende code gebruiken:

/* Zorgt dat updateClickerGeld elke seconde activeert. */
setInterval(updateClickerGeld, 1000)
Voorbeeldcode actieve Clickers inkomsten toevoegen
const aantalClickerTiers = 5

const clickerInkomsten = [1, 2, 4, 8, 32] const aantalClickers = [0, 0, 0, 0, 0]

function berekenGeldPerSeconde() { let totaal = 0

<span style="color:#00f">for</span> (<span style="color:#00f">let</span> tier = 0; tier &lt; aantalClickerTiers; tier++) {
    <span style="color:#00f">const</span> inkomsten = clickerInkomsten[tier]
    <span style="color:#00f">const</span> aantal = aantalClickers[tier]
    totaal += inkomsten * aantal
}

<span style="color:#00f">return</span> totaal

}

/* Voegt het geld van de Clickers toe aan het geld. Update de pagina. */ function updateClickerGeld() { geld = geld + berekenGeldPerSeconde()

<span style="color:#008000">/* Update de pagina! */</span>
updateGeld(geld)

}

/* Zorgt dat updateClickerGeld elke seconde activeert. */ setInterval(updateClickerGeld, 1000)

/* Verander het geld per seconde bedrag dat op de pagina word weergegeven naar hoeveelheid. */ function updateGeldPerSeconde(hoeveelheid) { const geldPerSecondeH2 = document.getElementById("geldPerSeconde")

geldPerSecondeH2.innerText = <span style="color:#a31515">&#34;Geld per seconde: &#34;</span> + hoeveelheid + <span style="color:#a31515">&#34;$&#34;</span>

}

Vergeet niet dat koopClicker (en later koopPowerup) updateGeldPerSeconde uitvoeren bij aankoop van een nieuwe Clicker!

Actieve Clickers weergeven

We kunnen eindelijk Clickers toevoegen, maar de pagina voelt nog erg leeg. Het zou leuk zijn om te zien welke Clickers we in dienst hebben! Hiervoor gebruiken we de div met id="actieveClickers". Dit is hoe de div er uit zou moeten zien na het kopen van twee Chefs:

<div class="tierdisplay rows" id="actieveClickers">
<h3>Actieve Clickers</h3>

<div class="tier0">
<!-- Per clicker van dit type voegen we een afbeelding toe. Er zijn nu 2 Chefs. --> 
<img src="https://pngimg.com/uploads/chef/chef_PNG54.png" class="icon">
<img src="https://pngimg.com/uploads/chef/chef_PNG54.png" class="icon">
</div>

<div class="tier1"></div>
...
</div>

We gebruiken hier weer hetzelfde systeem als voorheen, waarin elk onderdeel van het spel een div is, welke een lijst met class="tier" elementen bevat. Die elementen kunnen we dus vinden zoals voorheen! De vraag is nu: hoe voegen we hier afbeeldingen aan toe zoals in het bovenstaande voorbeeld?

Het antwoord: via document.createElement. Via deze functie kunnen we in de JavaScript code HTML elementen maken, en deze later in een ander HTML element toevoegen. Dit is te zien in het volgende voorbeeld:

const actieveClickerDiv = document.getElementById("actieveClickers")
const tierDiv = actieveClickerDiv.getElementsByClassName("tier0")[0]

const afbeelding = document.createElement("img")
afbeelding.src = "" /* jouw afbeelding bron */
afbeelding.className = "icon"

tierDiv.appendChild(afbeelding)

Het voorbeeld laat alleen niet zien hoe je dit voor een willekeurige tier doet. Als je dit lastig vindt, vraag dan gerust om hulp!

Voorbeeldcode actieve Clickers
/* Een lijst met de icoontjes van de Clickers. */
clickerIcons = [
    "https://pngimg.com/uploads/chef/chef_PNG54.png",
    /* tier 1 */
    /* tier 2 */
    /* tier 3 */
    /* tier 4 */
]

/* Voeg een nieuw icoontje toe aan de lijst met Actieve Clickers van tier tier */ function voegActieveClickerToe(tier) { const actieveClickerDiv = document.getElementById("actieveClickers") const tierDiv = actieveClickerDiv.getElementsByClassName("tier" + tier)[0]

<span style="color:#00f">const</span> afbeelding = document.createElement(<span style="color:#a31515">&#34;img&#34;</span>)
afbeelding.src = clickerIcons[tier]
afbeelding.className = <span style="color:#a31515">&#34;icon&#34;</span>

tierDiv.appendChild(afbeelding)

}

Van belang: gebruik voegActieveClickerToe(tier) aan het einde van de functie koopClicker, zodat het kopen van een Clicker van tier tier een nieuwe aan de lijst voor tier Clickers toevoegt.

Clicker Powerup winkel

We kunnen nu handmatig koekjes verdienen en deze investeren in Clickers. De Clickers zijn te zien in het overzicht met actieve Clickers. Het spel voelt echter een beetje eenzijdig: klikken > kopen > klikken > … > einde(?).

Een manier om extra strategie toe te voegen is het introduceren van Powerups. Een Powerup zorgt dat de actieve Clickers van een bepaalde tier beter worden. In het voorbeeld kan je voor de Chef een deegroller kopen: deze zorgt dat de Chef twee keer zoveel koekjes per seconde kan produceren.

In deze instructie zullen we één Powerup per tier Clicker maken. Dit is net iets simpeler dan in het originele spel, maar je kan het later uitbreiden! De HTML code voor Powerups lijkt erg op die van de Clicker winkel:

<!-- In deze div kan de speler Powerups voor de Clickers kopen. --> 
<div class="tierdisplay table" id="powerupWinkel">
<h3>Powerups</h3>
	<!-- Via deze div kan je de Deegroller Powerup kopen. Deze kan je als voorbeeld gebruiken. --> 
	<div class="tier0" onclick="koopPowerup(0)">
		<img class="icon" src="https://gartic.com.br/imgs/mural/__/__fera__/rolling-pin.png">
		<text>10$</text>
	</div>

    <div class="tier1" onClick="koopPowerup(1)">
		<img class="icon" src="???">
		<text>???$</text>
	</div>

	...
</div>

Ook hier komt een kort plan van aanpak van pas:

Ook moeten we zorgen dat we ergens een lijstje bijhouden met de inkomsten per seconde van elke Clicker. Als de Powerup is gekocht, vermenigvuldigen we die inkomsten met twee.

Tip: als de aanschaf succesvol is, kan je de Powerup uit de lijst halen via:

const tierDiv = ...
tierDiv.remove()
Voorbeeldcode Powerup winkel
const powerupKosten = [10, 100, 500, 1000, 5000]

let geld = 0 const beschikbarePowerups = [true, true, true, true, true] const clickerInkomsten = [1, 2, 4, 8, 32]

/* Koopt een Powerup, mits hier geld voor is en de powerup nog niet gekocht is. Update de pagina. */ function koopPowerup(tier) { if (beschikbarePowerups[tier] == false) { return } if (geld < powerupKosten[tier]) { return }

beschikbarePowerups[tier] = <span style="color:#00f">false</span>;
geld = geld - powerupKosten[tier]
clickerInkomsten[tier] = clickerInkomsten[tier] * 2

<span style="color:#008000">/* Update de pagina! */</span>
verwijderPowerup(tier)
updateGeldPerSeconde(berekenGeldPerSeconde())
updateGeld(geld)

}

/* Verwijder de Powerup voor tier tier uit de lijst. */ function verwijderPowerup(tier) { const powerupDiv = document.getElementById("powerupWinkel") const tierDiv = powerupDiv.getElementsByClassName("tier" + tier)[0]

tierDiv.remove()

}

Uitbreidingen

Allereerst: gefeliciteerd met het halen van het einde van deze instructie!

Er zijn een hoop uitbreidingen mogelijk op het basisconcept:

Hier kan je zelf mee aan de slag gaan en natuurlijk extra hulp bij vragen.

Laat ons eventueel weten of er nog andere uitbreidingen zijn die je in deze instructie zou willen zien!

Top secret

De volledige voorbeeldcode
/* Een lijst met de icoontjes van de Clickers. */
clickerIcons = [
    "https://pngimg.com/uploads/chef/chef_PNG54.png",
    /* tier 1 */
    /* tier 2 */
    /* tier 3 */
    /* tier 4 */
]

/* De volgende functies beïnvloeden het uiterlijk van de pagina. */

/* Voeg een nieuw icoontje toe aan de lijst met Actieve Clickers van tier tier */ function voegActieveClickerToe(tier) { const actieveClickerDiv = document.getElementById("actieveClickers") const tierDiv = actieveClickerDiv.getElementsByClassName("tier" + tier)[0]

<span style="color:#00f">const</span> afbeelding = document.createElement(<span style="color:#a31515">&#34;img&#34;</span>)
afbeelding.src = clickerIcons[tier]
afbeelding.className = <span style="color:#a31515">&#34;icon&#34;</span>

tierDiv.appendChild(afbeelding)

}

/* Verander het geld bedrag dat op de pagina word weergegeven naar hoeveelheid. */ function updateGeld(hoeveelheid) { const geldH2 = document.getElementById("geld")

geldH2.innerText = <span style="color:#a31515">&#34;Geld: &#34;</span> + hoeveelheid + <span style="color:#a31515">&#34;$&#34;</span>

}

/* Verander het geld per seconde bedrag dat op de pagina word weergegeven naar hoeveelheid. */ function updateGeldPerSeconde(hoeveelheid) { const geldPerSecondeH2 = document.getElementById("geldPerSeconde")

geldPerSecondeH2.innerText = <span style="color:#a31515">&#34;Geld per seconde: &#34;</span> + hoeveelheid + <span style="color:#a31515">&#34;$&#34;</span>

}

/* Verwijder de Powerup voor tier tier uit de lijst. */ function verwijderPowerup(tier) { const powerupDiv = document.getElementById("powerupWinkel") const tierDiv = powerupDiv.getElementsByClassName("tier" + tier)[0]

tierDiv.remove()

}

/* Vanaf hier focussen we op de functionaliteit! */

/* De volgende gegevens veranderen niet. */ const aantalClickerTiers = 5 const clickerKosten = [1, 10, 50, 100, 500] const powerupKosten = [10, 100, 500, 1000, 5000]

/* De volgende gevevens kunnen veranderen. */ let geld = 0 let maxAantalClickers = 14 /* Voor de basisfunctie ligt dit aan de grootte van je scherm. */ const clickerInkomsten = [1, 2, 4, 8, 32] const aantalClickers = [0, 0, 0, 0, 0] const beschikbarePowerups = [true, true, true, true, true]

/* Voegt 1$ toe en update de pagina */ function onClickCookie() { geld = geld + 1

updateGeld(geld) <span style="color:#008000">/* Update de pagina! */</span>

}

/* Berekent het geld dat de Clickers genereren per seconde. */ function berekenGeldPerSeconde() { let totaal = 0

<span style="color:#00f">for</span> (<span style="color:#00f">let</span> tier = 0; tier &lt; aantalClickerTiers; tier++) {
    <span style="color:#00f">const</span> inkomsten = clickerInkomsten[tier]
    <span style="color:#00f">const</span> aantal = aantalClickers[tier]
    totaal += inkomsten * aantal
}

<span style="color:#00f">return</span> totaal

}

/* Koopt een Powerup, mits hier geld voor is en de Powerup nog niet gekocht is. Update de pagina. */ function koopPowerup(tier) { if (beschikbarePowerups[tier] == false) { return } if (geld < powerupKosten[tier]) { return }

beschikbarePowerups[tier] = <span style="color:#00f">false</span>;
geld = geld - powerupKosten[tier]
clickerInkomsten[tier] = clickerInkomsten[tier] * 2

<span style="color:#008000">/* Update de pagina! */</span>
verwijderPowerup(tier)
updateGeldPerSeconde(berekenGeldPerSeconde())
updateGeld(geld)

}

/* Koopt een Clicker, mits hier geld voor is en er nog ruimte is. Update de pagina. */ function koopClicker(tier) { if (aantalClickers[tier] >= maxAantalClickers) { return } if (geld < clickerKosten[tier]) { return }

geld = geld - clickerKosten[tier]
aantalClickers[tier] = aantalClickers[tier] + 1

<span style="color:#008000">/* Update de pagina! */</span>
voegActieveClickerToe(tier)
updateGeldPerSeconde(berekenGeldPerSeconde())
updateGeld(geld)

}

/* Voegt het geld van de Clickers toe aan het geld. Update de pagina. */ function updateClickerGeld() { geld = geld + berekenGeldPerSeconde()

<span style="color:#008000">/* Update de pagina! */</span>
updateGeld(geld)

}

/* Zorgt dat updateClickerGeld elke seconde activeert. */ setInterval(updateClickerGeld, 1000)

Licentie

Deze instructies worden, net als alle andere instructies van CoderDojo Nijmegen, aangeboden onder een Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Licentie.

Creative Commons License