Sådan koder du din egen proceduremæssige fangehullskortgenerator ved hjælp af Random Walk Algorithm

Efterhånden som teknologien udvikler sig, og spilindholdet bliver mere algoritmisk genereret, er det ikke svært at forestille sig skabelsen af ​​en livlignende simulering med unikke oplevelser for hver spiller.

Teknologiske gennembrud, tålmodighed og raffinerede færdigheder bringer os derhen, men det første skridt er at forstå generering af proceduremæssigt indhold .

Selvom der findes mange out-of-the-box-løsninger til kortgenerering, lærer denne tutorial dig at lave din egen to-dimensionelle fangehullskortgenerator fra bunden ved hjælp af JavaScript.

Der er mange to-dimensionelle korttyper, og alle har følgende egenskaber:

1. Tilgængelige og utilgængelige områder (tunneler og vægge).

2. En tilsluttet rute, som afspilleren kan navigere.

Algoritmen i denne vejledning kommer fra Random Walk Algorithm, en af ​​de enkleste løsninger til kortgenerering.

Efter at have lavet et gitterlignende kort over vægge starter denne algoritme fra et tilfældigt sted på kortet. Det fortsætter med at lave tunneler og tage tilfældige drejninger for at fuldføre det ønskede antal tunneler.

For at se en demo skal du åbne CodePen-projektet nedenfor, klikke på kortet for at oprette et nyt kort og ændre følgende værdier:

  1. Dimensioner: kortets bredde og højde.
  2. MaxTunnels: det største antal drejninger algoritmen kan tage, mens du laver kortet.
  3. MaxLength: den største længde af hver tunnel, som algoritmen vælger, inden der foretages en vandret eller lodret drejning.

Bemærk: jo større maxTurn sammenlignes med dimensionerne, jo tættere er kortet. Jo større maxLength sammenlignes med dimensionerne, jo mere “tunnel-y” ser den ud.

Lad os derefter gå gennem kortgenereringsalgoritmen for at se, hvordan det:

  1. Lav et todimensionelt kort over vægge
  2. Vælger et tilfældigt startpunkt på kortet
  3. Mens antallet af tunneler ikke er nul
  4. Vælger en tilfældig længde fra den maksimalt tilladte længde
  5. Vælger en tilfældig retning at dreje til (højre, venstre, op, ned)
  6. Tegner en tunnel i den retning, mens man undgår kanterne på kortet
  7. Reducerer antallet af tunneler og gentager while-sløjfen
  8. Returnerer kortet med ændringerne

Denne sløjfe fortsætter, indtil antallet af tunneler er nul.

Algoritmen i kode

Da kortet består af tunnel- og vægceller, kunne vi beskrive det som nuller og ener i et todimensionelt array som følgende:

map = [[1,1,1,1,0], [1,0,0,0,0], [1,0,1,1,1], [1,0,0,0,1], [1,1,1,0,1]]

Da hver celle er i et todimensionelt array, kan vi få adgang til dens værdi ved at kende dens række og kolonne, såsom kort [række] [kolonne].

Før du skriver algoritmen, har du brug for en hjælperfunktion, der tager et tegn og en dimension som argumenter og returnerer et todimensionelt array.

createArray(num, dimensions) { var array = []; for (var i = 0; i < dimensions; i++) { array.push([]); for (var j = 0; j < dimensions; j++) { array[i].push(num); } } return array; } 

For at implementere Random Walk-algoritmen skal du indstille dimensionerne på kortet (bredde og højde), maxTunnelsvariablen og maxLengthvariablen.

createMap(){ let dimensions = 5, maxTunnels = 3, maxLength = 3; 

Lav derefter et todimensionelt array ved hjælp af den foruddefinerede hjælperfunktion (todimensionalt array af en).

let map = createArray(1, dimensions);

Opret en tilfældig kolonne og tilfældig række for at oprette et tilfældigt startpunkt for den første tunnel.

let currentRow = Math.floor(Math.random() * dimensions), currentColumn = Math.floor(Math.random() * dimensions);

For at undgå kompleksiteten af ​​diagonale sving skal algoritmen angive den vandrette og lodrette retning. Hver celle sidder i et todimensionelt array og kunne identificeres med sin række og kolonne. På grund af dette kunne retningerne defineres som subtraktioner fra og / eller tilføjelser til kolonne- og rækkenumrene.

Hvis du f.eks. Vil gå til en celle omkring cellen [2] [2], kan du udføre følgende handlinger:

  • at gå op , trække 1 fra dens række [1] [2]
  • for at gå ned , tilføj 1 til rækken [3] [2]
  • for at gå til højre , tilføj 1 til kolonnen [2] [3]
  • at gå til venstre , trække 1 fra kolonnen [2] [1]

Følgende kort illustrerer disse operationer:

Indstil nu directionsvariablen til følgende værdier, som algoritmen vælger, før du opretter hver tunnel:

let directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];

