Dialog system in actie.
Ontwerp prototype van dialog system.
Een compacte weergave van de BaseDialog class.
Implementatie van een dialog door middel van de BaseDialog class.
Voorbeeld van de user interface in-game.
Teksteffecten enum en implementatie van een teksteffect.
Regenboogkleurige en schuddende teksteffecten.
Afbeelding van test dialoog.

Een dialog system ontwerpen voor World Eater


In deze development blog heb ik het over hoe ik een dialog system heb gemaakt voor World Eater. Even voor context: World Eater is een 3D game die draait om een boss battle op een planeet. We hebben besloten een dialog system te maken, om het spel meer flavour te geven, en om het verhaal van de game te vertellen.

Het kan moeilijk zijn om een goede dialog system te maken vanwege de vele features die verwacht worden door andere developers. Ook zijn er veel assets te vinden die gemaakt zijn door anderen die dit probleem oplossen. Toch heb ik gekozen om dit systeem zelf te schrijven, zodat ik erachter kon komen hoe een goede dialog system ontworpen wordt.



Onderzoek

Ik heb een available product analysis gedaan om erachter te komen wat voor soort dialog systemen andere games hebben en welke features die hebben. De structuur van de meeste dialogsystemen komt op hetzelfde neer. De speler krijgt een afbeeldingen te zien van een personage, de tekst die het personage uitspreekt en eventueel de mogelijkheid voor de speler om te reageren.

Eerste prototype

In een meer technische zin: Elke dialoog object heeft een afbeelding, een string en een array van keuzes. Dit vormde de basis van het eerste prototype voor mij. Dit eerste prototype (inclusief een basic UI) heb ik aan mijn teamgenoten laten zien en zij waren er tevreden mee.

Aan de hand van dit prototype en het eerder genoemde onderzoek, heb ik besloten om nog meer features toe te voegen die vooral voor extra juice zorgden.



Doelen en vereisten

De primaire doelen van dit systeem zijn het toevoegen van flavour, het duidelijk maken wat er aan de hand is aan de speler en het mogelijk maken voor de speler om keuzes te maken.

Om de flavour te verhogen willen ik de mogelijkheid om geluid, plaatjes en teksteffecten toe te voegen aan de dialog. Verder wil ik branching dialogs, events die afvuren wanneer een knop ingedrukt wordt, een soepele overgang tussen de berichten en een goede developer experience.

Controller support is niet belangrijk, omdat de game op WebGL draait en dus gebruikt vrijwel iedereen een muis en een toetsenbord.



Systeem architectuur

Het dialog system voor World Eater is ontworpen met modulariteit en uitbreidbaarheid in gedachten, zodat toekomstige toevoegingen zonder problemen gemaakt kunnen worden. Dit zijn de belangrijkste onderdelen van de architectuur:

BaseDialog

De BaseDialog-klasse vormt de kern van het systeem. Het definieert de basiseigenschappen van een dialoog, zoals de tekst, opties, het karakter dat spreekt, en eventuele tekst- of geluidseffecten.

Er zijn een aantal classes die afstammen van BaseDialog, zoals SimpleContinuingDialog en SimpleEndingDialog. Deze classes zorgen voor minder arguments in de constructor en verbeteren de developer experience. Je kan ook gewoon de class zelf gebruiken wanneer dat nodig is.

Dialog flow

De DialogSystemController class regelt de flow van de dialog, zo houdt deze bijvoorbeeld bij wat het huidige bericht is. Een instantie van deze class wordt meegeven aan de BaseDialog en onderliggende classes, zodat deze kunnen wisselen tussen berichten.

De BaseDialog-klasse ondersteunt branching dialogs door middel van de nextDialog-eigenschap. Dit maakt het eenvoudig om dialoog keuze diagram te ontwerpen die afhangen van keuzes die de speler maakt.

Met behulp van callbacks zoals OnClickedOption en OnClickedNext kunnen specifieke acties worden uitgevoerd wanneer een speler interactie heeft met de dialoog.

Events

Het dialog system maakt gebruik van een evenement-gedreven aanpak om interacties soepel te laten verlopen. Bijvoorbeeld:

Wanneer een speler een optie kiest, roept het systeem de ClickOption method aan, die code uitvoert of naar een volgende dialoog schakelt. Het systeem kan eenvoudig worden uitgebreid met hooks voor het afvuren van externe events, zoals het starten van een animatie of het wijzigen van de gamestate.

