Întreaga idee a spaţiu este de a deține obiecte de joc. Obiectele de joc dintr-un spațiu nu ar trebui să aibă orice modalitatea de a comunica cu obiectele jocului într-un alt spațiu, astfel încât spațiile oferă un mijloc simplu de a separa diferite grupuri de obiecte de joc. În acest articol, veți învăța beneficiile unei astfel de arhitecturi.
Dacă doriți să vedeți un exemplu de implementare a spațiilor, vă rugăm să consultați motorul de joc open source SEL. Eu însumi autor sun activ și sunt mândru să o prezint ca o resursă pe deplin funcțională pentru cititorii acestui articol.
Aș dori să-i mulțumesc lui Sean Middleditch pentru că mi-a învățat despre beneficiile spațiilor.
Bacsis: Termenul spaţiu în contextul acestui articol se referă la un container special de obiecte de joc. Nu știu de fapt un termen oficial clar definit. Dacă știți de unul, vă rugăm să faceți un comentariu!
Într-un motor de joc convențional, obiectele de joc sunt de obicei stocate într-un singur container. Un astfel de container poate fi un alocator împreună cu un manager de mâner. Uneori containerul este doar o listă legată. Indiferent de implementarea reală, este posibil să existe doar un singur container care să dețină toate obiectele de joc și fiecare obiect de joc făcut este mereu în acest container.
Acest lucru este bine și funcționează total, dar are unele probleme organizatorice. De exemplu, imaginați-vă un manager tradițional de stat de joc. Adesea, între tranziția de la o stare la alta, toate obiectele de joc încărcate în prezent sunt eliberate, iar altele noi sunt citite de pe disc. Ca o optimizare, obiectele de joc pentru următoarea stare (sau nivel) pot fi încărcate pe un fir separat înainte de timp, astfel încât tranzițiile de stare să fie instantanee.
Cu toate acestea, există o problemă enervantă care apare în mod obișnuit: cum reprezentăm elementele GUI pentru meniuri? Poate că playerul HUD este codificat folosind obiecte de joc și scripturi atașate la aceste obiecte de joc. O implementare naivă a managementului de stat ar necesita distrugerea și recrearea tuturor elementelor HUD la tranziția de stat. Aceasta înseamnă că codul personalizat va fi necesar pentru a face față tranziției anumitor obiecte de la o stare la alta.
Sau poate un design de joc necesită un peisaj nebun de fundal în care se desfășoară o bătălie imensă - dar această bătălie nu trebuie să interfereze în nici un fel cu prim-planul (sau cu jucătorul).
Adesea, apar soluții ciudate pentru aceste lucruri, cum ar fi reprezentarea elementelor HUD ca fiind extrem de departe de restul gameplay-ului din lume. Întreruperea meniurilor și altele asemănătoare sunt doar mutate în vedere atunci când este necesar și se îndepărtează altfel. În general, este necesar un cod personalizat pentru a gestiona și a organiza obiecte de joc, deoarece acestea se găsesc pe o singură fabrică sau container.
O soluție acceptabilă a acestei probleme ar fi utilizarea spațiilor (a se vedea descrierea video suplimentară a colegului meu Sean Middleditch). Deoarece toate obiectele de joc din fiecare spațiu au interacțiune zero, spațiile devin un mod natural de abordare a organizării obiectelor de joc. Acest lucru poate minimiza foarte mult nevoia unui cod special de a menține un container logic separat într-un container real (așa cum sa menționat în secțiunea anterioară).
Să aruncăm o privire rapidă asupra unui exemplu de aplicare a spațiului într-o limbă similară C ++:
spațiu de clasă public: GameObject CreateObject (nume șir); const string GetName (void) const; privat: string m_name; // ar putea fi o formă de alocator, poate doar un std :: vector Container m_objects; ;
Adesea este util să puteți căuta un spațiu după nume. Acest lucru este deosebit de bun pentru scripturile în care un script poate folosi codul ca acesta:
// Rulați o logică pentru spațiul local de fundal = GetSpace ("Fundal") tornado = space.CreateObject ("Tornado") // În altă parte putem face ceva complet izolat din fundal, // cum ar fi recuperarea playerului motivul a murit) și a juca o animație // moarte peste partea superioară a spațiului jucătorului local = GetSpace ("CurrentLevel") player = space.GetPlayer () space.CreateObjectAt ("DeathAnimation"Diagrama care arată organizarea simplă a obiectelor de joc în recipiente izolate. Nu toate spațiile au aceeași cantitate de obiecte de joc sau chiar aceleași obiecte de joc.
Răspunsul la această întrebare este: toate acestea! Orice tip de obiect de joc va fi plasat într-un spațiu. Dacă sunteți familiarizați cu agregarea (sau proiectarea bazată pe componente), atunci un obiect de joc și componentele asociate vor locui în același spațiu.
Ideea este de a crea o caracteristică arhitecturală care să permită o modalitate simplă și eficientă de a grupa obiectele de joc împreună și de a le izola de alte grupuri.
Spațiile sunt destul de asemănătoare cu alte concepte care plutesc acum o vreme. Sa spus că spațiile sunt asemănătoare cu lista de afișare din Flash. Dacă sunteți familiarizați cu portalurile pentru a face răsturnarea (deosebit de importantă în jocurile 3D cu multe camere de interior), ideea este destul de asemănătoare și aici.
Cu toate acestea, există o distincție importantă de făcut aici. Spațiile sunt nu o formă de partiționare spațială, așa cum se face cu portaluri sau cu alte partiții spațiale (cum ar fi arborele BSP popular) pentru ocluzia renderei. Spațiile sunt una arhitectural facilitate pentru a permite izolarea obiectelor generale de joc.
Personal, îmi place să mă gândesc la spații precum straturile Photoshop: toate straturile pot fi vizualizate (sau auzite) simultan, dar când pictează pe un strat, niciun alt strat nu este afectat direct; fiecare strat este unic și izolat.
Conceptul de a sistem, pentru scopurile acestui articol, poate fi descrisă ca un set de funcționalități (funcții sau metode) care operează asupra obiectelor de joc ale unui spațiu. În acest fel, un spațiu este înmânat unui sistem de a efectua anumite acțiuni; un sistem special este global pentru întregul joc.
Diagrama exemplificând simplitatea de a permite unui sistem să opereze pe un spațiu ca intrare.Imaginați-vă un sistem grafic care conține a void Graphics :: DrawWorld (Space space)
funcţie. Acest DrawWorld
funcția ar fi buclele asupra obiectelor din spațiul dat care sunt renderabile și le vor atrage pe ecran.
Ideea este să scrieți codul (sistemele) care operează pe o anumită intrare a obiectelor de joc. Nu trebuie să se întâmple nicio urmărire sau gestionare specială a obiectelor de joc în cadrul unor astfel de sisteme. Un sistem nu ar trebui să facă nimic decât să efectueze operații pe obiecte de joc.
Acest stil vă oferă câteva beneficii foarte frumoase, după cum este detaliat în secțiunea următoare.
Cel mai rapid beneficiu al implementării spațiilor într-un motor este că manipularea elementelor sau meniurilor GUI devine trivială. Putem construi un spațiu dedicat unui anumit meniu și, ori de câte ori acest meniu este inactiv, sistemele pur și simplu nu trebuie să funcționeze asupra conținutului spațiului. Când un meniu este inactiv, acesta se află în memorie (obiectele de joc care cuprind memoria se află în spațiul din meniu) și nu face nimic; nici nu este actualizat de un sistem logic, nici nu este redat de un sistem grafic.
Când acest meniu devine din nou activ, acesta poate fi pur și simplu transferat către sistemele corespunzătoare. Meniul poate fi apoi actualizat și redat corespunzător. În același timp, jocul care se întâmplă în spatele meniului poate înceta să fie actualizat în orice mod, deși poate că a trecut încă la sistemul grafic care urmează să fie redat. Acest lucru pune în practică un mod elegant și robust de pauză-și-resume de funcționalitate care vine doar implicit din cauza modului de spații sunt definite.
Adesea, în jocuri în stil RPG, cum ar fi Pokémon, jucătorul va intra și va părăsi casele și colibele. În ceea ce privește gameplay-ul, aceste case diferite sunt de obicei complet izolate; case mici sau peșteri sunt un scenariu ideal pentru a aplica spații. Un spațiu întreg poate fi construit pentru a conține obiectele jocului dintr-o anumită casă, iar acestea pot fi încărcate și inițializate în memorie și pot fi odihnă până când sunt necesare. Tranzițiile instantanee pot fi realizate prin simpla schimbare a spațiului care este transmis diferitelor sisteme de motoare.
O idee interesantă pentru jocurile 2D, cum ar fi platformerii (sau chiar jocurile 3D) ar putea fi simularea nivelurilor reale și a dușmanilor din joc în fundal. Acest lucru ar putea aduce lumea la viață într-un mod care nu necesită de fapt niciun conținut suplimentar și cu greu orice timp de dezvoltare suplimentar. Cel mai bun exemplu pe care l-aș putea găsi despre asta este Rayman Legends:
Actualii dușmani, la fel ca și jucătorul, văd în mod normal, ar putea sărindă sau se târăsc pe pereți în depărtare. Tranzițiile dintre aceste "straturi" diferite ar putea oferi unele posibilități foarte interesante de proiectare.
Aceste posibilități sunt de fapt destul de rare pentru a găsi exemple și ideea de spații nu este cu adevărat îmbrățișată de studiourile sau motoarele AAA moderne. Cu toate acestea, designul este solid și beneficiile sunt reale.
În multe jocuri cu suport pentru multiplayeri în care ambii jucători joacă cu același client de joc, există unele limitări. Adesea jucătorii nu pot merge într-o zonă nouă fără a fi aproape una de cealaltă. Uneori, jucătorii nu pot părăsi ecranul celuilalt. Acest lucru se poate datora designului jocului, dar am o suspiciune că se datorează adesea limitărilor arhitecturale.
Cu spații, putem susține doi jucători, probabil cu ecrane separate, care călătoresc de la un nivel sau de la un imobil la altul. Fiecare jucător poate locui în același spațiu sau în două spații separate. Când fiecare jucător se află într-o clădire diferită, ambele pot fi situate în două spații separate. Unul se convertesc pe aceeași zonă, motorul poate transfera unul din obiectele jocului jucătorului în spațiul opus.
Acesta este cu siguranță exemplul meu preferat pentru modul în care spațiile sunt minunate. În editorii există adesea secțiuni în care puteți dezvolta un nou tip de obiect. Acest obiect în creație va avea, de obicei, un port de vizualizare pentru a previzualiza creația în timp ce se dezvoltă zgomotele de dezvoltare.
Poate fi imposibil ca majoritatea motoarelor să susțină în mod natural un astfel de editor. Ce se întâmplă dacă utilizatorul modifică poziția și se ciocnește brusc cu simularea și bate lucrurile peste sau activează unele AI? Codul personalizat trebuie să fie creat pentru a manipula în mod grațios obiectul din memorie într-un fel. Fie codul special de izolare a cazului, fie un format intermediar, trebuie să fie editat și tradus de la editor la simularea reală. Etapele intermediare pot fi o formă de serializare sau un obiect complex "nonprofit". Obiectele proxy pot necesita adesea o introspecție avansată de cod care să fie implementată într-un mod util. Aceste opțiuni pot fi scumpe sau inutile pentru multe proiecte.
Cu toate acestea, dacă cineva are spații la dispoziție, un obiect de cameră și obiectul în creație pot fi plasate într-un spațiu izolat. Acest spațiu poate fi apoi predat oricăror sisteme necesare și manipulat în mod izolat grațios. Nu este necesar un cod de caz special sau o autorizare suplimentară într-un astfel de scenariu.
Nivelele multiple pot fi menținute cu ușurință în cadrul editorilor. Vizualizările multiple și simulările pot fi executate simultan în mod izolat. Ce se întâmplă dacă un dezvoltator a vrut să divizeze dezvoltarea a două niveluri pentru a schimba înainte și înapoi rapid? Aceasta ar putea fi o sarcină dificilă de inginerie software, sau arhitectura editorului ar putea implementa niște forme de spații.
Ce gestionează toate spațiile? Mulți dezvoltatori de jocuri ar putea avea în practică că totul trebuie să poată fi "deținut" de un anumit manager. Toate spațiile trebuie să poată fi gestionate de această singură entitate, nu? De fapt, acest tip de paradigmă nu este necesar tot timpul.
În motorul SEL, spațiile sunt construite dintr-o locație și pot fi privite prin nume cu un dicționar, dar ar fi mai bine ca majoritatea proiectelor să lase spațiile să fie gestionate de la caz la caz. Adesea, are sens să creați un spațiu în cadrul unui script aleatoriu, să vă mențineți un timp și apoi să eliberați spațiul. Altfel, un spațiu este creat și se află în memorie pe întreaga durată a runtime-ului jocului.
O recomandare bună ar fi să lași utilizatorul să aloce un spațiu și să îl elibereze la alegere. Ar fi bine să folosiți un singur alocator pentru acest comportament. Cu toate acestea, managementul instanței spațiale însuși, așa cum se constată prin experiență, ar putea fi cel mai bine să nu fie îngrijorat; lăsați-o utilizatorului.
Notă: când un spațiu este distrus, ar trebui să curețe toate obiectele din interior! Nu confunda lipsa unui manager cu o lipsă de gestionare a vieții.
Componentele sunt adesea înregistrate cu sistemele lor respective într-un motor bazat pe componente. Cu toate acestea, cu spații acest lucru devine inutil. În schimb, fiecare spațiu trebuie să conțină ceea ce se numește a subspațiu. Un subspațiu poate fi foarte banal să implementeze - să zicem, ca vector al obiectelor componente. Ideea este să conțină pur și simplu diferite tipuri de containere componente în fiecare spațiu. Ori de câte ori este construită o componentă, ea cere cu ce spațiu să fie asociat și se înregistrează cu subspațiul.
Un spațiu nu trebuie să aibă în mod necesar fiecare subspațiu în sine. Spre exemplu, un spațiu din meniu nu va avea nevoie probabil de simulare fizică și, prin urmare, ar putea să nu aibă o întreagă instanță a unei lumi fizice reprezentând un subspațiu de fizică.
În cele din urmă, trebuie remarcat faptul că într-o arhitectură bazată pe componente, obiectele de joc ar trebui să aibă un mâner sau pointer în spațiul în care locuiesc. În acest fel, spațiul devine o parte a identificatorului unic al obiectului jocului în sine.
Spațiile sunt extrem de simple de implementat și oferă multe beneficii importante. Pentru destul de mult la fiecare joc și motor de joc existente, adăugarea de spații va fi una pozitivă datorită ușurinței implementării. Utilizați spații, chiar și pentru proiecte foarte mici și jocuri foarte mici!
Ca o resursă, propria mea implementare a spațiilor este open source pentru vizionarea publicului în cadrul motorului de joc SEL.