Pe măsură ce tehnologia evoluează și conținutul jocului devine mai algoritmic, nu este dificil să ne imaginăm crearea unei simulări de viață cu experiențe unice pentru fiecare jucător.

Descoperirile tehnologice, răbdarea și abilitățile rafinate ne vor duce acolo, dar primul pas este să înțelegem generarea de conținut procedural.

Deși există multe soluții pentru generarea hărților, acest tutorial vă va învăța să vă creați propriul generator de hărți bidimensionale de la zero, folosind JavaScript.

Există multe tipuri de hărți bidimensionale și toate au următoarele caracteristici:

1. Zone accesibile și inaccesibile (tuneluri și pereți).

2. O rută conectată pe care jucătorul o poate naviga.

Algoritmul din acest tutorial provine din Algoritmul Random Walk, una dintre cele mai simple soluții pentru generarea hărților.

După realizarea unei hărți a pereților în formă de grilă, acest algoritm începe dintr-un loc aleatoriu pe hartă. Continuă să facă tuneluri și să ia ture aleatorii pentru a-și completa numărul dorit de tuneluri.

Pentru a vedea o demonstrație, deschideți proiectul CodePen de mai jos, faceți clic pe hartă pentru a crea o hartă nouă și modificați următoarele valori:

  1. dimensiuni: lățimea și înălțimea hărții.
  2. MaxTunnels: cel mai mare număr de rotații pe care algoritmul le poate face în timpul realizării hărții.
  3. Lungime maxima: cea mai mare lungime a fiecărui tunel pe care o va alege algoritmul înainte de a face o întoarcere orizontală sau verticală.

Notă: cu cât este mai mare maxTurn este comparat cu dimensiunile, cu atât harta va fi mai densă. Cu cât este mai mare lungime maxima este comparat cu dimensiunile, cu atât va arăta mai „tunel-y”.

În continuare, să parcurgem algoritmul de generare a hărții pentru a vedea cum:

  1. Realizează o hartă bidimensională a pereților
  2. Alege un punct de plecare aleatoriu pe hartă
  3. În timp ce numărul tunelurilor nu este zero
  4. Alege o lungime aleatorie din lungimea maximă permisă
  5. Alege o direcție aleatorie la care să se întoarcă (dreapta, stânga, sus, jos)
  6. Desenează un tunel în acea direcție evitând în același timp marginile hărții
  7. Scade numărul de tuneluri și repetă în timp ce bucla
  8. Returnează harta cu modificările

Această buclă continuă până când numărul tunelurilor este zero.

Algoritmul în cod

Deoarece harta este formată din celule de tunel și de perete, am putea să o descriem ca zerouri și unele într-o matrice bidimensională, după cum urmează:

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]]

Deoarece fiecare celulă se află într-o matrice bidimensională, îi putem accesa valoarea știind rândul și coloana, cum ar fi harta [row][column].

Înainte de a scrie algoritmul, aveți nevoie de o funcție de ajutor care să ia un caracter și o dimensiune ca argumente și să returneze o matrice bidimensională.

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;  
}

Pentru a implementa algoritmul Random Walk, setați dimensiunile hărții (lățime și înălțime),maxTunnels variabilă șimaxLength variabil.

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

Apoi, faceți o matrice bidimensională utilizând funcția de ajutor predefinită (matrice bidimensională a acestora).

let map = createArray(1, dimensions);

Configurați o coloană aleatorie și un rând aleatoriu pentru a crea un punct de plecare aleatoriu pentru primul tunel.

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

Pentru a evita complexitatea virajelor diagonale, algoritmul trebuie să specifice direcțiile orizontale și verticale. Fiecare celulă se află într-un tablou bidimensional și ar putea fi identificată cu rândul și coloana sa. Din această cauză, direcțiile ar putea fi definite ca scăderi și / sau adaosuri la numerele de coloane și rânduri.

De exemplu, pentru a merge la o celulă din jurul celulei [2][2], puteți efectua următoarele operații:

  • a merge sus, scade 1 din rândul său [1][2]
  • a merge jos, adăugați 1 la rândul său [3][2]
  • a merge dreapta, adăugați 1 la coloana sa [2][3]
  • a merge stânga, scade 1 din coloana sa [2][1]

