Spaces nuttige spelobjectcontainers

Het hele punt van een ruimte is om game-objecten te houden. De spelobjecten in één spatie mogen dit niet hebben ieder manier om te communiceren met de game-objecten in een andere ruimte, zodat spaties een eenvoudig middel zijn om verschillende groepen game-objecten van elkaar te scheiden. In dit artikel leert u de voordelen van een dergelijke architectuur.

Als je een voorbeeld van de implementatie van spaties wilt zien, bekijk dan de open source game-engine SEL. Ik ben zelf actief bezig met het schrijven van SEL en ben er trots op het te presenteren als een volledig functionele bron voor de lezers van dit artikel.

Ik wil Sean Middleditch graag bedanken voor het onderwijzen van de voordelen van Spaces.

Tip: De voorwaarde ruimte in de context van dit artikel verwijst naar een speciale container game-objecten. Ik ben me niet echt bewust van een duidelijk omschreven officiële term. Als je er een weet, geef dan commentaar!


Conventioneel objectbeheer

In een conventionele game-engine worden game-objecten meestal opgeslagen in een enkele container. Een dergelijke container kan een allocator zijn samen met een handle manager. Soms is de container slechts een gekoppelde lijst. Wat de daadwerkelijke implementatie ook is, er kan slechts één container zijn die alle game-objecten bevat en elk gemaakt game-object bevindt zich altijd in deze container.

Dit gaat goed en werkt helemaal, maar er zijn wel enkele organisatorische problemen. Stel je bijvoorbeeld een traditionele game state manager voor. Vaak worden tussen de overgang van de ene staat naar de andere alle geladen game-objecten vrijgegeven en worden nieuwe vanaf de schijf ingelezen. Als een optimalisatie kunnen game-objecten voor de volgende status (of niveau) van tevoren in een afzonderlijke thread worden geladen, zodat statusovergangen onmiddellijk zijn.

Er is echter een vervelende kwestie die vaak optreedt: hoe vertegenwoordigen we GUI-elementen voor menu's? Misschien is de speler HUD gecodeerd met behulp van game-objecten en scripts die aan deze game-objecten zijn gekoppeld. Een naïeve implementatie van staatsbeheer zou oproepen dat alle HUD-elementen zouden worden vernietigd en opnieuw gecreëerd bij de overgang van de staat. Dit betekent dat er aangepaste code nodig is om de overgang van bepaalde objecten van de ene naar de andere status af te handelen.

Of misschien vraagt ​​een game-ontwerp om een ​​gek landschap op de achtergrond waar een enorme strijd gaande is, maar deze strijd mag op geen enkele manier de voorgrond (of de speler) beïnvloeden.

Vaak ontstaan ​​er rare hacky-oplossingen voor dit soort dingen, zoals het weergeven van HUD-elementen als extreem ver weg van de rest van de gameplay in de wereld. Pauzemenu's en dergelijke worden zojuist in het zicht geplaatst en verdwijnen anders. Over het algemeen is aangepaste code vereist om game-objecten te beheren en te ordenen, omdat ze allemaal in één fabriek of container zitten.


Meerdere game-objectcontainers

Een acceptabele oplossing voor dit probleem zou zijn om gebruik te maken van spaties (zie de aanvullende videobeschrijving door mijn collega Sean Middleditch). Omdat alle game-objecten in elke ruimte geen interactie hebben, worden ruimtes een natuurlijke manier om de organisatie van game-objecten te benaderen. Dit kan de behoefte aan speciale code om een ​​afzonderlijke logische container binnen een werkelijke container te houden aanzienlijk beperken (zoals vermeld in de vorige sectie).

Laten we een kort voorbeeld bekijken van hoe een implementatie van de ruimte eruit zou kunnen zien in een taal die lijkt op C ++:

 class Space public: GameObject CreateObject (string name); const string GetName (void) const; privé: string m_name; // Kan een vorm van toewijzer zijn, misschien gewoon een std :: vector Container m_objects; ;

