Puzzle-urile sunt parte integrantă a gameplay-ului pentru multe genuri. Fie că este simplu sau complex, puzzle-urile în curs de dezvoltare manual pot deveni rapid greoaie. Acest tutorial își propune să ușureze această povară și să deschidă calea pentru alte aspecte mai amuzante ale designului.
Împreună vom crea un generator pentru a compune puzzle-uri simple "procedate" imbricate. Tipul de puzzle pe care ne vom concentra pe tradiționalul "blocare și cheie" cel mai adesea este repetat ca: obține elementul x pentru a debloca zona y. Aceste tipuri de puzzle-uri pot deveni plictisitoare pentru echipele care lucrează la anumite tipuri de jocuri, în special crawlerele pentru dungeon, nisipurile și jocurile de rol unde puzzle-urile se bazează mai des pentru conținut și explorare.
Folosind generarea procedurală, obiectivul nostru este de a crea o funcție care să ia câțiva parametri și să revină un avantaj mai complex pentru jocul nostru. Aplicarea acestei metode va oferi o întoarcere exponențială pentru timpul dezvoltatorului, fără a sacrifica calitatea gameplay-ului. Consternarea dezvoltatorului poate, de asemenea, să scadă ca efect secundar fericit.
Pentru a continua, trebuie să cunoașteți un limbaj de programare la alegere. Deoarece cele mai multe dintre ceea ce discutăm sunt numai date și sunt generalizate în pseudocod, orice limbaj de programare orientat pe obiect va fi suficient.
De fapt, unii editori de drag-and-drop vor funcționa, de asemenea. Dacă doriți să creați o demonstrație care să poată fi redată de generatorul menționat aici, veți avea nevoie și de o familiarizare cu biblioteca preferată a jocurilor.
Să începem cu o privire la un pseudocod. Cele mai elementare elemente de bază ale sistemului nostru vor fi cheile și camerele. În acest sistem, jucătorului i se interzice să pătrundă în ușa camerei dacă nu posedă cheia. Iată ce vor arăta cele două obiecte ca clase:
clasa cheie Var playerHas; Locația Var; Funcția init (setLocation) Location = setLocation; PlayerHas = false; Funcția pickUp () this.playerHas = true; sala de clasă Var este blocată; Var assocKey; Funcția init () isLocked = true; assocKey = cheie nouă (aceasta); Deblocare funcție () this.isLocked = false; Funcția canUnlock Dacă (this.key.PlayerHas) Return true; Altceva Return false;
Clasa noastră cheie deține doar două informații acum: locația cheii și dacă jucătorul are acea cheie în inventarul său. Cele două funcții sunt inițializarea și preluarea. Inițializarea determină elementele de bază ale unei noi chei, în timp ce preluarea este pentru momentul în care un jucător interacționează cu cheia.
La rândul său, clasa camerei noastre conține, de asemenea, două variabile: este încuiat
, care deține starea actuală a încuietorii camerei și assocKey
, care deține obiectul cheie care deblochează această cameră specifică. Acesta conține o funcție pentru inițializare și una pentru a apela deblocarea ușii, iar alta pentru a verifica dacă ușa poate fi deschisă în prezent.
O singură ușă și cheia sunt distractive, dar putem mereu să-l adunăm cu cuiburile. Punerea în aplicare a acestei funcții ne va permite să creăm ușile în ușă, în timp ce servim ca generatoare primară. Pentru a menține cuiburile, va trebui să adăugăm și câteva variabile suplimentare la ușa noastră:
camera de clasă Var isLocked; Var assocKey; Var parentRoom; Var; Funcția init (setParentRoom, setDepth) Dacă (setParentRoom) parentRoom = setParentRoom; Altceva parentRoom = nici unul; Adâncime = setDepth; isLocked = true; assocKey = cheie nouă (aceasta); Deblocare funcție () this.isLocked = false; Funcția canUnlock Dacă (this.key.playerHas) Return true; Altceva Return false; Funcția roomGenerator (depthMax) Array roomsToCheck; Array terminatRoame; Cameră inițialRoom.init (none, 0); roomsToCheck.add (initialRoom); În timp ce (roomsToCheck! = Empty) Dacă (currentRoom.depth == depthMax) finishedRooms.add (currentRoom); roomsToCheck.remove (currentRoom); Altceva Room newRoom.init (currentRoom, currentRoom.depth + 1); roomsToCheck.add (newRoom); finishedRooms.add (currentRoom); roomsToCheck.remove (currentRoom);
Acest cod generator face următoarele:
Luând în parametru pentru puzzle-ul nostru generat (în mod specific câte straturi adânc o cameră imbricate ar trebui să meargă).
Crearea a două matrice: una pentru camere care sunt verificate pentru potențial cuibărit, iar altul pentru înregistrarea camerelor care sunt deja imbricate.
Crearea unei camere inițiale care să conțină întreaga scenă și apoi adăugați-o la matrice pentru ca noi să verificăm mai târziu.
Luând camera din fața mesei pentru a trece prin bucla.
Verificarea adâncimii camerei curente în funcție de adâncimea maximă furnizată (aceasta decide dacă vom crea o încăpere suplimentară sau dacă vom finaliza procesul).
Stabilirea unei încăperi noi și amplasarea acesteia cu informațiile necesare din camera părintelui.
Adăugarea unei noi camere la roomsToCheck
array și mutarea camerei anterioare la matricea terminată.
Repetați acest proces până când fiecare cameră din matrice este completă.
Acum putem avea cât mai multe camere pe care mașina noastră le poate gestiona, dar avem încă chei. Plasarea cheie are o provocare majoră: solvabilitatea. Oriunde punem cheia, trebuie să ne asigurăm că un jucător poate avea acces la el! Indiferent de cât de bine se pare cache-ul cheie ascuns, în cazul în care jucătorul nu poate ajunge la el, el sau ea este efectiv prins. Pentru ca jucătorul să continue prin puzzle, cheile trebuie să poată fi obținute.
Cea mai simplă metodă de a asigura solvabilitatea în puzzle-ul nostru este de a folosi sistemul ierarhic al relațiilor obiect părinte-copil. Deoarece fiecare cameră se află într-un altul, ne așteptăm ca un jucător să aibă acces la părintele fiecărei camere pentru a ajunge la el. Deci, atâta timp cât cheia este deasupra camerei din lanțul ierarhic, garantăm că jucătorul este capabil să obțină acces.
Pentru a adăuga generarea de chei la generația noastră procedurală, vom pune în funcția noastră principalele funcții:
Camera de funcționareGenerator (depthMax) Rooms ArrayToCheck; Array terminatRoame; Cameră inițialRoom.init (none, 0); roomsToCheck.add (initialRoom); În timp ce (roomsToCheck! = Empty) Dacă (currentRoom.depth == depthMax) finishedRooms.add (currentRoom); roomsToCheck.remove (currentRoom); Altceva Room newRoom.init (currentRoom, currentRoom.depth + 1); roomsToCheck.add (newRoom); finishedRooms.add (currentRoom); roomsToCheck.remove (currentRoom); Array allParentRooms; roomCheck = newRoom; În timp ce (roomCheck.parent) allParentRooms.add (roomCheck.parent); roomCheck = roomCheck.parent; Cheie newKey.init (Random (allParentRooms)); newRoom.Key = newKey; Întoarceți terminatCamerele; Altfel finishedRooms.add (currentRoom); roomsToCheck.remove (currentRoom);
Acest cod suplimentar va produce acum o listă a tuturor camerelor situate deasupra camerei curente din ierarhia hărților. Apoi alegem una dintre cele aleatoriu și setăm locația cheii în acea cameră. După aceea, atribuim cheia camerei pe care o deblochează.
Atunci când suntem chemați, funcția generatorului va crea acum și va returna un număr dat de camere cu chei, salvând eventual ore de timp de dezvoltare!
Asta face parte din pseudocodul generatorului nostru de puzzle simplu, deci acum să o punem în acțiune.
Am construit demo-ul nostru folosind JavaScript și biblioteca Crafty.js pentru a păstra cât mai ușor posibil, permițându-ne să ne păstrăm programul sub 150 de linii de cod. Există trei componente principale ale demo-ului nostru, după cum urmează:
Jucătorul se poate deplasa de-a lungul fiecărui nivel, taste de preluare și deblocarea ușilor.
Generatorul pe care îl vom folosi pentru a crea o nouă hartă automat de fiecare dată când se rulează demonstrația.
O extensie pentru generatorul nostru de a se integra cu Crafty.js, care ne permite să stocăm informații despre obiect, coliziune și entitate.
Pseudocodul de mai sus acționează ca un instrument de explicație, astfel încât implementarea sistemului în limba proprie va necesita unele modificări.
Pentru demonstrația noastră, o parte din clase sunt simplificate pentru o utilizare mai eficientă în JavaScript. Aceasta include scăderea anumitor funcții legate de clase, deoarece JavaScript permite accesul facil la variabilele din cadrul clasei.
Pentru a crea partea de joc a demo-ului nostru, inițializăm Crafty.js și apoi o entitate de jucător. În continuare, oferim entității jucătorului cele patru comenzi de direcție de bază și o detectare minoră de coliziune pentru a preveni intrarea în camere blocate.
Camerele au acum o entitate Crafty, stocând informații despre dimensiunea, locația și culoarea lor pentru reprezentarea vizuală. Vom adăuga, de asemenea, o funcție de tragere pentru a ne permite să creăm o cameră și să o atragem pe ecran.
Vom furniza chei cu adăugări similare, inclusiv stocarea entității Crafty, mărime, locație și culoare. Cheile vor fi de asemenea colorate pentru a se potrivi cu camerele pe care le deblochează. În sfârșit, putem plasa cheile și le putem crea entitățile folosind o nouă funcție de tragere.
Nu în ultimul rând, vom dezvolta o funcție mică de ajutor care creează și returnează o valoare de culoare hexazecimală aleatorie pentru a elimina sarcina de a alege culorile. Dacă nu vă plac culorile, desigur.
Acum, dacă aveți propriul generator simplu, iată câteva idei pentru a ne extinde exemplele:
Porționați generatorul pentru a permite utilizarea în limba dvs. de programare la alegere.
Extindeți generatorul pentru a include crearea de camere de ramificație pentru o personalizare ulterioară.
Adăugați capacitatea de a gestiona mai multe intrări în camera generatorului, pentru a permite puzzle-uri mai complexe.
Extindeți generatorul pentru a permite amplasarea cheie în locații mai complicate pentru a îmbunătăți rezolvarea problemelor jucătorului. Acest lucru este interesant în special atunci când este asociat cu mai multe căi pentru jucători.
Acum, că am creat împreună acest generator de puzzle, folosiți conceptele prezentate pentru a vă simplifica propriul ciclu de dezvoltare. Ce sarcini repetitive găsiți voi înșivă? Ce vă deranjează cel mai mult să vă creați jocul?
Sunt șanse, cu o mică generație de planificare și procedură, să puteți face procesul mult mai simplu. Sperăm că generatorul nostru vă va permite să vă concentrați asupra părților mai atrăgătoare ale jocului în timpul tăierii lumii.
Mult noroc, și te voi vedea în comentarii!