Următoarea hartă ilustrează aceste operații:

Cum sa va codificati propriul generator de harti de temnita
Grila de opțiuni operaționale

Acum, setați directions variabilă la următoarele valori pe care algoritmul le va alege înainte de a crea fiecare tunel:

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

În cele din urmă, inițiază randomDirection variabilă pentru a deține o valoare aleatorie din matricea de direcții și pentru a seta lastDirection variabilă la o matrice goală care va conține cea mai veche randomDirection valoare.

Notă: lastDirection matricea este goală în prima buclă, deoarece nu există mai multe randomDirection valoare.

let lastDirection = [], 
    randomDirection;

Apoi, asigurați-vă maxTunnel nu este zero și dimensiunile și maxLengths-au primit valori. Continuați să găsiți direcții aleatorii până când găsiți una care nu este inversă sau identică cu lastDirection. Acest face while loop ajută la prevenirea suprascrierii tunelului trasat recent sau a desenării a două tuneluri spate-în-spate.

De exemplu, dacă lastTurn este [0, 1], bucla do while împiedică funcția să avanseze până la randomDirection este setat la o valoare care nu este [0, 1] sau opusul [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]));

În bucla do while, există două condiții principale care sunt împărțite la un || (SAU) semn. Prima parte a afecțiunii constă, de asemenea, din două afecțiuni. Primul verifică dacă randomDirectionPrimul element este inversul lastDirection‘s primul articol. Al doilea verifică dacă randomDirectionAl doilea element este inversul lastTurnal doilea element.

Pentru a ilustra, dacă lastDirection este [0,1] și randomDirection este [0,-1], prima parte a condiției verifică dacă randomDirection[0] === – lastDirection[0]), care echivalează cu 0 === – 0 și este adevărat.

Apoi, verifică dacă (randomDirection[1] === – lastDirection[1]) care echivalează cu (-1 === -1) și este, de asemenea, adevărat. Deoarece ambele condiții sunt adevărate, algoritmul revine pentru a găsi alta randomDirection.

A doua parte a condiției verifică dacă prima și a doua valoare a ambelor matrice sunt aceleași.

După alegerea unui randomDirection care îndeplinește condițiile, setați o variabilă pentru a alege aleatoriu o lungime din maxLength. A stabilit tunnelLength variabilă la zero la server ca iterator.

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

Faceți un tunel rotind valoarea celulelor de la unu la zero în timp ce tunnelLength este mai mic decât randomLength. Dacă în buclă tunelul lovește marginile hărții, bucla ar trebui să se rupă.

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; }

Altfel, setați celula curentă a hărții la zero folosind currentRow și currentColumn. Adăugați valorile în randomDirection matrice prin setare currentRow și currentColumn unde trebuie să se afle în următoarea iterație a buclei. Acum, creșteți tunnelLength iterator.

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

După ce bucla face un tunel sau se rupe lovind o margine a hărții, verificați dacă tunelul are cel puțin un bloc. Dacă da, setați lastDirection la randomDirection și decrement maxTunnels și întoarce-te să faci alt tunel cu altul randomDirection.

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

Această instrucțiune IF previne bucla for care a lovit marginea hărții și nu a făcut un tunel de cel puțin o celulă pentru a decrementa maxTunnel și schimbați lastDirection. Când se întâmplă acest lucru, algoritmul merge pentru a găsi altul randomDirection a continua.

Când termină de desenat tuneluri și maxTunnels este zero, returnează harta rezultată cu toate virajele și tunelurile sale.

}
 return map;
};

Puteți vedea algoritmul complet în următorul fragment:

Felicitări pentru citirea acestui tutorial. Acum sunteți bine echipat pentru a vă crea propriul generator de hărți sau pentru a îmbunătăți această versiune. Verificați proiectul pe CodePen și pe GitHub ca aplicație de reacție.

Mulțumesc pentru lectură! Dacă ți-a plăcut această poveste, nu uita să o împărtășești pe social media.

Mulțumiri speciale lui Tom pentru co-scrierea acestui articol.