Het is vaak handig om op naam een ​​spatie op te zoeken. Dit is vooral handig voor scripts waarbij een script code kan gebruiken zoals:

 // Voer wat logica in voor de achtergrond lokale ruimte = GetSpace ("Achtergrond") tornado = space.CreateObject ("Tornado") // Elders kunnen we iets volledig geïsoleerd van de achtergrond doen, // zoals het ophalen van de speler (die voor sommige reden stierf) en speelde een // dood-animatie over de speler lokale ruimte = GetSpace ("CurrentLevel") player = space.GetPlayer () space.CreateObjectAt ("DeathAnimation", speler)
Diagram dat de eenvoudige organisatie van game-objecten in geïsoleerde containers weergeeft. Niet alle ruimtes hebben dezelfde hoeveelheid game-objecten, of zelfs dezelfde game-objecten.

Welke objecten gaan naar ruimtes?

Het antwoord op deze vraag is: allemaal! Elk type game-object wordt in een spatie geplaatst. Als u bekend bent met aggregatie (of op onderdelen gebaseerd ontwerp), dan zullen een spelobject en de bijbehorende componenten zich allemaal in dezelfde ruimte bevinden.

Het idee is om een ​​architecturale functie te maken om een ​​eenvoudige, effectieve manier om game-objecten bij elkaar te groeperen en ze te isoleren van andere groepen.

Terminologie

Ruimtes lijken veel op andere concepten die al een tijdje rondzweven. Er is gezegd dat ruimtes lijken op de weergavelijst in Flash. Als je bekend bent met portals voor het ruimen van munten (vooral belangrijk in 3D-games met veel binnenruimtes), is het idee hier ook heel gelijkaardig.

Er is hier echter een belangrijk onderscheid te maken. Spaties zijn niet een vorm van ruimtelijke partitionering zoals wordt gedaan met portals of andere ruimtelijke partitionering (zoals de populaire BSP-boom) voor renderocclusie. Spaties zijn een bouwkundig functie om isolatie van algemene game-objecten mogelijk te maken.

Persoonlijk denk ik graag aan ruimtes zoals Photoshop-lagen: alle lagen kunnen gelijktijdig worden bekeken (of gehoord), maar bij het schilderen op een laag worden geen andere lagen ooit rechtstreeks beïnvloed; elke laag is uniek en geïsoleerd.

Systemen en ruimtes

Het concept van a systeem, voor de doeleinden van dit artikel kan worden omschreven als een set van functionaliteit (functies of methoden) die werkt op de spelobjecten van een ruimte. Op deze manier wordt een ruimte aan een systeem overhandigd om wat actie uit te voeren; een bepaald systeem is globaal voor het hele spel.

Diagram dat de eenvoud illustreert van het toestaan ​​dat een systeem als invoer op een ruimte werkt.

Stel je een grafisch systeem voor dat a bevat void Graphics :: DrawWorld (Space space) functie. Deze DrawWorld functie zou lus over de objecten binnen de gegeven ruimte die renderbaar zijn en ze op het scherm tekenen.

Het idee is om nu code (systemen) te schrijven die werkt op een gegeven invoer van game-objecten. Binnen dergelijke systemen hoeft geen speciaal tracking of beheer van game-objecten plaats te vinden. Een systeem zou niets moeten doen behalve bewerkingen uitvoeren op game-objecten.

Deze stijl geeft je een aantal hele mooie voordelen, zoals beschreven in het volgende gedeelte.


Voordelen van Spaces

Het meest directe voordeel van het implementeren van spaties binnen een engine is dat de verwerking van GUI-elementen of -menu's triviaal wordt. We kunnen een ruimte maken die specifiek is voor een bepaald menu en, wanneer dit menu inactief is, hoeven systemen eenvoudigweg niet te werken met de inhoud van de ruimte. Wanneer een menu inactief is, bevindt het zich in het geheugen (de spelobjecten die het geheugen bevatten, zit in de menubalk) en doet niets; het wordt noch geactualiseerd door een logicasysteem noch weergegeven door een grafisch systeem.