Indled til sidst randomDirectionvariabel for at holde en tilfældig værdi fra retningsarrayet, og indstil lastDirectionvariablen til en tom matrix, der holder den ældrerandomDirectionværdi.

Bemærk: Den lastDirectionarray er tom på den første sløjfe, fordi der ikke er noget ældre randomDirectionværdi.

let lastDirection = [], randomDirection;

Sørg derefter for, at maxTunneldet ikke er nul, og at dimensionerne og maxLengthværdierne er modtaget. Fortsæt med at finde tilfældige retninger, indtil du finder en, der ikke er omvendt eller identisk med lastDirection. Dette gør mens sløjfe hjælper med at forhindre overskrivning af den nyligt trukkede tunnel eller trække to tunneller bag-til-ryg.

For eksempel, hvis din lastTurner [0, 1], forhindrer do while-sløjfen funktionen i at bevæge sig fremad, indtil den randomDirectioner indstillet til en værdi, der ikke er [0, 1] eller det modsatte [0, -1].

do { randomDirection = directions[Math.floor(Math.random() * directions.length)]; } while ((randomDirection[0] === -lastDirection[0] && randomDirection[1] === -lastDirection[1]) || (randomDirection[0] === lastDirection[0] && randomDirection[1] === lastDirection[1])); 

I do while-sløjfen er der to hovedbetingelser, der er divideret med en || (ELLER) tegn. Den første del af tilstanden består også af to betingelser. De første én tjekker, om det randomDirection's første punkt er det modsatte af det lastDirection' s første punkt. Den anden kontrollerer, om det randomDirectionandet element er det modsatte af det lastTurnandet element.

For at illustrere, hvis den lastDirectioner [0,1] og randomDirectioner [0, -1], kontrollerer den første del af betingelsen, om randomDirection[0] === - lastDirection[0]), hvilket svarer til 0 === - 0, og er sandt.

Then, it checks if (randomDirection[1] === — lastDirection[1]) which equates to (-1 === -1) and is also true. Since both conditions are true, the algorithm goes back to find another randomDirection.

The second part of the condition checks if the first and second values of both arrays are the same.

After choosing a randomDirection that satisfies the conditions, set a variable to randomly choose a length from maxLength. Set tunnelLength variable to zero to server as an iterator.

let randomLength = Math.ceil(Math.random() * maxLength), tunnelLength = 0;

Make a tunnel by turning the value of cells from one to zero while the tunnelLength is smaller than randomLength. If within the loop the tunnel hits the edges of the map, the loop should break.

while (tunnelLength < randomLength) { if(((currentRow === 0) && (randomDirection[0] === -1))|| ((currentColumn === 0) && (randomDirection[1] === -1))|| ((currentRow === dimensions — 1) && (randomDirection[0] ===1))|| ((currentColumn === dimensions — 1) && (randomDirection[1] === 1))) { break; }

Else set the current cell of the map to zero using currentRow and currentColumn. Add the values in the randomDirection array by setting currentRow and currentColumn where they need to be in the upcoming iteration of the loop. Now, increment the tunnelLength iterator.

else{ map[currentRow][currentColumn] = 0; currentRow += randomDirection[0]; currentColumn += randomDirection[1]; tunnelLength++; } } 

After the loop makes a tunnel or breaks by hitting an edge of the map, check if the tunnel is at least one block long. If so, set the lastDirection to the randomDirection and decrement maxTunnels and go back to make another tunnel with another randomDirection.

if (tunnelLength) { lastDirection = randomDirection; maxTunnels--; } 

This IF statement prevents the for loop that hit the edge of the map and did not make a tunnel of at least one cell to decrement the maxTunnel and change the lastDirection. When that happens, the algorithm goes to find another randomDirection to continue.

Når det er færdigt med at tegne tunneler og maxTunnelser nul, skal du returnere det resulterende kort med alle dets sving og tunneler.

} return map; };

Du kan se den komplette algoritme i følgende uddrag:

Tillykke med at have læst igennem denne vejledning. Du er nu veludstyret til at lave din egen kortgenerator eller forbedre denne version. Tjek projektet på CodePen og på GitHub som en reageringsapplikation.

Tak for læsningen! Hvis du kunne lide denne historie, så glem ikke at dele den på sociale medier.

Særlig tak til Tom for at være med til at skrive denne artikel.