In deze tutorial zal ik proberen de interessante wereld van zeshoekige tegel-gebaseerde games te introduceren met behulp van de eenvoudigste benaderingen. U leert hoe u een tweedimensionale matrixgegevens omzet naar een overeenkomstige hexagonale niveau-indeling op het scherm en omgekeerd. Aan de hand van de opgedane informatie maken we een zeshoekig mijnenveger-spel in twee verschillende hexagonale lay-outs.
Dit zal je op gang helpen met het verkennen van eenvoudige zeshoekige bordspellen en puzzelspellen en zal een goed startpunt zijn om ingewikkeldere benaderingen te leren, zoals het axiale of kubieke hexagonale coördinatenstelsel.
In de huidige generatie casual gaming zien we niet veel games die een zeshoekige, op tegels gebaseerde benadering gebruiken. Degenen die we tegenkomen zijn meestal puzzelspellen, bordspellen of strategiespellen. Ook wordt aan de meeste van onze vereisten voldaan door de vierkante rasterbenadering of isometrische benadering. Dit leidt tot de natuurlijke vraag: "Waarom hebben we een andere en duidelijk gecompliceerde zeshoekige benadering nodig?" Laten wij het uitzoeken.
Dus wat maakt de zeshoekige, op tegels gebaseerde benadering relevant, omdat we al andere benaderingen hebben geleerd en geperfectioneerd? Laat me een paar redenen noemen.
Ik zou zeggen dat de laatste reden voldoende zou moeten zijn om deze nieuwe aanpak onder de knie te krijgen. Het toevoegen van dat unieke gameplay-element aan je spellogica zou het verschil kunnen maken en je in staat stellen een geweldige game te maken.
De andere redenen zijn puur technisch en zouden alleen van kracht worden als je te maken hebt met ingewikkelde algoritmen of grotere tegelsets. Er zijn ook veel andere aspecten die kunnen worden genoemd als voordelen van de zeshoekige benadering, maar de meeste zullen afhangen van de persoonlijke interesse van de speler.
Een zeshoek is een polygoon met zes zijden, en een zeshoek met alle zijden van dezelfde lengte wordt een regelmatige zeshoek genoemd. Voor theoriedoeleinden beschouwen we onze hexagonale tegels als gewone zeshoeken, maar deze kunnen in de praktijk worden platgedrukt of uitgerekt.
Het interessante is dat een zeshoek op twee verschillende manieren kan worden geplaatst: de puntige hoeken kunnen verticaal of horizontaal worden uitgelijnd. Wanneer puntige toppen verticaal uitgelijnd zijn, wordt dit a genoemd horizontaal lay-out, en wanneer ze horizontaal zijn uitgelijnd, wordt dit a genoemd verticaal lay-out. U denkt misschien dat de namen onjuist zijn met betrekking tot de verstrekte uitleg. Dit is niet het geval omdat de naamgeving niet wordt gedaan op basis van de puntige hoeken, maar de manier waarop een raster van tegels wordt neergelegd. De afbeelding hieronder toont de verschillende tegeluitlijningen en bijbehorende lay-outs.
De keuze van de lay-out is volledig afhankelijk van de visuals en gameplay van je game. Toch eindigt uw keuze hier niet, omdat elk van deze lay-outs op twee verschillende manieren kan worden geïmplementeerd.
Laten we eens kijken naar een horizontale zeshoekige rasterlay-out. Alternatieve rijen van het raster moeten horizontaal worden verschoven met hexTileWidth / 2
. Dit betekent dat we konden kiezen om de oneven rijen of de even rijen te verschuiven. Als we ook de bijbehorende weergeven rij, kolom waarden, deze varianten zien er als volgt uit.
Evenzo kan de verticale lay-out in twee variaties worden geïmplementeerd, terwijl alternatieve kolommen worden verrekend met hexTileHeight / 2
zoals hieronder getoond.
Ga vanaf hier verder met het verwijzen naar de broncode die bij deze tutorial is meegeleverd voor een beter begrip.
De bovenstaande afbeeldingen, met de rijen en kolommen weergegeven, maken het gemakkelijker om een directe correlatie te visualiseren met een tweedimensionale array die de niveaudata opslaat. Laten we zeggen dat we een eenvoudige tweedimensionale array hebben levelData
zoals hieronder.
var levelData = [[0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0 ], [0,0,0,0,0]]
Om het visualiseren te vergemakkelijken, zal ik hier het beoogde resultaat weergeven in zowel verticale als horizontale variaties.
Laten we beginnen met horizontale lay-out, de afbeelding aan de linkerkant. In elke rij worden, indien afzonderlijk genomen, de aangrenzende tegels horizontaal verschoven ten opzichte van hexTileWidth
. Alternatieve rijen worden horizontaal gecompenseerd door een waarde van hexTileWidth / 2
. Het verticale hoogteverschil tussen elke rij is hexTileHeight * 3/4
.
Om te begrijpen hoe we tot een dergelijke waarde voor de hoogteverschuiving kwamen, moeten we rekening houden met het feit dat de bovenste en onderste driehoekige gedeelten van een horizontaal geplaatst hexagon precies hexTileHeight / 4
.
Dit betekent dat de zeshoek een rechthoekig heeft hexTileHeight / 2
gedeelte in het midden, een driehoek hexTileHeight / 4
gedeelte bovenop en een omgekeerde driehoek hexTileHeight / 4
gedeelte aan de onderkant. Deze informatie is voldoende om de code te maken die nodig is om het hexagonale raster op het scherm te leggen.
var verticalOffset = hexTileHeight * 3/4; var horizontalOffset = hexTileWidth; var startX; var startY; var startXInit = hexTileWidth / 2; var startYInit = hexTileHeight / 2; var hexTile; for (var i = 0; i < levelData.length; i++) if(i%2!==0) startX=2*startXInit; else startX=startXInit; startY=startYInit+(i*verticalOffset); for (var j = 0; j < levelData[0].length; j++) if(levelData[i][j]!=-1) hexTile= new HexTile(game, startX, startY, 'hex',false,i,j,levelData[i][j]); hexGrid.add(hexTile); startX+=horizontalOffset;
Met de Hextile
prototype, ik heb een aantal extra functionaliteiten toegevoegd aan de Phaser.Sprite
prototype waarmee het de ik
en j
waarden. De code plaatst in wezen een nieuwe zeshoekige tegel sprite
op startX
en starty
. Deze code kan worden gewijzigd om de even-offset-variant weer te geven door een operator in de. Te verwijderen als
voorwaarde zoals deze: if (i% 2 === 0)
.
Voor een verticale lay-out (de afbeelding op de rechter helft) worden aangrenzende tegels in elke kolom verticaal versprongen door hexTileHeight
. Elke alternatieve kolom wordt verticaal verschoven door hexTileHeight / 2
. Door de logica toe te passen die we hebben toegepast voor verticale offset voor de horizontale lay-out, kunnen we zien dat de horizontale offset voor de verticale lay-out tussen aangrenzende tegels op een rij is hexTileWidth * 3/4
. De bijbehorende code staat hieronder.
var verticalOffset = hexTileHeight; var horizontalOffset = hexTileWidth * 3/4; var startX; var startY; var startXInit = hexTileWidth / 2; var startYInit = hexTileHeight / 2; var hexTile; for (var i = 0; i < levelData.length; i++) startX=startXInit; startY=2*startYInit+(i*verticalOffset); for (var j = 0; j < levelData[0].length; j++) if(j%2!==0) startY=startY+startYInit; else startY=startY-startYInit; if(levelData[i][j]!=-1) hexTile= new HexTile(game, startX, startY, 'hex', true,i,j,levelData[i][j]); hexGrid.add(hexTile); startX+=horizontalOffset;
Op dezelfde manier als bij de horizontale lay-out kunnen we overschakelen naar de even-offset-variant door de !
operator in de top als
staat. Ik gebruik een Phaser Groep
om alle te verzamelen hexTiles
genaamd Hexgrid
. Voor de eenvoud gebruik ik het middelpunt van de zeshoekige tegelafbeelding als een anker, anders moeten we ook rekening houden met de beeldverschuivingen.
Een ding om op te letten is dat de waarden voor de tegelbreedte en tegelhoogte in de horizontale lay-out niet gelijk zijn aan de waarden voor de tegelbreedte en de tegelhoogte in de verticale lay-out. Maar als we dezelfde afbeelding voor beide lay-outs gebruiken, kunnen we het tegelbeeld gewoon 90 graden roteren en de waarden van tegelbreedte en tegelhoogte verwisselen.
De array-naar-plaatsing logica was interessant eenvoudig, maar het omgekeerde is niet zo eenvoudig. Bedenk dat we de array-index moeten vinden van de zeshoekige tegel waarop we hebben getikt. De code om dit te bereiken is niet mooi en wordt meestal met vallen en opstaan bereikt.
Als we rekening houden met de horizontale lay-out, kan het lijken alsof het middelste rechthoekige deel van de zeshoekige tegel ons gemakkelijk kan helpen om de j
waarde omdat het gewoon een kwestie is van het verdelen van de X
waarde door hexTileWidth
en het nemen van de integer waarde. Maar tenzij we het weten ik
waarde, we weten niet of we op een vreemde of zelfs rij staan. Een geschatte waarde van ik
kan worden gevonden door de y-waarde te delen door hexTileHeight * 3/4
.
Nu komen de gecompliceerde delen van de zeshoekige tegel: de bovenste en onderste driehoekige delen. De onderstaande afbeelding helpt ons het probleem bij de hand te begrijpen.
De gebieden 2, 3, 5, 6, 8 en 9 vormen samen één tegel. Het meest gecompliceerde deel is om te bepalen of de getapte positie in 1/2 of 3/4 of 7/8 of 9/10 ligt. Hiervoor moeten we alle afzonderlijke driehoekige gebieden in beschouwing nemen en ertegenaan kijken met behulp van de helling van de schuine rand.
Deze helling kan worden gevonden uit de hoogte en breedte van elk driehoekig gebied, die respectievelijk zijn hexTileHeight / 4
en hexTileWidth / 2
. Laat me je de functie tonen die dit doet.
function findHexTile () var pos = game.input.activePointer.position; pos.x- = hexGrid.x; pos.y- = hexGrid.y; var xVal = Math.floor ((pos.x) / hexTileWidth); var yVal = Math.floor ((pos.y) / (hexTileHeight * 3/4)); var dX = (pos.x)% hexTileWidth; var dY = (pos.y)% (hexTileHeight * 3/4); var slope = (hexTileHeight / 4) / (hexTileWidth / 2); var caldY = dX * slope; var delta = hexTileHeight / 4-caldY; if (yVal% 2 === 0) // correctie moet gebeuren in driehoekige delen & de offset-rijen if (Math.abs (delta)> dY) if (delta> 0) // oneven rij rechtsonder half xVal--; yVal--; else // oneven rij linksonder half yVal--; else if (dX> hexTileWidth / 2) // beschikbare waarden werken niet voor even rij rechtsonder als half (dY<((hexTileHeight/2)-caldY))//even row bottom right half yVal--; else if(dY>caldY) // oneven rij rechtsboven en midden rechts helften xVal--; else // even rij linksonder half yVal--; pos.x = yVal; pos.y = xval; terugkeerpositie;
Eerst vinden we xval
en yVal
op dezelfde manier als voor een vierkant raster. Dan vinden we de resterende horizontale (dX
) en verticaal (dY
) waarden na het verwijderen van de tegelvermenigvuldigeroffset. Met behulp van deze waarden proberen we uit te zoeken of het punt zich in een van de gecompliceerde driehoekige gebieden bevindt.
Indien gevonden, brengen we overeenkomstige wijzigingen aan in de beginwaarden van xval
en yVal
. Zoals ik al eerder zei, is de code niet mooi en niet eenvoudig. De makkelijkste manier om dit te begrijpen zou zijn om te bellen findHexTile
op muisbeweging, en zet dan console.log
in elk van die omstandigheden en beweeg de muis over verschillende gebieden binnen één zeshoekige tegel. Op deze manier kunt u zien hoe elk intra-hexagonaal gebied wordt behandeld.
De codewijzigingen voor de verticale lay-out worden hieronder weergegeven.
function findHexTile () var pos = game.input.activePointer.position; pos.x- = hexGrid.x; pos.y- = hexGrid.y; var xVal = Math.floor ((pos.x) / (hexTileWidth * 3/4)); var yVal = Math.floor ((pos.y) / (hexTileHeight)); var dX = (pos.x)% (hexTileWidth * 3/4); var dY = (pos.y)% (hexTileHeight); var slope = (hexTileHeight / 2) / (hexTileWidth / 4); var caldX = dY / slope; var delta = hexTileWidth / 4-caldX; if (xVal% 2 === 0) if (dX> Math.abs (delta)) // even links else // oneven recht if (delta> 0) // oneven rechts onderaan xVal--; yVal--; else // oneven rechtsboven xVal--; else if (delta> 0) if (dX4. Buren zoeken
Nu we de tegel hebben gevonden waarop we hebben getikt, zoeken we alle zes aangrenzende tegels. Dit is een heel eenvoudig probleem om op te lossen als we het raster visueel analyseren. Laten we de horizontale lay-out eens bekijken.
De afbeelding hierboven toont het oneven en even rijen van een horizontaal opgemaakt zeshoekig raster wanneer een middelste tegel de waarde heeft van
0
voor beideik
enj
. Uit de afbeelding wordt duidelijk dat als de rij oneven is, dan voor een tegel opi, j
de buren zijni, j-1
,i-1, j-1
,i-1, j
,i, j + 1
,i + 1, j
, eni + 1, j-1
. Wanneer de rij even is, dan voor een tegel opi, j
de buren zijni, j-1
,i-1, j
,i-1, j + 1
,i, j + 1
,i + 1, j + 1
, eni + 1, j
. Dit kan eenvoudig handmatig worden berekend.Laten we een soortgelijk beeld analyseren voor het oneven en even kolommen van een verticaal uitgelijnd zeshoekig raster.
Wanneer we een oneven kolom hebben, een tegel op
i, j
zal hebbeni, j-1
,i-1, j-1
,i-1, j
,i-1, j + 1
,i, j + 1
, eni + 1, j
als buren. Evenzo zijn de buren voor een even kolomi + 1, j-1
,i, j-1
,i-1, j
,i, j + 1
,i + 1, j + 1
, eni + 1, j
.5. Hexagonale mijnenveger
Met de bovenstaande kennis kunnen we proberen een zeshoekig mijnenveger-spel te maken in de twee verschillende lay-outs. Laten we de functies van een mijnenveger-game afbreken.
- Er zal N aantal mijnen verborgen zijn in het rooster.
- Als we op een tegel met een mijn tikken, is het spel afgelopen.
- Als we op een tegel tikken die een aangrenzende mijn heeft, wordt het aantal mijnen er direct omheen weergegeven.
- Als we op een mijn tikken zonder aangrenzende mijnen, zou dit leiden tot het onthullen van alle verbonden tegels die geen mijnen hebben.
- We kunnen tikken en vasthouden om een tegel als mijn te markeren.
- Het spel is afgelopen als we alle tegels zonder mijnen onthullen.
We kunnen gemakkelijk een waarde opslaan in de
levelData
array om een mijn aan te geven. Dezelfde methode kan worden gebruikt om de waarde van nabijgelegen mijnen in de array-index van de aangrenzende tegels te vullen.Bij het starten van het spel zullen we willekeurig de
levelData
array met N aantal mijnen. Hierna zullen we de waarden voor alle naburige tegels bijwerken. We gebruiken een recursieve methode om alle verbonden lege tegels te laten zien als de speler op een tegel tikt die geen mijn heeft als buurman.Niveaugegevens
We moeten een mooi uitziend zeshoekig raster maken, zoals in de onderstaande afbeelding.
Dit kan gedaan worden door slechts een deel van de
levelData
matrix. Als we gebruiken-1
als de waarde voor een niet-bruikbare tile en0
als de waarde voor een bruikbare tegel, dan onzelevelData
voor het bereiken van het bovenstaande resultaat zal er als volgt uitzien.// horizontale tegelvormige niveau var levelData = [[-1, -1, -1,0,0,0,0,0,0,0, -1, -1, -1], [-1, -1 , 0,0,0,0,0,0,0,0, -1, -1, -1], [-1, -1,0,0,0,0,0,0,0,0, 0, -1, -1], [-1,0,0,0,0,0,0,0,0,0, -1, -1], [-1,0,0,0, 0,0,0,0,0,0,0,0,0, -1], [0,0,0,0,0,0,0,0,0,0,0,0,0-1], [ 0,80,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0] 0, -1], [-1,0,0,0,0,0,0,0,0,0,0, -1], [-1,0,0,0,0,0, 0,0,0,0,0, -1, -1], [-1, -1,0,0,0,0,0,0,0,0,0, -1, -1], [ -1, -1,0,0,0,0,0,0,0,0, -1, -1, -1], [-1, -1, -1,0,0,0,0, 0,0,0, -1, -1, -1]];Tijdens het doorlopen van de array, zouden we alleen hexagonale tegels toevoegen als de
levelData
heeft de waarde van0
. Voor de verticale uitlijning hetzelfdelevelData
kan worden gebruikt, maar dat zouden we wel moeten doen transponeren de array. Hier is een handige methode die dit voor u kan doen.levelData = transpose (levelData); // ... functie transponeren (a) return Object.keys (a [0]). Map (functie (c) return a.map (functie (r) return r [c];););Mijnen toevoegen en buren bijwerken
Standaard is onze
levelData
heeft slechts twee waarden,-1
en0
, waarvan we alleen het gebied zouden gebruiken0
. Om aan te geven dat een tegel een mijn bevat, kunnen we de waarde van gebruiken10
.Een lege zeshoekige tegel kan maximaal zes mijnen in de buurt hebben, omdat het zes aangrenzende tegels heeft. We kunnen deze informatie ook opslaan in de
levelData
zodra we alle mijnen hebben toegevoegd. Kort gezegd, alevelData
index met een waarde van10
heeft een mijn en als deze waarden bevat van0
naar6
, die het aantal aangrenzende mijnen aangeeft. Na het vullen van mijnen en het bijwerken van buren, als een arrayelement nog steeds is0
, het geeft aan dat het een lege tegel is zonder aangrenzende mijnen.We kunnen de volgende methoden gebruiken voor onze doeleinden.
function addMines () var tileType = 0; var tempArray = []; var newPt = nieuwe Phaser.Point (); for (var i = 0; i < levelData.length; i++) for (var j = 0; j < levelData[0].length; j++) tileType=levelData[i][j]; if(tileType===0) newPt=new Phaser.Point(); newPt.x=i; newPt.y=j; tempArray.push(newPt); for (var i = 0; i < numMines; i++) newPt=Phaser.ArrayUtils.removeRandomItem(tempArray); levelData[newPt.x][newPt.y]=10;//10 is mine updateNeighbors(newPt.x,newPt.y); function updateNeighbors(i,j)//update neighbors around this mine var tileType=0; var tempArray=getNeighbors(i,j); var tmpPt; for (var k = 0; k < tempArray.length; k++) tmpPt=tempArray[k]; tileType=levelData[tmpPt.x][tmpPt.y]; levelData[tmpPt.x][tmpPt.y]=tileType+1;Voor elke mijn toegevoegd
addMines
, we verhogen de arraywaarde die in al zijn buren is opgeslagen. DegetNeighbors
methode retourneert geen tegel die buiten ons effectieve gebied ligt of als deze een mijn bevat.Tik op Logica
Wanneer de speler op een tegel tikt, moeten we het bijbehorende array-element vinden met behulp van de
findHexTile
methode eerder uitgelegd. Als de tile-index zich binnen ons effectieve gebied bevindt, vergelijken we de waarde in de array-index om te zien of het een mijn of een lege tegel is.function onTap () var tile = findHexTile (); if (! checkforBoundary (tile.x, tile.y)) if (checkForOccuppancy (tile.x, tile.y)) if (levelData [tile.x] [tile.y] == 10) // console .log ( 'arm'); var hexTile = hexGrid.getByName ("tile" + tile.x + "_" + tile.y); if (! hexTile.revealed) hexTile.reveal (); // game over else var hexTile = hexGrid.getByName ("tile" + tile.x + "_" + tile.y); if (! hexTile.revealed) if (levelData [tile.x] [tile.y] === 0) //console.log('recursive reveal '); recursiveReveal (tile.x, tile.y); else //console.log('reveal '); hexTile.reveal (); revealedTiles ++; infoTxt.text = 'gevonden' + reveatedTiles + 'of' + blankTiles;We houden het totale aantal blanco tegels bij met behulp van de variabele
blankTiles
en het aantal tegels onthuld met behulp vanrevealedTiles
. Als ze eenmaal gelijk zijn, hebben we de game gewonnen.Wanneer we op een tegel tikken met een matrixwaarde van
0
, we moeten recursief de regio onthullen met alle verbonden blanco tegels. Dit wordt gedaan door de functierecursiveReveal
, die de tegelindices van de getapte tegel ontvangt.functie recursiveReveal (i, j) var newPt = nieuwe Phaser.Point (i, j); var hexTile; var tempArray = [newPt]; var buren; while (tempArray.length) newPt = tempArray [0]; var neighbors = getNeighbors (newPt.x, newPt.y); while (neighbors.length) newPt = neighbors.shift (); Hextile = hexGrid.getByName ( "tile" + newPt.x + "_" + newPt.y); if (! hexTile.revealed) hexTile.reveal (); revealedTiles ++; if (levelData [newPt.x] [newPt.y] === 0) tempArray.push (newPt); newPt = tempArray.shift (); // het leek een punt zonder buurman soms ontsnapt aan de iteratie zonder geopenbaard te worden, vang het hier hexTile = hexGrid.getByName ("tile" + newPt.x + "_" + newPt.y ); if (! hexTile.revealed) hexTile.reveal (); revealedTiles ++;In deze functie vinden we de buren van elke tegel en onthullen de waarde van die tegel, terwijl ondertussen buurtegels aan een array worden toegevoegd. We blijven dit herhalen met het volgende element in de array totdat de array leeg is. De recursie stopt wanneer we matrixelementen met een mijn ontmoeten, wat wordt gegarandeerd door het feit dat
getNeighbors
zal geen tegel met een mijn teruggeven.Tegels markeren en onthullen
U moet gemerkt hebben dat ik het gebruik
hexTile.reveal ()
, wat mogelijk wordt gemaakt door eenHextile
prototype dat de meeste kenmerken van onze zeshoekige tegel behoudt. ik gebruik deonthullen
functie om de tegelwaardetekst weer te geven en de kleur van de tegel in te stellen. Evenzo is detoggleMark
functie wordt gebruikt om de tegel als mijn te markeren als we tikken en vasthouden.Hextile
heeft ook eenonthuld
attribuut dat bijhoudt of het wordt aangeboord en onthuld of niet.HexTile.prototype.reveal = function () this.tileTag.visible = true; this.revealed = true; if (this.type == 10) this.tint = '0xcc0000'; else this.tint = '0x00cc00'; HexTile.prototype.toggleMark = function () if (this.marked) this.marked = false; this.tint = "0xFFFFFF; else this.marked = true; this.tint = "0x0000cc;Bekijk de zeshoekige mijnenveger met onderstaande horizontale oriëntatie. Tik om tegels te onthullen en tik op om mijnen te markeren. Er is vanaf nu geen game over, maar als je een waarde van onthult
10
, dan is het hasta la vista baby!Wijzigingen voor de verticale versie
Omdat ik hetzelfde beeld van een zeshoekige tegel gebruik voor beide richtingen, roteer ik de Sprite voor de verticale uitlijning. De onderstaande code in de
Hextile
prototype doet dit.if (isVertical) this.rotation = Math.PI / 2;De mijnenvegerlogica blijft hetzelfde voor het verticaal uitgelijnde zeshoekige raster met het verschil voor
findHextile
engetNeighbors
logica die nu het uitlijningsverschil moet accommoderen. Zoals eerder vermeld, moeten we ook de transponering van de niveaure array met bijbehorende lay-outlus gebruiken.Bekijk de verticale versie hieronder.
De rest van de code in de bron is eenvoudig en duidelijk. Ik zou graag willen dat je de ontbrekende herstart-, spelwinst- en game-over-functionaliteit toevoegt.
Conclusie
Deze benadering van een spel met zeshoekige tegels op basis van een tweedimensionale matrix is meer een benadering van een leek. Interessantere en functionelere benaderingen zijn het wijzigen van het coördinatensysteem in verschillende typen met behulp van vergelijkingen.
De belangrijkste zijn axiale coördinaten en kubieke coördinaten. Er zal een vervolg-tutorialserie zijn die deze benaderingen zal bespreken. Ondertussen zou ik aanraden om Amit's ongelooflijk grondige artikel over zeshoekige rasters te lezen.