Wanneer dit menu weer actief wordt, kan het eenvoudig worden doorgegeven aan de juiste systemen. Het menu kan vervolgens worden bijgewerkt en op de juiste manier worden weergegeven. Tegelijkertijd kan het spelen van de gameplay achter het menu op geen enkele manier worden gestopt, hoewel het misschien nog steeds wordt doorgegeven aan het grafische systeem dat moet worden weergegeven. Dit implementeert triviaal een elegant en robuust pauze-en-hervat type functionaliteit die gewoon impliciet komt door de manier waarop ruimtes worden gedefinieerd.

Geïsoleerde niveaus

Vaak zal de speler in RPG-achtige spellen zoals Pokémon huizen en hutten betreden en verlaten. Wat het spel betreft, zijn deze verschillende huizen meestal volledig geïsoleerd; kleine huizen of grotten zijn een ideaal scenario om ruimtes toe te passen. Een volledige ruimte kan worden geconstrueerd om de spelobjecten van een bepaald huis te bevatten, en deze kunnen in het geheugen worden geladen en geïnitialiseerd en rusten tot ze nodig zijn. Onmiddellijke overgangen kunnen worden bereikt door simpelweg uit te wisselen welke ruimte wordt overhandigd aan de verschillende motorsystemen.

Een leuk idee voor 2D-games zoals platformspelers (of zelfs 3D-spellen) zou kunnen zijn om de werkelijke niveaus en vijanden van het spel op de achtergrond te simuleren. Dit kan de wereld tot leven brengen op een manier die eigenlijk geen extra inhoud vereist, en nauwelijks enige extra ontwikkelingstijd. Het beste voorbeeld dat ik hier kan vinden is Rayman Legends:


Werkelijke vijanden, net zoals de speler normaal ziet, kunnen rondspringen of kruipen op de muren in de verte. Overgangen tussen deze verschillende "lagen" kunnen enkele zeer interessante ontwerpmogelijkheden bieden.

Dit soort mogelijkheden zijn eigenlijk vrij zeldzaam om voorbeelden van te vinden en het idee van ruimtes wordt niet echt omarmd door moderne AAA-studio's of -motoren. Het ontwerp is echter solide en de voordelen zijn reëel.

Lokale multiplayer

In veel spellen met ondersteuning voor meerdere spelers waarbij beide spelers met dezelfde gameclient spelen, zijn er enkele beperkingen. Vaak kunnen de spelers niet naar een nieuw gebied gaan zonder dat ze in de buurt van elkaar zijn. Soms kunnen de spelers elkaars scherm niet eens verlaten. Dit kan te maken hebben met game-design, maar ik heb het vermoeden dat dit vaak te wijten is aan architecturale beperkingen.

Met spaties kunnen we twee spelers ondersteunen, misschien met gesplitste schermen, die van het ene niveau naar het andere gaan. Elke speler kan zich in dezelfde ruimte bevinden, of in twee afzonderlijke ruimtes. Wanneer elke speler zich in een ander gebouw bevindt, bevinden ze zich mogelijk in twee afzonderlijke ruimtes. Als ze op hetzelfde gebied samenkomen, kan de engine een van de spelobjecten van de speler overbrengen naar de tegenoverliggende ruimte.

Redacteur Ondersteuning

Dit is absoluut mijn favoriete voorbeeld van hoe ruimtes geweldig zijn. In editors zijn er vaak secties waar u een nieuw type object kunt ontwikkelen. Dit object in creatie heeft meestal een viewport om een ​​voorbeeld van de creatie te zien terwijl de ontwikkeling meegiet.

Het kan voor de meeste zoekmachines onmogelijk zijn om zo'n editor op natuurlijke wijze te ondersteunen. Wat als de gebruiker de positie aanpast en het plotseling botst met de simulatie en dingen omver gooit, of sommige AI activeert? Aangepaste code moet worden gemaakt om het object op de een of andere manier in het geheugen op een correcte manier te verwerken. Ofwel speciale isolatiecode, ofwel een tussenformaat, moet worden bewerkt en vertaald vanuit de editor naar de daadwerkelijke simulatie. Tussenstappen kunnen een vorm van serialisatie zijn of een complex niet-opdringerig dummy "proxy-object". Proxy-objecten kunnen vaak geavanceerde code-introspectie vereisen om op een nuttige manier te worden geïmplementeerd. Deze opties kunnen voor veel projecten duur of onnodig zijn.

