Viktor PennskogViktor Pennskog
VOICEBOX still

VOICEBOX

Ett 2D co-op-spel på ett farligt nöjesfält, med en deterministisk och segmentbaserad procedurell levelgenerator.

Team
5 studenter
Tidsperiod
10 veckor
Omfattning
Procedurell generering + grundsystem
Status
Släppt på Steam
Teknik
UnityC#PCGScriptableObjectMultiplayer

Procedurgenererade banor för VOICEBOX

VOICEBOX är ett 2D top-down co-op-spel för upp till sex spelare. Spelarna utforskar en nöjespark, löser pussel och undviker monster som kan höra dem prata.

Jag var en av två programmerare i projektet. Mitt huvudsakliga ansvar var den procedurgenererade levelgeneratorn, men jag arbetade även med networking, Steam-integration, ljud och andra grundsystem.

Segmentbaserad generering

Generatorn bygger varje bana från LevelSegment-prefabs istället för enskilda tiles. Det ger spelet procedurgenererad variation samtidigt som varje rum och korridor kan designas för pussel, fiender och co-op-interaktioner.

Varje segment innehåller den data som generatorn behöver:

  • Connectors definierar var andra segment kan kopplas fast
  • Bounds hindrar rum från att överlappa
  • viktade val styr vilka segment som kan dyka upp
  • användningsgränser hindrar samma segment från att användas för ofta
  • item spawners anger positioner för interagerbara objekt

När ett segment placeras riktar generatorn in två kompatibla connectors och jämför det nya segmentets bounds med den befintliga layouten. Om segmentet överlappar försöker generatorn med ett annat alternativ.

Unity Scene View som visar segmentets connectors och bounds med Gizmos

Unity Gizmos visualiserar connectors, deras riktningar och segmentets bounds. Dessa debugverktyg gjorde det mycket enklare att hitta felkonfigurerade rum utan att behöva läsa generatorns kod.

Genereringspipeline

När generatorn växte delade jag upp den i en konfigurerbar GenerationStage-pipeline. Varje steg har ett eget ansvar och bygger vidare på resultatet från föregående steg:

  1. Placera startrummet och generera grundlayouten
  2. Lägg till ett särskilt slutsegment
  3. Placera pusselinnehåll
  4. Öppna dörrar mellan anslutna rum
  5. Stäng oanvända kopplingar med väggar
  6. Placera föremål i den färdiga banan
Flödesdiagram som visar levelgeneratorns steg

Strukturen höll rumsplaceringen separerad från gameplayinnehållet och gjorde systemet enklare att bygga ut, felsöka och konfigurera i Unity-inspektorn.

Generatorn kan köras direkt under gameplay eller långsamt vid felsökning, så att varje placeringsbeslut syns i Scene View.

Deterministisk multiplayer

Banorna genereras från ett seed. Med samma seed och segmentdata kan varje klient själv återskapa samma layout istället för att ta emot hela kartan över nätverket.

Deterministisk generering var också värdefull under utvecklingen. När en layout visade ett fel kunde jag spara dess seed och testa exakt samma bana flera gånger.

Resultat

Det färdiga systemet genererar sammanhängande layouter, lägger till slutrum och pussel, hanterar dörrar och väggar samt placerar föremål. Den datadrivna uppsättningen gör det också möjligt att lägga till och balansera nya rum utan att ändra generatorns kod.

Den viktigaste lärdomen från projektet var att procedurgenerering kräver mer än slumpmässighet. En användbar generator behöver också tydliga regler, bra debugverktyg och tillräcklig kontroll för att andra i teamet tryggt ska kunna skapa innehåll.