[TUT] Game development 06 - Primaire Level en Tile objecten

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Dit is een vervolg op: [TUT] Game development 05 - Keyboard events

    Hallo ICTScripters,

    In dit blogbericht de 6e tutorial van de serie game development. In deze serie gaan we de primaire Level en Tile klasse maken. Later in de serie gaan we deze klasse gebruiken om Levels te genereren. Ik waarschuw daarom alvast dat er in de komende tutorials weinig tot niets wijzigt binnen de game, maar op de achtergrond natuurlijk wel. Maargoed nu verder met de tutorial...

    Omdat we nu gaan beginnen met het maken van onderdelen die daadwerkelijk gebruikt gaan worden in de client is het eerst belangrijk om GOED te bedenken welke onderdelen we nodig hebben. Natuurlijk ga ik jullie dit denkwerk besparen en maak ik hier alvast een lijstje met onderdelen die sowieso in een level voor gaan komen:
    • De grootte van de map (breedte en hoogte).
    • Losse tiles.
    • Een update methoden om de logica van het level te calculeren (denk aan bewegende NPC's).
    • Een methoden om een tijd cyclus te maken (denk aan Dag/Licht en Nacht/Donker).
    • Een methoden om de losse tiles te renderen.
    Voor we beginnen alleen nog een korte uitleg over Tiles. Wat zijn Tiles nou precies?
    De naam zegt al genoeg, een tile is een "vloertegel" wat erg logisch klinkt bij het maken van een 2D game want je hebt namelijk geen diepte. In tegenstelling tot onze huidige (voorbeeld) code hebben tiles een vaste waarde en worden dus ook buiten de camera/viewport gerenderd. Verder heb je 3 soorten tiles waarmee we een "soort" diepte gaan creëren:
    • Een normale tile, dit is een tile waar je doorheen kan lopen en waar je karakter bovenop wordt gerenderd (denk aan een vloer)
    • Een solide tile, dit is een tile waar je met je karakter niet doorheen kan lopen. (denk aan een muur of het einde van het level)
    • Een masked tile, dit is een tile waar je doorheen kan lopen echter wordt je karakter onder deze tile gerenderd (denk aan het lopen achter een huis)
    Dan gaan we nu beginnen met bouwen van de klasse, omdat het hier voornamelijk om de theorie gaat zal er weinig spannends gebeuren. Maak een Level klasse aan en stop deze in de package level, hiervoor is een apparte package omdat hier straks alle levels in komen te staan.

    Source Code

    1. package com.michaelbeers.elysium.level;
    2. import com.michaelbeers.elysium.gfx.Screen;
    3. public class Level {
    4. protected int width, height;
    5. protected int[] tiles;
    6. //
    7. // Constructor voor een random level.
    8. //
    9. public Level(int width, int height) {
    10. this.width = width;
    11. this.height = height;
    12. this.tiles = new int[width * height];
    13. generateLevel();
    14. }
    15. protected void generateLevel() {
    16. }
    17. //
    18. // Constructor voor een level dat geladen wordt d.m.v. een file.
    19. //
    20. public Level(String path) {
    21. loadLevel(path);
    22. }
    23. protected void loadLevel(String path) {
    24. }
    25. //
    26. // Updaten van de logica
    27. //
    28. public void update() {
    29. }
    30. //
    31. // Updaten van de time cycle.
    32. //
    33. private void time() {
    34. }
    35. //
    36. // Renderen van het scherm
    37. //
    38. public void render(int xScroll, int yScroll, Screen screen) {
    39. }
    40. }
    Display All


    Waarom heeft de Level klasse 2 constructors?
    Omdat we 2 manieren van genereren gaan maken en deze beide andere parameters nodig hebben.

    Waarom is deze klasse niet Abstract?
    De snelle programmeurs zullen zicht dit inderdaad afvragen, heel simpel omdat we nog niet weten of we daadwerkelijk opties gaan toevoegen of aanpassen. Wanneer we klaar zijn met de Client kunnen we dit wellicht overwegen.

    Verder met de tutorial, de Tile klasse...
    Een level bestaat uit Tiles, hiervoor gaan we nu een appart object maken wij willen namelijk niet dat het level de Tiles gaat renderen maar slechts de positie (x,y). Verder krijgen Tiles de nodige opties mee om te kijken of dit een normale, solide of masked tile is. Net als in de Level klasse zal er met deze code weinig spannends voorbij komen, omdat deze tutorial grotendeels uit theorie bestaat :sleeping: saai ik weet het. Maak daarom een nieuwe klasse aan met de naam Tile en plaats deze in de package level.tile dit is dus een sub-package tile in de level package!

    Source Code

    1. package com.michaelbeers.elysium.level.tile;
    2. import com.michaelbeers.elysium.gfx.Screen;
    3. import com.michaelbeers.elysium.gfx.Sprite;
    4. public class Tile {
    5. public int x, y;
    6. public Sprite sprite;
    7. public Tile(Sprite sprite) {
    8. this.sprite = sprite;
    9. }
    10. public void render(int x, int y, Screen screen) {
    11. }
    12. public boolean solid() {
    13. return false;
    14. }
    15. public boolean mask() {
    16. return false;
    17. }
    18. }
    Display All


    Net als in de Level klasse gaan we deze NOG niet abstract maken omdat we hiervoor te weinig data hebben. Om de error's eruit te halen is het zaak om niet te vergeten de imports te fixen CTRL+SHIFT+O :thumbsup:.

    Tot slot gaan we de Screen klasse nog even een schoonmaak beurt geven en wat extra dingen toevoegen namelijk een offset. Ik denk dat hiervoor weinig uitleg nodig is deze updates bestaan uit een setter en een aantal variabelen. Verder zullen er ook een aantal dingen verwijderd zijn die we niet meer nodig hebben vanuit de voorbeeld code. Vergeet hier nogmaals niet om de imports te fixen met CTRL+SHIFT+O:

    Source Code

    1. public class Screen {
    2. public int width, height;
    3. public int xOffset, yOffset;
    4. public int[] pixels;
    5. public Screen(int width, int height) {
    6. this.width = width;
    7. this.height = height;
    8. pixels = new int[width * height];
    9. }
    10. public void clear() {
    11. for (int i = 0; i < pixels.length; i++) {
    12. pixels[i] = 0;
    13. }
    14. }
    15. public void render(int xOffset, int yOffset) {
    16. for (int y = 0; y < height; y++) {
    17. int ypixels = y + yOffset;
    18. if (ypixels < 0 || ypixels >= height) {
    19. continue;
    20. }
    21. for (int x = 0; x < width; x++) {
    22. int xpixels = x + xOffset;
    23. if (xpixels < 0 || xpixels >= width) {
    24. continue;
    25. }
    26. pixels[xpixels + ypixels * width] = Sprite.grass.pixels[(x & 15) + (y & 15) * Sprite.grass.SIZE];
    27. }
    28. }
    29. }
    30. public void setOffset(int xOffset, int yOffset) {
    31. this.xOffset = xOffset;
    32. this.yOffset = yOffset;
    33. }
    34. }
    Display All


    Wanneer je deze stappen hebt gedaan ben je op het einde gekomen van deze tutorial, zoals gewaarschuwd zullen we de komende tijd veel op de achtergrond wijzigen en weinig veranderingen zien in de client. Wanneer we daadwerkelijk alles van een level hebben gebouwd, zal ik pas de Github repository updaten, omdat het tot nu toe weinig nut heeft.

    Tot de volgende tutorial!

    Met vriendelijke groet,

    Michael Beers
    Dit was mijn spreekbeurt, zijn er nog vragen?

    1,221 times read

Comments 3

  • R.Haentjens -

    Dat waarschuwing was had ik ook al gezegd :p maar oke, bedankt

  • M.Beers -

    Hallo R.Haentjes,

    Dit is geen error maar een warning... dit zou je momenteel bij elke methoden moeten krijgen omdat deze methoden nog nergens worden gebruikt. Gewoon negeren dus!

    Mvg,

    Michael Beers

  • R.Haentjens -

    Hallo,

    Ik heb bij mij nog niet echt een error maar een waarschuwing, namelijk bij level, op regel 42:
    private void time() {

    bij die time geeft hij dit aan "the method time() from the type Level is never used locally"