Als er echter spaties tot hun beschikking staan, kunnen een cameraobject en het object in creatie in een geïsoleerde ruimte worden geplaatst. Deze ruimte kan dan worden overgedragen aan alle systemen die nodig zijn en op een elegante manier worden afgehandeld. In een dergelijk scenario is geen speciale case-code of extra authoring nodig.

Meerdere niveaus kunnen met gemak binnen editors worden onderhouden. Meerdere viewports en simulaties kunnen tegelijkertijd op zichzelf worden uitgevoerd. Wat als een ontwikkelaar de ontwikkeling van twee niveaus zou willen splitsen om snel heen en weer te wisselen? Dit kan een moeilijke taak voor software-ontwikkeling zijn, of de architectuur van de editor kan een of andere vorm van spaties implementeren.


Implementatie-ideeën

Ruimtebeheer

Wat beheert alle ruimtes? Veel game-ontwikkelaars hebben het in hun praktijk dat alles moet kunnen worden "eigendom" van een manager. Alle ruimtes moeten door deze enkele entiteit kunnen worden beheerd, toch? Eigenlijk is dit soort paradigma gewoon niet nodig altijd.

In de SEL-engine worden spaties geconstrueerd vanaf één locatie en kunnen ze op naam worden opgezocht met een woordenboek, maar voor de meeste projecten is het misschien het beste om spaties van geval tot geval te laten beheren. Het is vaak logisch om een ​​spatie in een willekeurig script te maken, het een tijdje vast te houden en vervolgens de spatie los te laten. In andere gevallen wordt een spatie gemaakt en wordt de volledige duur van de looptijd van de game in het geheugen opgeslagen.

Een goede aanbeveling is om de gebruiker gewoon een spatie toe te wijzen en deze naar believen vrij te geven. Het zou waarschijnlijk goed zijn om één enkele toewijzer voor dit gedrag te gebruiken. Het beheer van de ruimte-instantie zelf, zoals gevonden door ervaring, kan echter het best geen zorgen maken; laat het aan de gebruiker over.

Notitie: wanneer een ruimte wordt vernietigd, moet deze alle objecten binnenin opruimen! Verwar het gebrek aan een manager niet met een gebrek aan levenslange management.

Componenten en subruimten

Componenten worden vaak geregistreerd met hun respectieve systemen in een componentgebaseerde engine. Met spaties wordt dit echter overbodig. In plaats daarvan zou elke spatie moeten bevatten wat een a wordt genoemd subspace. Een subruimte kan erg triviaal zijn om te implementeren, bijvoorbeeld als een vector van componentobjecten. Het idee is om eenvoudig verschillende soorten componentcontainers in elke ruimte te bevatten. Telkens wanneer een component wordt geconstrueerd, wordt gevraagd in welke ruimte deze moet worden gekoppeld en registreert zichzelf de subruimte.

Een ruimte hoeft niet per se elk type subruimte in zichzelf te hebben. Een menubalk heeft bijvoorbeeld waarschijnlijk geen fysieke simulatie nodig en heeft daarom mogelijk niet een hele instantie van een fysica-wereld die een subruimte van fysica vertegenwoordigt..

Ten slotte moet worden opgemerkt dat in een op componenten gebaseerde architectuur uw spelobjecten een handvat of verwijzing naar de ruimte waarin ze zich bevinden moeten hebben. Op deze manier wordt de ruimte een onderdeel van de unieke ID van het spelobject zelf.


Conclusie

Ruimtes zijn uiterst eenvoudig te implementeren en bieden veel belangrijke voordelen. Voor vrijwel elke game en game-engine die bestaat, zal de toevoeging van spaties een positieve zijn vanwege het gemak van de implementatie. Gebruik spaties, zelfs voor hele kleine projecten en hele kleine games!

Als hulpmiddel is mijn eigen implementatie van ruimtes open source voor publiek bekijken in de game-engine SEL.