Eindelijk weer een update

Ik heb een lange tijd geen update meer geplaatst op mijn blog. Maar nu heb ik weer tijd gevonden en genomen om even een stukje te schrijven. Ik ben de laatste tijd druk bezig geweest met het programmeren van mijn rts-game Polemos.
Voor het spel heb ik niet verder gewerkt aan de minimap, omdat het nog niet bekend was hoe de tiles precies gingen werken. Nu ben ik een stukje verder met het spel zelf. Ik heb de gehele code twee keer gerefactord om een betere structuur te krijgen en verschillende stukken code te laten werken op een simpele leesbare manier.

Op het moment kunnen nu de tiles, resourcefields en de units weergegeven worden op het scherm.

Wat er nog moet gebeuren, veel oa:

  • Betere pathfinding
  • Beacons
  • Selectie en besturing van units
  • GUI
  • Menu’s

De grotere minimap

Na mijn laatste post over het maken van een grotere minimap, ben ik meteen begonnen met een klasse te schrijven die dit zou moeten kunnen weergeven. Maar, dit werkte uiteraard niet, aangezien je dan natuurlijk al eerder een post van mij gehad zou hebben. Na een flinke tijd getest te hebben ben ik erachter dat ik geen graphics context hiervoor kan gebruiken zoals ik dat heb gedaan bij de kleinere minimap. Met de graphics context krijg je de mogelijkheid om op een image te tekenen. Dit is een soort van static klasse.

Voor dit probleem zal vast een oplossing zijn met het gebruik van een graphics context. Maar wat ik nu ga gebruiken is de klasse ImageBuffer die slick mij geeft. Hierbij kan ik elke pixel in een image veranderen. Elke vakje bij de minimap is 4×4 pixels dus dit zou geen probleem moeten zijn. Het maakt sommige dingen zelfs gemakkelijker, aangezien de objecten bij de rand niet dubbel getekend hoeven worden. Ik kan gewoon de index van een image laten berekenen met de hoeveelheid pixels.

Als ik dit gemaakt heb, ga ik wat simpele tiles maken waarmee ik een grote map maak, met bijbehorende minimap. Dan kan er getest worden hoe dit met elkaar moet communiceren. Het zal allemaal zeer simpel eruit komen zien, maar de achterliggende logica is toch vrij lastig (voor een beginner).

En een grotere minimap dan?

Lees eerst mijn vorige post over de minimap, die is hier te vinden.

Om een grotere minimap te kunnen tekenen moet de minimap op verschillende images getekend worden. Hiervoor moet er per tile gekeken worden op welke image die getekend moet worden. Tiles die in het midden zitten die zullen soms ook dubbel getekend moeten worden.

Hiervoor moet er gekeken worden naar de locatie van de tile op het geheel. Elke tile is in de minimap 4 px breed en hoog. Maar bij de hoogte overlappen ze elkaar een beetje. Hierdoor is de hoogte die uiteindelijk gebruikt wordt 3px. Zo kan je zeggen dat de 128ste (128 * 4 = 512) tile getekend moet worden op de image ernaast. Maar een image die een offset heeft wordt 2 px opgeschoven naar rechts. Zo krijg je bij de 127ste (127*4 +2 = 510) tile met een offset dat de helft van de tile op de image staat. Dan moet op de image ernaast de tile opnieuw getekend worden, omdat je anders maar een halve tile ziet.

Om dit te kunnen doen moet een simpele manier bedenken om precies uit te rekenen op welke image een tile getekend moet worden. Dit ga ik vanmiddag even proberen, en ik zal daar zo snel mogelijk weer een post over maken.

Minimap

Voor een rts is een minimap een belangrijk gedeelte, welke ook het grootste gedeelte van de gameplay bepaald. Veel informatie wordt hier namelijk uitgehaald. Ik ben bezig met proberen en testen, om uiteindelijk een simpele rts te maken. Hier moet natuurlijk ook een minimap bij. Dit moet uiteraard weer met zeshoekjes. De manier dat alle vakjes ingedeeld zijn staat hier.

