Hela punkten av a rymden är att hålla spelobjekt. Spelobjekten i ett utrymme borde inte ha några sätt att kommunicera med spelobjekten i ett annat utrymme, så att mellanslag ger ett enkelt sätt att skilja olika grupper av spelobjekt. I den här artikeln lär du dig fördelarna med en sådan arkitektur.
Om du vill se ett exempel på genomförandet av mellanslag, se open source-spelmotorn SEL. Jag själv är aktivt författande SEL och är stolt över att presentera den som en fullt fungerande resurs för läsare av denna artikel.
Jag vill tacka Sean Middleditch för att lära mig om fördelarna med Spaces.
Tips: Termen rymden I samband med denna artikel avses en speciell behållare av spelobjekt. Jag är faktiskt inte medveten om en tydligt definierad officiell term. Om du känner till en snälla kommentera!
I en konventionell spelmotor lagras spelobjekt i en enda behållare. En sådan behållare kan vara en fördelare tillsammans med en handtagshanterare. Ibland är behållaren bara en länkad lista. Oavsett vad det faktiska genomförandet är, kan det finnas bara en enda behållare som innehåller alla spelobjekt, och varje spelobjekt är alltid i denna behållare.
Det här är bra och fungerar helt, men det har några organisatoriska problem. Tänk dig till exempel en traditionell spelstatistiker. Ofta, mellan övergången från ett tillstånd till ett annat, befrias alla aktuella laddade spelobjekt och nya läses in från disken. Som en optimering kan spelobjekt för nästa tillstånd (eller nivå) laddas på en separat tråd före tid så att tillståndsövergångar är momentana.
Det finns dock ett irriterande problem som vanligtvis uppstår: hur representerar vi GUI-element för menyer? Kanske är spelaren HUD kodad med hjälp av spelobjekt och skript som är kopplade till dessa spelobjekt. En naiv implementering av statsförvaltningen skulle kräva att alla HUD-element förstörs och återskapas vid tillståndsövergången. Detta innebär att anpassad kod kommer att krävas för att hantera övergången av vissa objekt från ett tillstånd till ett annat.
Eller kanske en speldesign kallar galet bakgrundslandskap där någon enorm kamp går på - men denna kamp får inte störa förgrunden (eller spelaren) på något sätt.
Ofta uppstår konstiga hacky lösningar för sådana sorters saker, som att representera HUD-element som extremt långt ifrån resten av spelet i världen. Paus menyer och liknande flyttas bara in vid behov, och flyttas annars bort. I allmänhet krävs anpassad kod för att hantera och organisera spelobjekt, eftersom de alla är bosatta på en enda fabrik eller behållare.
En acceptabel lösning på detta problem skulle vara att använda utrymmen (se ytterligare videobeskrivningen av min kollega Sean Middleditch). Eftersom alla spelobjekt i varje utrymme har nollinteraktion blir utrymmen ett naturligt sätt att närma sig organisationen av spelobjekt. Detta kan i hög grad minimera behovet av specialkod för att behålla en separat logisk behållare inom en faktisk behållare (som nämnts i föregående avsnitt).
Låt oss ta en snabb titt på ett exempel på vad en rymdimplementering kan se ut på ett språk som liknar C ++:
klassrummet offentligt: GameObject CreateObject (strängnamn); const sträng GetName (void) const; privat: sträng m_name; // Kan vara någon form av fördelare, kanske bara en std :: vektor Container m_objects; ;
Det är ofta användbart att kunna leta upp ett mellanslag vid namn. Detta är särskilt bra för skript där ett skript kan använda kod som så:
// Kör lite logik för bakgrunden lokalt utrymme = GetSpace ("Background") tornado = space.CreateObject ("Tornado") // Annanstans kan vi göra något helt isolerat från bakgrunden, // som att hämta spelaren (som för vissa orsak dog) och spelar en // dödsanimering över toppen av spelaren lokalt utrymme = GetSpace ("CurrentLevel") player = space.GetPlayer () space.CreateObjectAt ("DeathAnimation", spelare)Diagram som visar den enkla organisationen av spelobjekt i isolerade behållare. Inte alla utrymmen har samma mängd spelobjekt, eller till och med samma spelobjekt.
Svaret på denna fråga är: alla av dem! Vilken typ av spelobjekt som helst kommer att placeras i ett mellanslag. Om du är bekant med aggregering (eller komponentbaserad design) kommer ett spelobjekt och dess tillhörande komponenter att ligga inom samma utrymme.
Tanken är att skapa en arkitektonisk funktion för att möjliggöra ett enkelt och effektivt sätt att gruppera spelobjekt tillsammans och isolera dem från andra grupper.
Spaces är ganska liknade några andra begrepp som har flyttat runt ett tag nu. Det har sagts att mellanslag är relaterade till visningslistan i Flash. Om du är bekant med portaler för att göra culling (särskilt viktigt i 3D-spel med många inredningsrum), är idén ganska lika här också.
Det finns dock en viktig skillnad att göra här. Rummen är inte en form av rumslig partitionering som görs med portaler eller annan spridning (som det populära BSP-trädet) för att göra ocklusion. Mellanslag är en arkitektonisk funktion för att tillåta isolering av allmänna spelobjekt.
Personligen tycker jag om att tänka på utrymmen som Photoshop-lager: alla lager kan ses (eller hörs) samtidigt, men när man målar på ett lager påverkas inga andra lager direkt, varje lager är unikt och isolerat.
Konceptet a systemet, i enlighet med denna artikel kan beskrivas som en uppsättning funktionalitet (funktioner eller metoder) som fungerar på spelobjekten i ett utrymme. På detta sätt överlämnas ett utrymme till ett system för att utföra vissa åtgärder. ett visst system är globalt för hela spelet.
Diagram som exemplifierar enkelheten att låta ett system fungera på ett utrymme som ingång.Tänk dig ett grafiksystem som innehåller en tomrumsgrafik :: DrawWorld (rymdutrymme)
fungera. Detta DrawWorld
funktionen skulle slingra över objekten inom det angivna utrymmet som kan återges och dra dem på skärmen.
Tanken är att nu skriva kod (system) som arbetar med en given ingång av spelobjekt. Ingen speciell spårning eller hantering av spelobjekt måste ske inom sådana system. Ett system ska inte göra något annat än att utföra operationer på spelobjekt.
Denna stil ger dig några riktigt fina fördelar, som beskrivs i nästa avsnitt.
Den mest omedelbara fördelen med att implementera utrymmen inom en motor är att hanteringen av GUI-element eller menyer blir trivial. Vi kan konstruera ett utrymme dedikerat till en viss meny och närhelst denna meny är inaktiv behöver system inte helt enkelt fungera på innehållet i utrymmet. När en meny är inaktiv sitter den i minnet (spelobjekten som innehåller minnet sitter inom menyutrymmet) och gör ingenting; Det uppdateras inte av ett logiskt system eller görs av ett grafiksystem.
När den här menyn blir aktiv igen kan den enkelt överlämnas till lämpliga system. Menyn kan sedan uppdateras och göras på lämpligt sätt. Samtidigt kan spelningen på baksidan av menyn sluta uppdateras på något sätt, även om det kanske fortfarande övergår till grafiksystemet som ska återges. Detta implementerar trivialt en elegant och robust paus och återupptatt typ av funktionalitet som bara kommer implicit på grund av hur utrymmen definieras.
Ofta, i spel i RPG-stil som Pokémon kommer spelaren att gå in och lämna hus och hytter. När det gäller gameplay är dessa olika hus vanligtvis helt isolerade. små hus eller grottor är ett idealiskt scenario för att tillämpa utrymmen. Ett helt utrymme kan konstrueras för att innehålla spelobjekten för ett visst hus, och dessa kan laddas och initialiseras i minnet och vila tills det behövs. Omedelbara övergångar kan uppnås genom att enkelt byta ut vilket utrymme som överlämnas till de olika motorsystemen.
En cool idé för 2D-spel som plattformar (eller till och med 3D-spel) kan vara att simulera faktiska nivåer och fiender från spelet i bakgrunden. Detta kan få världen att leva på ett sätt som inte egentligen kräver ytterligare innehåll, och knappast någon ytterligare utvecklingstid. Det bästa exemplet jag kunde hitta av detta är Rayman Legends:
Faktiska fiender, samma som spelaren ser normalt, kan hoppa runt eller krypa på väggarna på avstånd. Övergångar mellan dessa olika "lager" kan ge några mycket intressanta designmöjligheter.
Dessa typer av möjligheter är egentligen sällsynta att hitta exempel på och tanken om utrymmen är inte riktigt omhändertagen av moderna AAA-studior eller -motorer. Emellertid är designen solid och fördelarna är verkliga.
I många spel med multiplayer support där båda spelarna spelar med samma spelklient finns det vissa begränsningar. Ofta kan spelarna inte gå till ett nytt område utan att de båda är nära varandra. Ibland kan spelarna inte ens lämna varandras skärm. Detta kan bero på speldesign, men jag har misstankar att det ofta beror på arkitektoniska begränsningar.
Med mellanslag kan vi stödja två spelare, kanske med delade skärmar, resa från en nivå eller bygga till en annan. Varje spelare kan bo i samma utrymme, eller i två separata utrymmen. När varje spelare befinner sig i en annan byggnad kan de båda vara i två separata utrymmen. En konvergerar sig på samma område, motorn kan överföra en av spelarens spelobjekt till det motsatta utrymmet.
Detta är definitivt mitt favorit exempel på hur utrymmen är fantastiska. I redaktörer finns det ofta sektioner där du kan utveckla en ny typ av objekt. Detta objekt i skapandet kommer vanligen att ha ett visningsport för att förhandsgranska skapandet som en utveckling av hums tillsammans.
Det kan vara omöjligt för de flesta motorer att naturligtvis stödja en sådan redaktör. Vad händer om användaren ändrar positionen och det plötsligt kolliderar med simuleringen och slår saker över eller aktiverar lite AI? Anpassad kod måste skapas för att graciöst hantera objektet i minnet på något sätt. Antingen speciella fall isoleringskod, eller något mellanformat, måste redigeras och översättas från redaktören till den faktiska simuleringen. Intermediära steg kan vara någon form av serialisering eller komplicerat icke-påträngande dummy "proxy-objekt". Proxyföretag kan ofta kräva avancerad kodintrospektion som ska genomföras på ett användbart sätt. Dessa alternativ kan vara dyra eller onödiga för många projekt.
Men om man har utrymmen till sitt förfogande kan ett kamerobjekt och objektet i skapandet placeras i ett isolerat utrymme. Detta utrymme kan sedan överlämnas till vad som behövs och hanteras isolerat graciöst. Ingen särskild ärende kod eller ytterligare författande skulle vara nödvändigt i ett sådant scenario.
Flera nivåer kan enkelt behållas inom redaktörerna. Flera visningsportar och simuleringar kan köras i isolering samtidigt. Vad händer om en utvecklare ville dela upp skärmen på två nivåer för att snabbt växla fram och tillbaka? Det här kan vara en svår programvaruteknik, eller redaktörsarkitekturen kan implementera någon form av mellanslag.
Vad hanterar alla utrymmen? Många spelutvecklare kan ha det i sin praktik att allt måste kunna "ägas" av någon chef. Alla utrymmen måste kunna hanteras av denna enskilda enhet, eller hur? Egentligen är denna typ av paradigm bara inte nödvändigt hela tiden.
I SEL-motorn är mellanslag konstruerade från en plats och kan letas upp med namn med en ordlista, men det kan vara bäst för de flesta projekt att bara låta utrymmen hanteras från fall till fall. Ofta är det bara meningsfullt att skapa ett mellanslag inom något slumpmässigt skript, hålla fast vid det en stund och släpp sedan ut mellanslaget. Andra gånger skapas ett mellanslag och sitter i minnet hela tiden för spelets körtid.
En bra rekommendation skulle vara att bara låta användaren allokera ett utrymme och frigöra det efter vilja. Det skulle troligen vara bra att använda en enda fördelare för detta beteende. Förvaltningen av rymdinstansen själv, som hittades genom erfarenhet, kan dock vara bäst oroad över; lämna det till användaren.
Notera: När ett utrymme förstörs, ska det städa upp alla föremål inom! Förväxla inte bristen på en chef med brist på livslängdshantering.
Komponenter registreras ofta med sina respektive system i en komponentbaserad motor. Men med utrymmen blir det onödigt. I stället bör varje utrymme innehålla det som kallas a underrum. En delrum kan vara mycket trivial att implementera-säg, som en vektor av komponentobjekt. Tanken är att helt enkelt innehålla olika typer av komponentbehållare inom varje utrymme. När en komponent är konstruerad begär den vilken plats den ska associeras med och registrerar sig med delrummet.
Ett utrymme behöver inte nödvändigtvis ha varje enskild typ av delrum inom sig själv. Ett menyutrymme kommer förmodligen inte att behöva någon fysisk simulering och kan därför inte ha en hel instans av en fysikvärld som representerar ett fysikunderrum.
Slutligen bör det noteras att i en komponentbaserad arkitektur måste dina spelobjekt ha ett handtag eller en pekare till det utrymme de bor i. På så sätt blir rymden en del av den unika identifieraren för själva spelet objektet.
Områdena är extremt enkla att genomföra och ger många viktiga fördelar. För nästan alla spel och spelmotorer i existensen kommer tillägget av mellanslag att vara en positiv på grund av enkel implementering. Använd mellanslag, även för mycket små projekt och mycket små spel!
Som en resurs är mitt eget genomförande av mellanslag öppen källkod för allmän visning i spelmotorn SEL.