Scheiding van logica en rendering

De weergave van de dialoog (zoals hoe tekst wordt weergegeven op het scherm) is volledig gescheiden van de logica die de flow van de dialoog beheert. Dit wordt bereikt door een aparte class te hebben voor het renderen van de UI elements.

Verwante classes & interfaces

  • SimpleContinuingDialog
  • SimpleEndingDialog
  • BaseDialogCharacter
  • BaseDialogOption
  • SimpleContinueDialogOption
  • SimpleEndDialogOption
  • DialogSound
  • DialogSystemController
  • DialogSystemUI
  • IDialog
  • IDialogCharacter
  • IDialogOption
  • TestDialog(1-5)
  • TestDialogCharacter(1-2)




User interface

Ontwerp

Het ontwerp van het dialog system is simpel en consistent gehouden, met een focus op leesbaarheid en een futuristische uitstraling die past bij het ruimtethema van de game:

UI Design: Donkere doorzichtige achtergronden met afgeronde hoeken en contrasterende lichte tekst. Een afbeelding aan de linkerkant, tekst aan de rechterkant en interactieopties rechtsonder. Interactie: Opties vergroten wanneer de muis erover beweegt om interactie intuïtief te maken.

Teksteffecten

Unity's UI toolkit heeft support voor Rich Text, dus ik heb dit gebruikt om simpele effecten zoals gekleurde en schuin-gedrukte tekst toe te voegen aan de dialog system. Deze kunnen ingevoerd worden in een string door middel van tags die een beetje lijken op HTML.
Zo wordt bijvoorbeeld de onderstaande tekst omgetoverd tot "Hello, world."

<i>Hello</i>, <b>world</b>

Maar eigenlijk wilden we nog meer effecten toevoegen aan de dialog system om de tekst meer leven te geven. Ik heb besloten deze effecten toe te voegen: Regenboogkleurige, golvende, op en neergaande, schuddende en draaiende tekst. Ten slotte heb ik ook nog wat opties om tekst groter en kleiner te maken toegevoegd als tekst effect.

Deze custom tekst effecten werden samen gevoegd in één C# enum.

Deze enum die DialogTextEffects heet, kan gebruikt worden bij het aanmaken van een nieuwe dialog om één of meer effecten toe te voegen. Niet alle effecten werken met elkaar vanwege de manier waarop ze achter de schermen geimplementeerd zijn, veel wel.

Hiernaast zie je een voorbeeld van een dialog waar een regenboog effect wordt gebruikt. Het regenboog effect is een van de weinige effecten waar ook een "target" string aan toegevoegd kan worden. In dit geval is dat "space dust".

De implementatie van de verschillende effecten bevinden zich in de DialogSystemUI class. In de CreateDialogElement method wordt door middel van een foreach loop en een switch case bepaald welke effecten worden toegepast.

Alle effecten behalve de tekstgroottes worden toegepast door middel can Unity Coroutines die elke frame een IEnumerator functie aanroepen. Deze functies nemen de Label (het tekstvak) en voeren het effect met een aantal parameters er op uit.

In dit geval wordt de positie (offset) van het tekstvak berekent door middel van willekeurige getallen, waardoor de speler de illusie krijgt dat de tekst schudt. Hieronder is het effect te zien dat de bovenstaande ShakeEffect method produceert.

Overgangen

Om berichten soepel over te laten gaan, heb ik een fade-out en fade-in animatie toegevoegd via Unity’s UI Toolkit. Dit creëert een vloeiende overgang en geeft een visuele indicatie van het einde van een bericht, wat de immersie van de speler versterkt.

Een technische uitdaging: Het balanceren van animaties en interactie zonder vertragingen voor de speler. Dit heb ik opgelost door een cooldownmechanisme te implementeren dat parallel draait aan de animaties.



Testen

Ik heb het systeem getest door middel van een test dialoog te maken. In deze testdialog worden de verschillende features van het systeem getest, bijvoorbeeld de branching, geluiden en teksteffecten.

Het enige probleem waar ik tijdens het testen tegen aan liep was dat de game crasht wanneer je een loop hebt in je dialoogstructuur en dit komt door de manier waarop het systeem geprogrammeerd is. Maar omdat we geen behoefte hebben aan een loop in de dialogs, heb ik hier verder niks mee gedaan.