Een minimap is een verkleinde versie van een kaart. (goh het heet minimap). Het probleem is dat als er een kaart is van 50.000 vakjes je alle vakjes door moet lopen en allemaal moet tekenen. Dit zorgt natuurlijk voor een flinke performance drop. Om dit op te lossen moet de minimap niet constant opnieuw getekend worden. Maar moet hij opgeslagen worden. Zo kan het als één geheel weergeven worden. Als er een verandering komt in de kaart hoeft alleen dat stukje veranderd te worden. En hoeft er niet constant de kaart opnieuw getekend te worden.

Wat mij het handigst lijkt voor het weergeven van veranderingen in de kaart, is om alle objecten die op de kaart weergeven moeten worden een notificatie geven als hun positie is veranderd, of hun status is veranderd. Ik heb er nog niet heel goed over nagedacht, maar dit is in ieder geval een systeem dat aardig moet werken.

Ik heb het opslaan van de gehele minimap al een beetje zitten proberen, en het werkt aardig. Slick heeft nog wel enkele rare dingetjes, zoals het omkeren van de graphics. En dat het bij plaatjes kleiner dan 256 px hoogte linksonder als oorsprong te pakken, en bij plaatjes groter dan dat, de gebruikelijke linksboven als oorsprong (in ieder geval voor opengl).

Hier is omheen te werken om gewoon het plaatje van de minimap 512*512 px te laten zijn. Om een zeshoek redelijk te benaderen in simpele graphics kan een simpel plusje gebruikt worden die 4 px breed en hoog is. Deze lijken in elkaar zeer op zeshoeken. Dit geeft wel als probleem dat een minimap al snel groter wordt dan 512*512 px. Een kaart met 63 ringen is 63*2 +1 = 127 vakjes breed. En omdat elk vakje 4px is, kom je dan al op 508 px uit. Een kaart met 63 ringen heeft 12097 vakjes. Dit is natuurlijk een vrij kleine kaart. Een vierkante kaart zou dan ongeveer 100 * 120 vakjes zijn. Als je een rts wilt die je kan spelen met meer mensen dan 2, dan heb je al snel een veel grotere kaart nodig. Dan is een kaart met 127 ringen een optimale grootte, als ik de groottes pak van Age Of Empires, waarbij de grootste kaart 240*240 is. Bij 127 ringen zijn er namelijk 48769 vakjes. wat iets kleiner is dan 240*240 = 57600 vakjes.

Een minimap van een kaart met 127 ringen komt buiten de 512 px, namelijk (127*2 +1) *4 = 1020 px. Hiervoor moet daarom een oplossing bedacht worden. De gehele minimap kan opgesplitst worden in verschillende images. Hierdoor is er wel wat overlap, maar daar is niet veel aan te doen, omdat er verschillende zeshoeken afgesneden worden.

De uiteindelijke minimap die weergeven wordt, moet natuurlijk niet 512*512 px zijn. Dit zou veelste groot zijn. De minimap moet op de een of andere manier kleiner gemaakt worden. Dit kan door de gehele minimap te scalen. Of maar een gedeelte van de minimap te laten zien. Ikzelf wil dat laatste, dit geeft namelijk het gevoel dat de kaart groter is. Ook geeft het wat overzicht weg, waardoor het een mooi gameplay element mee geeft.

Het weergeven van een zeshoekige kaart

Ik heb iets met zeshoeken, wat veel dingen veel moeilijker maakt, dan dat het al is. Ten eerste moet je een soort coordinaten systeem maken om te kunnen berekenen welke zeshoeken aan elkaar liggen. Daarnaast moet je weten waar de zeshoek weergeven moet worden op het scherm.

Een zeshoekige kaart heeft een middelpunt, met een aantal ringen van zeshoeken eromheen. Hiermee is ook de hoeveelheid vakjes uit te rekenen. Bijvoorbeeld een kaart met 10 ringen heeft 331 zeshoekjes.

Vooral dat laatste is lastig. Ik heb nu een klein testje gedaan, maar die loopt verre van optimaal. Deze loopt namelijk door een lijst (array) met alle zeshoekjes heen, en rekent dan per zeshoek uit wat zijn locatie moet zijn. Als deze binnen het scherm past tekent het programma de zeshoek. Dit is zeer inefficiënt. Er moet een manier gevonden worden om heel makkelijk en snel de juiste zeshoekjes te tekenen. Misschien een array die wel op de x, en de y gebaseerd is, in plaats van op de eigen coördinatensysteem. Zo kan je heel makkelijk met de locatie van het beeld de juiste zeshoekjes tekenen.

Een kaart van zeshoekjes met 5 ringen heeft de onderstaande vorm:

hexmap

Hierbij is het dus lastig om een eenduidig coördinatensysteem te maken die op de x en y waarden van het scherm is gebaseerd. Hiervoor moeten bepaalde regels vastgesteld worden. Het probleem is namelijk dat de de helft van de rijen vergeleken met de middelste (en langste) rij een kleine verschuiving heeft. Hier moet dus rekening mee gehouden worden. De middelste rij is ook meteen de langste rij, dus deze kan gebruikt worden voor de bepaling van de x-waardes. De meest linkse heeft x-waarde 0, die daarnaast x-waarde 1, en zo gaat het door tot 10 (het zijn 11 vakjes in totaal). voor de rij erboven en eronder stellen we een regel op.

Elk vakje schuiven we een half vakje naar links. Hierdoor krijg je dus dat de vakjes van 0 tot en met 9 genummerd worden. De rij erboven en onder worden niet verschoven, en dan weer wel. Deze verschuiving is afhankelijk van de y-waarde vergeleken met de y-waarde van de middelste rij.

Een kaart met 3 ringen krijgt dan de onderstaande coördinaten:

_,_ | 1,0 | 2,0 | 3,0 | 4,0 | _,_ | _,_

_,_ | 1,1 | 2,1 | 3,1 | 4,1 | 5,1 | _,_

0,2 | 1,2 | 2,2 | 3,2 | 4,2 | 5,2 | _,_

0,3 | 1,3 | 2,3 | 3,3 | 4,3 | 5,3 | 6,3

0,4 | 1,4 | 2,4 | 3,4 | 4,4 | 5,4 | _,_

_,_ | 1,5 | 2,5 | 3,5 | 4,5 | 5,5 | _,_

_,_ | 1,6 | 2,6 | 3,6 | 4,6 | _,_ | _,_

 

Dit kan gemakkelijk gebruikt worden in een array. Hierdoor is het mogelijk om de juiste zeshoeken te verkrijgen, zonder een hele collectie te doorlopen. Een andere mogelijkheid is om hiervoor een speciale klasse te maken, die het opzoeken heel gemakkelijk doet. Hier heb ik nog niet echt naar gekeken. Maar dit kan gezien worden dat er een klasse is met een collectie rijen. Als je dan een rij nodig hebt, hoeft hij alleen door die lijt heen te gaan, en daarna uit die rij, door een lijst met zeshoeken te gaan. Deze manier maakt het wel beter qua geheugen. Maar de methode die ik hierboven heb bedacht is vele male makkelijker te maken, vooral omdat er geen grote vertaalomslag nodig is van de x en y waardes.

Dit coordinatensysteem is gemakkelijk te gebruiken met een vaste array. Een nadeel hiervan is dat er een deel van de array niet gebruikt wordt. (ongeveer 25%). Dit is niet zo’n heel groot probleem, omdat er maar een zo’n array is. En deze array bestaat helemaal uit pointers naar objecten (de zeshoeken). Pointers nemen niet zoveel ruimte in als een geheel object. Maar ik ben natuurlijk nog een beginner, dus ik ken alle pro’s en con’s van de verschillende systemen nog niet precies. Maar dit moet een goed begin geven om een grote kaart gemakkelijk en redelijk efficiënt weer te geven.