You are on page 1of 40

Voorwoord

Met bijzondere dank aan Coded Illusions, Koert van der Veer en Thijs
Kruithof, presenteer ik hier mijn verslag over mijn afstudeerstage en -
opdracht bij Coded Illusions.

Dit verslag is vertrouwelijk.

Er staan verschillende beelden van het spel in dit verslag. Deze beelden
zijn van zeer vroege interne testversies en zijn niet representatief voor
het uiteindelijk product. Alles op de beelden is voor verandering vat-
baar, of zou helemaal niet in het spel terecht kunnen komen. Het level
uit de in-game en editor screenshots heb ik zelf gemaakt en komt niet
voor in het spel.

De screenshot op de voorkant is van het spel Assassin’s Creed van


Ubisoft.

Sijmen Mulder.

1
Inhoudsopgave
Voorwoord .............................................................................. 1 4–Code on the moves............................................................ 15

Inhoudsopgave ........................................................................ 2 Een nieuw systeem ............................................................ 16


Stapelen en invoer............................................................. 18
1–Coded Illusions.................................................................... 3 Met animaties.................................................................... 20
Eigen physics..................................................................... 23
Haven, de game .................................................................. 5 Ontwerpdoelen ................................................................. 25
2–Special Moves ..................................................................... 6 5–Aanpak en implementatie .................................................. 28
Moves in games................................................................... 6 Ladder ............................................................................... 29
Soorten moves ..................................................................... 7 Leap of Faith...................................................................... 32
3–Unreal Engine...................................................................... 9 Wallhop ............................................................................ 34
UnrealScript ...................................................................... 10 6–Afsluiting ........................................................................... 36
Animaties .......................................................................... 11 Wat er minder ging............................................................ 36
Physics .............................................................................. 13 Wat er goed ging ............................................................... 37
Pawns en controllers.......................................................... 14 Conclusie .......................................................................... 38
Summary in English ............................................................... 39

2
1–Coded Illusions
Voor mijn stage was ik op zoek naar een bedrijf in de game sector. Dit
primair vanwege de major die ik volg, Game Technology. Ik was al be-
kend met een aantal bedrijven.

Eén daarvan is Guerilla Games. Dit bedrijf werkt aan games voor Sony
spelcomputers, en is de maker van de bestseller Killzone. Het bedrijf is
na de release van Killzone opgegaan in Sony, maar werkt wel verder als
zelfstandig ontwikkelaar in Nederland. Binnen dit bedrijf kende ik ie-
mand, waarbij ik wel eens voorzichtig geïnformeerd heb naar een sta-
geplek. Helaas bleek dit niet mogelijk.

PlayLogic is een andere grote, bekende spelontwikkelaar. Behalve dat is


het ook nog een internationale uitgever van spellen. Het manifesteert
zich uitgebreid op gaming gerelateerde evenementen in Nederland, en
is zelfs betrokken bij specifieke game-gerelateerde opleidingen in Ne-
derland. De ontwikkelaar is echter gevestigd in Breda—terwijl ik in
Purmerend woon, en behalve dat, spraken de games mij niet aan.

In een nummer van het Ronduit Insite tijdschrift stond een artikel over
Coded Illusions. Het artikel ging wat meer over het algemene spelont-
wikkelingsproces, maar mijn interesse was gewekt. Het bedrijf maakt
net als Guerilla en PlayLogic zogenaamde AAA (triple-A) games. Dit
zijn spellen met budgetten die vaak in de miljoenen lopen, en meestal
worden uitgegeven en gepromoot door grote uitgevers. Wat Coded Illu-
sions uniek maakt is de christelijke achtergrond van het bedrijf. Het
hoopt door de games niet alleen een goede spelervaring, maar ook een
boodschap over te brengen op de gamers. Deze boodschap is echter
niet prominent aanwezig, het is een achterliggende gedachte. Het be-

3
drijf probeert de game die het in ontwikkeling heeft niet als ‘christelijke
game’ in de markt te zetten.

Coded Illusions is gevestigd in Rotterdam. Het heeft ongeveer 40 werk-


nemers, waaronder een aantal stagairs. Het heeft ongeveer dezelfde or-
ganisatiestructuur als de meeste spelontwikkelaars:

De afdeling Productie zorgt voor de facilitaire zaken, en is verantwoor-


delijk voor het eindproduct. Deze afdeling verdeelt de middelen binnen
het bedrijf, en zorgt voor een goed verloop van het ontwikkelproces als
geheel. Het verzorgt de zakelijke kant van het bedrijf.

Game Design bedenkt de spelelementen, het achtergrondverhaal, en


alles wat daar verder nog bij komt kijken. Alle ontwerpbeslissingen in
het spel worden uiteindelijk door deze afdeling genomen. Voorbeelden
van wat Game Design bedenkt zijn hoe wapens werken, de opbouw
van levels in het spel, en de look & feel die het spel ongeveer moet
hebben.

Art zorgt voor al het artistieke werk. Zij maken de karaktermodellen,


beschilderen de objecten uit het spel, maken levels, etc. Art maakt ook
concept art. Dit zijn tekeningen die een sfeerimpressie moeten geven
van het spel, zodat iedereen uit het bedrijf hetzelfde beeld heeft bij de
te maken content. ‘Content’ is de verzamelnaam voor alles uit het spel
wat zichtbaar en hoorbaar is.

Tenslotte is er nog de afdeling Coding, die de code van het spel schrijft.
Dit zijn de instructies voor de spelcomputer die ervoor zorgen dat het
spel werkt. Alle interacties, alle bewegingen, het tekenen van alle ob-
jecten, het afspelen van geluiden—uiteindelijk wordt dit allemaal door
de code gedaan. Dit is de afdeling waar ik werkte.

4
Zelda

Haven, de game
Coded Illusions werkt aan het spel met de werktitel Haven. Deze werk-
titel is aangekondigd op Games in Concert. Hierbij werden er ook wat
beelden uit de game en concept art getoond.

Het spel is een zogenaamde action-adventure. Dit genre is een breed


begrip, en betekent ongeveer zoveel als: ‘spellen waar teveel actie in zit
om het een adventure game te noemen, maar te weinig voor een action
game’. Spellen in dit genre hebben vaak een uitgebreid verhaal dat in
de loop van het spel uitgediept wordt, onder andere door gesprekken
met andere spelkarakters en tussenfilmpjes.

Voorbeelden van bekende action-adventures zijn Zelda, Prince of Per-


sia, en Metroid. Elk van deze spellen heeft een totaal andere stijl, maar
een gedeelde factor is het prominent aanwezig zijn van een verhaallijn
en actie-elementen. Deze drie spelreeksen hebben ook allemaal in be-
perkte mate platformelementen.

Platformgames zijn spellen waarbij de speler met het spelkarakter


springt, hangt, en rent over en op verschillende platforms in de wereld,
om uiteindelijk het eindpunt te halen. Mario is zo’n spel. De genoemde
action-adventures hebben dit allemaal ook een beetje: op bepaalde
plekken moet je je behendigheid gebruiken om over smalle randjes te
lopen, of om moeilijk toegankelijke plekken te bereiken.

Over Haven zelf kan nog niet zo veel gezegd worden omdat er officieel
nog weinig over bekend is. De site vermeldt dat het gaat om een spel
dat zich afspeelt in de toekomst, in een verhaal geïnspireerd op het bij-
belboek Openbaringen, dat gaat over het einde van de wereld.

Prince of Persia
5
Wall jump in Metroid

2–Special Moves
De opdracht van de stage is:

“Bedenk een systeem om special moves af te handelen, en maak hier-


van een prototype. Implementeer twee bestaande en twee nieuwe mo-
ves. Een systeem voor de AI om de moves te gebruiken is optioneel.”

In dit verslag schrijf ik hoe ik deze opdracht heb aangepakt. Eerst zal ik
beschrijven wat special moves inhouden. Voordat ik daarna overga tot
een behandeling van het ontwerp en de implementatie, is het belangrijk
om eerst wat achtergrondinformatie te geven over de gebruikte techno-
logie.

Moves in games
Een move is ruim te omschrijven als een ‘speciale handeling’. Special
move systemen zitten al in een groot aantal spellen, in de een meer dan
in de ander. Er zijn een aantal spellen die er bekend om staan. Een spel
dat om zijn rijke traditie van moves en daarbij horende gedetailleerde
animaties bekend staat is een spel dat ik al eerder heb genoemd, Prince
of Persia.

In platformers zijn special moves wat minder prominent aanwezig.


Vaak bestaan ze wel, maar zijn ze transparant in de game geïmplemen-
teerd—je merkt niet eens dat het ‘special moves’ zijn. Zo kun je in Ma-
rio springen met de A knop, maar je kunt je daarmee ook afzetten tegen
een muurtje. Dat is de wall jump. Het is een special move, maar wordt
niet zo duidelijk gepositioneerd als special move.

Wall hop in Prince of Persia


6
Pro Evolution Soccer Het idee van special moves is niet gebonden aan platformers en action
adventure games. Het is in meer of mindere mate in veel andere soorten
games terug te vinden waar een spelkarakter bestuurd wordt. Denk
hierbij aan vechtspellen als de klassieker Street Fighter, of zelfs voetbal-
spellen als Pro Evolution Soccer.

Een spel dat zeker niet mag ontbreken in een bespreking van games met
special moves is het spel Assassin’s Creed. Dit spel werd in de pers ge-
prezen om de bewegingsvrijheid en het fantastische klimsysteem. In het
spel is het mogelijk om alles vast te grijpen wat maar een beetje uit de
muur steekt, en daaraan op te klimmen. Ook kan de speler over daken
rennen, en er was één bijzondere move, de Leap of Faith: hierbij kan de
speler vanaf een aantal hoge punten naar beneden springen in het ver-
trouwen goed terecht te komen—heel toevallig altijd in hooibaal.

Soorten moves
Er zijn twee soorten moves te onderscheiden:

De eerste soort zal ik atomaire moves noemen. Dit zijn moves die geï-
nitieerd worden door de context of door een speler, en vervolgens tijde-
lijk de controle over het spelkarakter (bijna) volledig overnemen. Hier is
dus geen sprake van interactie na het initiëren van de move. Een paar
voorbeelden hiervan zijn slidings in voetbalspellen, het oppakken van
wapens in schietspellen en de Leap of Faith uit Assassin’s Creed.
Assassin’s Creed

7
Belangrijk om hier op te merken is dat hoewel de moves zelf over het
algemeen kort duren, ze wel het spelkarakter in een aparte state1 kun-
nen zetten. Denk hierbij aan een move waarbij het spelkarakter zich
tegen de muur aan drukt. De eigenlijke beweging waarbij dat gebeurt
zou de move zijn, en het resultaat een nieuwe state voor het spelkarak-
ter. Voor de speler lijkt het een grote doorlopende move. Ik zal hier la-
ter nog op terugkomen.

De tweede soort zijn de niet-atomaire moves. Dit zijn moves die over
een wat langere periode actief blijven, en waar de speler invloed op
heeft tijdens het uitvoeren. Voorbeelden hiervan zijn het (niet-
automatisch) beklimmen van een ladder, slingeren aan een touw, of
over muren rennen.

Bij de stage opdracht werd er een aantal voorbeelden gegeven van mo-
ves die het systeem moest ondersteunen. Dit waren onder andere rol-
len, ladder klimmen, en over een muurtje springen. Dit is een menge-
ling van atomaire moves en niet-atomaire moves. Bij het ontwerp zou ik
rekening moeten houden met deze verschillende soorten moves.

1
Een state is een bepaalde modus. Voorbeelden zijn ‘gewoon’, ‘hangend
aan een randje’, en ‘gebukt’.

8
3–Unreal Engine
Voordat ik uitleg hoe ik te werk ben gegaan met de moves, is het be-
langrijk om de technische achtergrond van het project te kennen. De
begrippen en technologieën die hier uitgelegd komen later in het ver-
slag terug.

Voor het ontwikkelen van Haven wordt er een engine gebruikt. Een
game engine is een basis waarop een spel gebouwd kan worden. Het
bevat een aantal onderdelen die vaak terugkomen in de code van spel-
len.

Dit is meteen het belangrijkste voordeel van de engine—doordat deze


onderdelen niet opnieuw geschreven hoeven te worden, kan er meteen
begonnen worden aan het programmeren van het spel. Een aantal za-
ken waar de engine voor zorgt zijn geavanceerde grafische effecten,
gameplay mechanics2, artificiële intelligentie, physics, ondersteuning
voor verschillende spelcomputers zoals de Xbox 360 en Playstation 3.
Ook belangrijk zijn de meegeleverde tools om de engine te kunnen ge-
bruiken, zoals een level editor.

Onderdelen die uniek zijn voor het spel moeten wel nog zelf geïmple-
menteerd worden. Special moves, het onderwerp van mijn afstudeerop-
dracht, is zo’n onderdeel.

2
Gameplay mechanics zijn de eigenlijke spelelementen
Gears of War (UE 3)
9
De engine die gebruikt wordt voor Haven is Unreal Engine 3 van Epic
Games. Dit is een van de meest bekende, en wordt in veel andere, be-
kende spellen gebruikt. Voorbeelden hiervan zijn Gears of War, Unreal
Tournament 3 en Mass Effect.

Unreal Engine 3 is primair geschreven in C++. Dit omdat het een taal is
die zich in de spelontwikkeling bewezen heeft, en het werkt op alle be-
langrijke platformen. Voor verschillende platformen (spel- en ‘gewone’
computers) heeft UE een aparte backend. Hierdoor werkt het op een
aantal verschillende spelcomputers, terwijl de verschillen hiertussen
zoveel mogelijk op de achtergrond worden gehouden voor de gebruiker
van de engine.

Dit hoofdstuk zal een aantal van de technische aspecten van de engine
behandelen, omdat deze nodig zijn om een beeld te geven van de op-
dracht. Later zal blijken hoe deze onderdelen allemaal samenkomen in
het special move systeem.

UnrealScript
Het eigenlijke spel wordt geschreven in UnrealScript. Dit wordt gecom-
pileerd en geïnterpreteerd door de engine. UnrealScript is een object-
georiënteerde taal met een C-achtige syntax. Het is specifiek bedoeld
voor gebruik in games. Vanwege het belang voor mijn project, en dus
dit verslag, zal ik hier wat dieper op in gaan.

Een klasse kan gedeeltelijk in UnrealScript, en voor het andere deel in


C++ geschreven zijn. Dit is mogelijk door het native attribuut dat op
een klasse gezet kan worden. Als een klasse native gedeclareerd is in
UnrealScript zullen er headers voor C++ gegenereerd worden. Het is
ook mogelijk om functies te declareren als een native functie. Dan staat
BioShock (UE3)
10
deze wel in UnrealScript gedeclareerd, maar wordt de eigenlijke im-
plementatie geschreven in C++.

Voordelen van UnrealScript zijn onder andere de korte compilatietijd,


en de mogelijkheden tot reflectie. Zo kan er bijvoorbeeld een lijst van
variabelen opgevraagd worden. Hier wordt gebruikt van gemaakt in de
editor.

UnrealScript ondersteunt ook zogenaamde states. Een klasse kan een


state definiëren. Wanneer deze state als actief wordt gezet, wordt er bij
BioShock (UE 3) een functieaanroep eerst gekeken of de state die functie definieert. Zo
ja, dan wordt die aangeroepen in plaats van het origineel. Dit is verge-
lijkbaar met subklassen, behalve dat er direct tussen states gewisseld
kan worden.

De scheidingslijn tussen wat in C++ en wat in UnrealScript geschreven


wordt is niet duidelijk getekend. In ieder geval worden onderdelen waar
snelheid cruciaal is, of er platformafhankelijkheid is, geschreven in
C++.

Zoals alle andere gameplay elementen, is UnrealScript de aangewezen


taal voor special moves. Een aantal onderdelen, zoals de animaties en
physics moesten wel in C++ geschreven worden om conventie- en
snelheidsredenen.

Animaties
Omdat de geloofwaardigheid van special moves sterk beïnvloed wordt
door de kwaliteit van de animaties, moet er goede ondersteuning voor
animaties in het movesysteem zitten.
Mijn testlevel in de editor

11
Een stuk van een AnimTree
De animator maakt voor spelkarakters een zogenaamde animatieboom.
Aan de hand van een hiërarchische lijst van keuzes wordt beslist welke
pose of animatie er wordt gebruikt. Dit is niet exclusief—een spelkarak-
ter kan bijvoorbeeld 80% lopen en 20% rollen. In dit geval berekent de
engine een tussenpose. Dit heet blending.

Dit kan door het gebruik van een skeletal animatiesysteem. Hoe het
precies werkt valt buiten het bestek van dit verslag, maar het komt er
ongeveer op neer dat een spelkarakter door het animatiesysteem gezien
Unreal editor met eigen testlevel wordt als een skelet waarvan de ‘botten’ apart geanimeerd kunnen
worden.

Deze hiërarchische lijst van beslissingen vormt de animatieboom. Voor


de animator wordt dit gevisualiseerd als aan elkaar verbonden blokken.
Een voorbeeld van zo’n blok is er bijvoorbeeld een die een animatie
selecteert op basis van positie of huidige actie op een ladder, bijvoor-
beeld ‘omhoogklimmend’, ‘omlaagklimmend’, of ‘stil’. Het idee van de
boom is dat er op meerdere niveaus gezocht kan worden. Zo kan er bij-
voorbeeld eerst gekeken worden of er gesprongen wordt, en zo ja, of
dat was met een wapen in de hand.

Deze animatieboom wordt de animation tree (of AnimTree) genoemd.


De aparte blokken heten animation nodes (of AnimNodes). Een losse
animatie of pose is hier een speciaal geval van en wordt een animation
sequence (of AnimSequence) genoemd.

In dit project zou één specifieke mogelijkheid van het animatie systeem
in het bijzonder van belang blijken. Dit is wat verplaatsingsanimatie ge-
noemd wordt. Om dit de begrijpen, is het nodig te weten hoe het skelet
van het spelkarakter is opgebouwd. Dit begint bij de root bone. De rest
Enkele AnimNodes van het skelet is hier vandaan opgebouwd

12
Bij verplaatsingsanimatie wordt dit bot van plaats verandert. Daarmee
verandert de positie van het spelkarakter in de wereld. Hierdoor is het
mogelijk om naast andere poses, ook een animatie met een beweging te
maken. Een voorbeeld hiervan zou bijvoorbeeld rollen kunnen zijn.
Daarbij maakt het karakter een koprol, en verplaatst daarbij een stuk
naar voren. Dit zou met verplaatsingsanimatie gedaan kunnen worden.

Physics
Het physics systeem zorgt voor een natuurgetrouw gedrag van objecten
in het spel. Het zorgt voor zaken als zwaartekracht en realistische bot-
singen. Voor de physics maakt UE3 gebruik van de AGEIA PhysX libra-
ry. Een library is een verzameling voorgeprogrammeerde functionaliteit.

Het physics subsysteem kan een aantal verschillende modi afhandelen,


zoals bijvoorbeeld ‘vallend’ of ‘lopend’. Ook wordt de modus aange-
past mocht de speler bijvoorbeeld landen na een sprong. Dan gaat de
modus over naar ‘lopend’.

Deze functionaliteit is belangrijk omdat dat het ook mogelijk maakt om


eigen modi toe te voegen. Dit kan bijvoorbeeld worden gebruikt voor
een spin die op een muur kan lopen. Dat krijgt dan een aparte physics
modus.

AEIGA PhysX in actie


13
Pawns en controllers
De twee belangrijkste klassen voor de opdracht zijn HPawn en HPlay-
erController3. HPawn is een subklasse van Pawn, wat een actor is. Een
actor is elk spelobject. Denk hierbij aan spelers, maar ook wapens, be-
wegende platformen, en triggers. Pawns zijn een speciaal type actors,
dit zijn namelijk spelkarakters.

HPlayerController is een subklasse van Controller—die overigens ook


een actor is. Een controller ‘bestuurt’ eigenlijk een pawn. Het geeft de
pawn opdrachten om dingen te doen. In het geval van de PlayerCon-
troller leest deze acties als het indrukken van knoppen op de gamepad
af, en geeft aan de hand hiervan opdrachten aan de pawn, zoals met de
methode DoJump van Pawn.

Tenslotte moet nog opgemerkt worden dat de pawn ook de verschillen-


de physics modi afhandelt. In pawn zit een methode die de huidige
physics modus bekijkt, en afhankelijk daarvan een methode aanroept. Is
de huidige physics modus bijvoorbeeld ‘lopend’, dan wordt de methode
physWalking() aangeroepen.

3
De H prefix wordt gebruikt door Coded Illusions voor hun eigen klassen.

14
4–Code on the moves
Er was in de engine geen apart systeem voor moves beschikbaar. Een
move, zoals het beklimmen van een ladder, bestond meestal uit ver-
schillende onderdelen. Vaak was er een state voor in de PlayerControl-
ler, en soms ook in Pawn. Verder werden er een aantal animatie nodes
geschreven speciaal voor die move, en niet zelden een aparte physics
modus, zodat de bewegingen apart afgehandeld konden worden.

Er was ook geen centraal systeem dat het wisselen tussen verschillende
moves kan afhandelen. Een move moest dus altijd zelf beslissen welke
andere move de volgende zou zijn. Er is wel één methode, maar deze is
niet helemaal toereikend. Hierbij wordt de nieuwe move ‘op de stapel
gelegd’, dat heet een stack. Wanneer deze afgelopen is wordt de move
er weer afgehaald, en wordt dat wat eronder lag weer actief. Zo wordt
dus altijd de laatste state actief, maar ook dat is niet altijd gewenst.

Wat is als grootste nadeel heb ondervonden van de originele methode


is dat een move versplinterd raakt, met stukjes van de move in verschil-
lende delen van het programma. Ter illustratie heb ik een aantal klassen
genomen, en daaruit een hele kleine selectie van variabelen en states
genomen. Hierbij heb ik alles wat betrekking heeft op het beklimmen
van de ladder aangegeven.

Zoals zichtbaar zal zijn uit het voorbeeld, is het grootste probleem hier
dat alles nogal verspreid is. Door de moves worden HPawn en HPlay-
erController snel onnodig groot. Een nieuwe move toevoegen kost veel
werk en tijd.

15
Een nieuw systeem
Ik redeneerde dat de meeste moves bestaan uit een opeenstapeling van
atomaire moves. Zo kan de ladder move opgedeeld worden in: om-
hoogklimmen, omlaagklimmen en omlaagglijden. De tussenstaat, waar
de speler aan de ladder hangt, zou door de overkoepelende move wor-
den afgehandeld.

Om dit verder uit te werken heb ik verschillende moves, die mijn be-
drijfsbegeleider me genoemd heeft, ontleed in zo klein mogelijke een-
heden. De bedoeling was om een boomstructuur te krijgen. In zo’n
structuur staat er één move bovenaan, en bestaat deze weer uit verschil-
lende moves. Deze worden de kinderen genoemd. In dit geval staat er
aan de top bijvoorbeeld LadderMove, met als kinderen LadderClim-
bUpMove en LadderClimbDownMove. Alle kinderen aan het einde zijn
de atomaire moves.

Met een systeem dat op deze manier opgebouwd zou zijn zou het mo-
gelijk zijn om met een aantal basisbouwstenen op een simpele manier
een groot aantal verschillende moves te kunnen maken. Om deze ba-
sisblokken te identificeren heb ik atomaire moves die ik gevonden had
vergeleken op functionaliteit. Twee soorten kwam ik daarbij veel tegen.

De eerste was een move die slechts bestaat uit een animatie, eventueel
met beweging. Omhoogklimmen op een ladder is hiervan een voor-
beeld. Dit is waar de eerder genoemde verplaatsingsanimatie handig
bleek te zij. Door hiervan gebruik te maken zou het mogelijk worden
om dit soort moves heel makkelijk te implementeren.

Een andere steeds terugkerende atomaire move was die van een ver-
plaatsing van de pawn over korte afstand. Een voorbeeld hiervan is het

16
‘goed voor een ladder gaan staan’ voor de ladder move. Het toeval
kwam mij te hulp, want juist toen ik aan het ontwerpen was werd de
generic mount feature afgerond. Hiermee is het mogelijk om de pawn
opdracht te geven om zich volautomatisch naar een opgegeven positie
te begeven. Animatie wordt daarbij meteen geregeld.

Het diagram hiernaast was een van de eerste die ik heb gemaakt. Achter
de naam ‘blocking anim’ zit nog wat meer dan wat ik tot nu toe heb
uitgelegd. Mijn idee was namelijk dat het standaardgedrag van een
MultiMove—de superklasse van nodes in die tree die meerdere kinde-
ren hebben—in principe sequentieel uitgevoerd zou worden. Dan zou
het programmeerwerk voor een move zich alleen hoeven te beperken
tot het instellen van de juiste parameters, en de overkoepelende con-
troller. Dat zou op het voorbeeld dus slechts ‘ladder’ zijn.

Nadeel van dit systeem is dat het veel van de (spel)computer zou eisen.
Er zouden veel objecten nodig zijn, voor elk onderdeel van de move
één. Dit zou meer geheugen gebruiken dan acceptabel was. Daarom
heb ik dit idee genomen, maar op een andere manier geïmplementeerd.
In de Move klasse heb ik een enkele functie, StartBlockingAnim ge-
maakt. Deze start zo’n blokkerende animatie, eventueel met beweging.
Wanneer de animatie afgelopen is wordt OnBlockingAnimEnd() aange-
roepen. In deze functie kijkt de move wat er als volgende gedaan moet
worden.

Dit systeem is iets minder mooi dan een bijna volautomatische boom
van move onderdelen, maar wel veel efficiënter. Bovendien maakt het
het overzetten van bestaande moves naar het nieuwe systeem veel
makkelijker—eerst kan de code uit de states van de player controller en
pawn worden overgezet naar een eigen move, en wanneer dit werkt

17
kunnen onderdelen stuk voor stuk vervangen worden door blokkerende
animaties. Dit is hoe ik de ladder heb geïmplementeerd.

De move heeft twee Tick functies: TickPawn() en TickController(). Zoals


de namen impliceren, worden deze functies respectievelijk vanuit Pawn
en een Controller aangeroepen op de huidige move. In principe is het
zo dat in TickController() de eigenlijke logica zit met betrekking tot wat
er gedaan moet worden. TickPawn() voert die dan uit.

Stapelen en invoer
Nu zijn de moves wel omschreven, maar er is nog steeds geen manier
om te regelen welke move wanneer actief wordt.

Sommige moves worden actief bij een hele specifieke actie van de ge-
bruiker. Hierbij wordt er op een knop gedrukt, en de move wordt actief.
Alhoewel, dit is wat voorbarig—het zou natuurlijk kunnen zijn dat je
een gegeven move niet kan doen vanaf een bepaalde status. Een voor-
beeld is rollen: het spelkarakter kan niet rollen terwijl deze aan het val-
len is. Een ander probleem is dat sommige acties leiden tot verschillen-
de resultaten afhankelijk van wat er op het moment gebeurt. Springen
van een ladder werkt bijvoorbeeld anders dan het springen vanaf de
vloer.

De vraag is hier dus hoe er gekozen wordt welke specifieke actie


(‘springen vanaf ladder’) er wordt uitgevoerd bij een opdracht voor een
abstracte actie (‘spring’). Ook kan opgemerkt worden dat er voor de
speler geen onderscheid is tussen het doen van een actie zoals sprin-
gen, waar de move op reageert, en het direct starten van een nieuwe
move, bijvoorbeeld rollen. Daarom was ik er van overtuigd dat er ook
in de code weinig onderscheid tussen gemaakt zou moeten worden.

18
In het ontwerp dat ik hiervoor maakte voegde ik een methode toe aan
HMove, HandleAction(). Deze neemt als enkel argument een naam, en
geeft ‘true’ terug indien de actie afgehandeld is, of ‘false’ indien niet.

In HPlayerController kwam er een extra methode die de invoer afvangt


en doorgeeft aan de move. Dit werkt zo: in een .ini bestand staat aan-
gegeven welke methode van PlayerController wordt aangeroepen,
eventueel met een argument. In dit geval werd dus die nieuwe methode
aangeroepen. Deze geeft vervolgens de opdracht door aan de huidige
move, die kijkt of die er wat mee kan. Zo niet, dan kijkt de PlayerCon-
troller of het toevallig de naam van een move is, en maakt in dat geval
zo mogelijk die move actief.

Moves kunnen zelf aangeven of ze actief willen worden. Eerst had ik


daar een methode CanBecomeActive() voor. Als de move dan geacti-
veerd werd, werd Activate() aangeroepen. Het bleek echter dat er veel
dubbele code zat in die twee functies, daarom heb ik de eerste iets an-
ders genoemd: PrepareForActivation(). Dat impliceert dat dit voorberei-
dende werk niet meer in Activate() gedaan hoeft te worden.

Er is echter nog een andere manier waarop een move actief kan wor-
den, namelijk passief. Een voorbeeld is als de speler valt en op de grond
terecht komt. Tijdens het vallen zou de ‘val’ move actief kunnen zijn.
Bij het landen zou dan automatisch de ‘loop’ move actief worden. Het
systeem dat ik hiervoor heb gemaakt was een van de meest complexe in
het special move systeem, maar dat had niet veel simpeler gekund zon-
der de functionaliteit ervan aan te tasten.

In de code is er een prioriteitsvolgorde van moves gedefinieerd. Een


move kan vervolgens aangeven of hij de actieve move wenst te worden,
of anders kan worden—dit onderscheid is belangrijk.

19
Elk frame wordt er een check gedaan of een nieuwe move actief wenst
te worden. Als er een move met een hogere prioriteit actief wenst te
worden, wordt deze dat. Een voorbeeld hiervan is de ladder move. Als
de ladder move ziet dat het spelkarakter tegen een ladder aanloopt, zal
deze aangeven te wensen om de nieuwe move te worden.

Zo niet, dan wordt er geen andere move geactiveerd. Het is ook moge-
lijk om een geforceerde overgang naar een nieuwe move te doen. In dit
geval wordt er net zoals hiervoor eerst gekeken of er een move is die
wenst geactiveerd te worden. Als geen van de moves actief wenst te
worden, wordt de lijst achterstevoren teruggelopen om te kijken of er
één is die activatie niet wenst, maar wel accepteert. De reden dat er
dan achterstevoren gezocht wordt, is omdat de dan logische moves zo-
als ‘lopen’ achteraan in de prioriteitenlijst staan.

Een geforceerde overgang naar een nieuwe move wordt bijvoorbeeld


gebruikt als een move afgelopen is, en er een nieuwe geactiveerd moet
worden.

Met animaties
Juist bij een special move systeem is het belangrijk dat er goede anima-
ties gemaakt kunnen worden. Zoals eerder genoemd zijn er de blokke-
rende animaties, maar dat alleen is niet genoeg: ook wanneer er geen
blokkerende animatie wordt afgespeeld moet het spelkarakter een be-
paalde pose of animatie hebben. Daarom moet de animator ook infor-
matie uit het systeem kunnen halen over wat er precies gaande is. Een
heel belangrijk onderwerp hier zijn de overgangen tussen moves.

Bij het bespreken van mijn opdracht werd er een scenario voorgesteld,
waarin er vloeiend gewisseld kon worden tussen verschillende moves.

20
Het voorbeeld was het spelkarakter dat aan een randje hangt, en opzij
klimt langs de muur in de richting van een ladder. De onderkant van de
ladder is op dezelfde hoogte als het randje. Daar aangekomen, klimt het
spelkarakter met een vloeiende beweging op naar de ladder.

Dit voorbeeld illustreert een veel voorkomende soort overgang tussen


moves, van de een naar de ander. Eén van de doelen van het animatie-
systeem was het mogelijk maken om dit soort overgangen goed te kun-
nen animeren.

Ik heb verschillende animatiesystemen overwogen. Bij het eerste ont-


werp ging ik (te veel) uit van een directe overzetting van de eerder ge-
noemde ‘move tree’. Het idee was dat er een AnimNode zou komen,
waarbij je kon opgeven voor welke submove deze van toepassing was.
Daar kon je vervolgens een lijstje van animaties opgeven die die move
zou kunnen doen. Dat werden dan de outlets van die node.

Er waren drie problemen met deze methode: ten eerste zou de animator
voor elke submove een node moeten toevoegen in de animatieboom.
Dit zou veel te veel zijn, en problemen geven met de workflow en het
systeem trager maken. Het tweede probleem is dat de animator bekend
zou moeten zijn met de precieze opbouw van de hele animatieboom.
Tenslotte forceert dit systeem de hiërarchie van de moves op de anima-
tieboom.

Alle drie de problemen hebben een gedeelde oorzaak. De node is be-


Het eerste animatie ontwerp. perkt tot één move. Het tweede ontwerp ontstond uit het verwijderen
Moves boven, voorbeeld AnimNodes onder. van die beperking, en er zo een ontwerp van te maken waarbij de ani-
(De animator kan de move kiezen in de no- mator outlets kan aanmaken voor elke animatie, uit welke move dan
de, en voegt de ‘outlets’ toe.) ook. Eventueel zou de animator er voor kunnen kiezen meerdere nodes

21
aan te maken met elk een verschillende set animaties er in, om het zo
beter in zijn AnimTree te passen.

Dit was een goed idee, maar er was een beperking—ik had nog geen
rekening gehouden met de overgangen tussen de moves. Mijn eerste
ingeving was om de animator ook overgangsscenario’s in te laten voe-
ren in dezelfde node. Dit bleek niet zo’n goed idee. Het paste niet goed
in de denkwijze van de animator, en de nodes zouden wel heel erg
groot worden door een exponentiële toename van het aantal mogelijke
combinaties tegenover het aantal moves. Het was duidelijk dat de over-
gangen niet in die node konden, er moest dus een andere komen.

De volgende stap was het bedenken hoe deze AnimNode eruit zou
gaan zien. Het meest voor de hand lag een node waarbij de animator
outlets zou kunnen aanmaken voor de verschillende mogelijke over-
gangen. Maar dit zou het probleem alleen maar uitstellen, want ook de
grootte of het aantal van deze nodes zou explosief toenemen met het
toevoegen van nodes.
Het tweede animatie ontwerp.
(Hierbij kan de animator alle move ani- Als oplossing bedacht ik dat deze nieuwe node uit zou kunnen gaan
maties en overgangen benoemen vanuit van de huidige move. In deze node geeft de animator dan aan of hij wil
één node.) kijken naar het in- of uitblenden4 naar of van een andere move. Daarna
kan hij outlets maken voor de verschillende moves. Zo kan de animator
zeggen: ‘als er nu overgegaan wordt naar een andere move, kijk dan
welke, en selecteer aan de hand daarvan een animatie’.

4
Blenden betekent hier ‘geleidelijk overgaan naar’

22
Bij dit ontwerp zal misschien opvallen dat er hier gekozen kan worden
voor het inblenden vanaf één move, en het uitblenden naar een andere
toe. Bij de eerdere ontwerpen ging ik er vanuit dat eerst move A actief
is, daarna een overgang AB, en daarna B. Die gedachtegang vormde de
basis van het probleem dat hier ontstaan was, omdat het aantal combi-
naties tussen moves groot zou zijn. Met dit aparte in- en uitblendsys-
teem ziet het er anders uit: eerst is move A actief, die gaat dan naar uit-
blendmodus, dan wordt move B actief in inblendmodus, en dan gaat de
inblendmodus uit, zodat move B ‘gewoon’ actief is. Tijdens het blenden
is voor de animator het gegeven beschikbaar naar of van welke move er
geblend wordt.

Het is ook nog belangrijk om de vraag te beantwoorden hoe de nodes


aan de animatienamen komen. Dit gebeurt op de volgende manier:
eerst wordt er gekeken of er een blokkerende animatie actief is. Als dat
het geval wordt deze gebruikt. Wanneer er geen blokkerende animatie
actief is, wordt de ‘normale’ animatienaam gebruikt. Deze kan vrij wor-
den ingesteld door de move.

Eigen physics
Het uiteindelijke animatie ontwerp.
Het laatste element van de moves zijn de physics. Dit lijkt een ingewik-
(Het opgeven van overgangen gebeurt nu
keld en imponerend onderwerp, en dat is niet onterecht. Physics engi-
in een apart node.)
nes zijn lastig om goed te maken, en vereisen veel kennis, vooral in de
natuur- en wiskunde.

Gelukkig was het bij de moves niet zo’n heel groot probleem. In het
Unreal Engine hoofdstuk heb ik al uitgelegd hoe het Unreal Engine phy-
sics systeem ongeveer werkt. Het harde werk wordt gedaan door de
achterliggende physics engine, en de moves hoeven in de meeste geval-
len niets meer te doen dan deze aansturen.

23
Moves die eigen physics nodig hadden in het oude systeem, hadden
een eigen physics modus. Daarbij hoorde een physNaamVanModus()
functie die physics dan aanstuurt. Dit systeem was onhandig, omdat het
zorgde voor nog meer verspreiding van move code. Ik zou een manier
moeten bedenken om de physics code van de moves naar de move
klasse zelf te verplaatsen, zonder het oude systeem hiervoor om te
bouwen.

Die laatste beperking was opgelegd door de aard van de opdracht: de


engine moest zoveel mogelijk onveranderd blijven, zodat er geen pro-
blemen met engine-updates zouden ontstaan. Dit physics systeem is
onderdeel van de engine, en daarom moet de physics-implementatie
voor de moves er naadloos op aansluiten.

Gelukkig vond ik snel een oplossing. In de Pawn klasse bestaat er een


functie handlePhysics(). Deze kijkt welke physics mode actief is, en
roept de desbetreffende functie aan. Ik voegde een functie HandlePhy-
sics() toe aan de move klasse. Deze geeft een booleanse waarde terug,
die aangeeft of de move hem afhandelt. Standaard gebeurt dat niet, en
wordt er false teruggegeven. Voordat handlePhysics() naar de physics
state gaat kijken, roept deze eerst HandlePhysics() aan van de huidige
move. Als deze hem afhandelt, wordt de physics state genegeerd. An-
ders wordt deze zoals gebruikelijk afgehandeld.

Op deze manier hebben moves de optie om de physics af te handelen,


zonder dat er extra code geschreven hoeft te worden mocht de move
dat niet wensen, of dat er code ergens anders in het spel geplaatst moet
worden. Overigens is de physics code in C++ geschreven, vanwege
snelheidsredenen.

24
Ontwerpdoelen
Voordat ik overga naar de implementatie van het ontwerp in het vol-
gende hoofdstuk, wil ik nog iets vertellen over de verschillende ont-
werpdoelen die ik voor ogen had tijdens de implementatie. Deze heb ik
niet vanaf het begin gehanteerd—ik ondervond de noodzaak hiervoor
tijdens de ontwikkeling zelf.

Ten eerste was het mijn bedoeling alles zo simpel mogelijk te doen. Dit
was vooral in het begin bij het ontwerpen van de code lastig, omdat het
moeilijk is om goed gebruik te maken van de mogelijkheden die al ge-
boden worden bij een beperkt aan inzicht in het bestaande ontwerp en
bestaande mogelijkheden. Door dit gebrek aan inzicht dreigt al snel het
gevaar om veel te uitgebreide of ingewikkelde ontwerpen te maken.

Een manier die hielp bij het ‘keep it simple’ doel is door mijzelf voor te
houden dat wat ik toevoegde, niet iets heel bijzonders, of groots was.
Daardoor had ik ook niet meer de neiging om iets te maken dat inge-
wikkelder of complexer was dan andere onderdelen.

Dit doel werd mij duidelijk bij het werken aan de move tree, zoals ik
eerder in dit hoofdstuk beschreef. Eerst had ik een complex systeem,
met een hele boom van moves. Uiteindelijk is de essentie teruggevoerd
tot één enkele functieaanroep in de HMove klasse. Op dat moment
werd mij duidelijk hoe belangrijk het is om het simpel te houden™.

Het tweede ontwerpidee waar ik mij op wilde concentreren was het


gebruik van push in plaats van poll. Het verschil hiertussen zal ik uit-
leggen met een voorbeeld:

25
De speler drukt op een knop om te springen. Het spel registreert deze
invoer, en begrijpt dat de speler wil springen. Daarom zet het spel de
flag bWantsToJump. Een flag geeft aan of er iets aan de hand is, in dit
geval dus of de speler wil springen.

Tijdens het bijwerken van het spelkarakter, wat elk beeldje opnieuw
gebeurt, kan een stukje ladder-gerelateerde code zien dat bWantsTo-
Jump aan staat, terwijl dat op dat moment niet kan, bijvoorbeeld omdat
de speler nog bezig is met het omlaagglijden langs de ladder. Daarom
beslist de code om bWantsToJump weer uit te zetten. Op die manier
zitten er nog veel meer checks in, door de code verspreid. Aan het eind
van de rit wordt gekeken of bWantsToJump nog aan staat, en zo ja, dan
wordt er ‘echt’ gesprongen.

Het nadeel hiervan zal duidelijk zijn: het wordt zo ontzettend moeilijk
om te achterhalen wat er in bepaalde gevallen gebeurt, of waarom iets
gebeurt zoals het gebeurt, omdat de code die daar invloed op heeft
door verschillende functies in het spel verspreid kan zijn.

Om die reden wilde ik met zo weinig mogelijk statusvariabelen werken,


zoals die flag. In plaats daarvan zou er in het voorbeeld van springen
één functie zijn die alle checks doet. Die functie zou dan al op het
moment van indrukken van de ‘spring’ knop aangeroepen worden—
push dus. Op die manier zijn er geen statusvariabelen meer nodig om
bedoelingen tussen verschillende onderdelen van het spel te communi-
ceren.

26
VANWEGEHETGEBREKAA Op sommige punten was het echter onvermijdelijk om wat statusvaria-
NCONTENTOPDERECHTE belen voor communicatie te hebben. Dit was dan vooral het geval bij
zaken waar eerst een functie uitgevoerd wordt die de opdracht geeft, en

RKANTVANDEZEPAGINA pas later, onafhankelijk, een andere functie de taak uitvoert indien no-
dig.

WASHETNIETMOGELIJK
DEZEKANTTEVOORZIEN
VANEENZINNIGGRAPPI
GOFZELFSNUTTELOZEA
FBEELDINGINPLAATSD
AARVANHEBIKERVOORG
EKOZENOMDEZEKANTTE
VULLENMETDEZETEKST
UELEDECORATIEOVERI
GENSNOGHARTELIJKBE
DANKTVOORHETLEZENV
ANMIJNSTAGEVERSLAG 27
5–Aanpak en implementatie
In het voorgaande hoofdstuk heb ik vooral beschreven hoe ik het sys-
teem ontworpen heb. Dit was ook het primaire doel van de opdracht.
Daarnaast was er nog vereist dat ik een werkend prototype zou maken,
met daarin voorbeeldimplementaties van bestaande moves, en twee
nieuwe. In dit hoofdstuk leg ik uit hoe ik de verschillende moves heb
geïmplementeerd voor het prototype.

Ik heb het project iteratief aangepakt. Ik maakte steeds een ontwerpje,


en implementeerde het dan als een concept om te kijken of het goed
werkte, en wat er beter kon. Bij sommige ontwerpen bleek al op voor-
hand dat ze niet afdoende waren, bijvoorbeeld uit overleg met de be-
drijfsbegeleider. In die gevallen ben ik meteen terug gegaan naar de
ontwerptafel.

Tijdens het implementeren ben ik steeds uitgegaan van de ladder move.


Deze move had alles in zich wat het systeem nodig zou hebben: stan-
daard bouwblokken in de vorm van blokkerende animaties voor het
klimmen, eigen stukjes logic voor bijvoorbeeld het naar beneden glij-
den langs een ladder en de besturing, en eigen physics.

Het was oorspronkelijk de bedoeling dat ik twee bestaande moves zou


implementeren. Enkele moves die als tweede showcase in overweging
zijn genomen waren rollen, en het klimmen over een randje.

Rollen leek een goed voorbeeld van hoe een animatie-met-verplaatsing


move zou werken. Dit bleek alleen wat ingewikkelder dan eerst ge-
dacht, omdat er nog extra dingen bij zaten zoals padcorrectie als er
schuin tegen een muur aan gerold wordt. Het alternatief was om de be-

28
staande code om te bouwen tot een move. Omdat alle aspecten die
hierbij van belang waren al waren gedemonstreerd met de ladder move,
was het een beter idee om de tijd de besteden aan de implementatie
van nieuwe moves. Hetzelfde gold voor het klimmen over een randje,
deze lijkt wat opbouw betreft heel veel op de ladder.

De twee nieuwe moves waren snel bedacht. De eerste was vergelijk-


baar met de Leap of Faith uit Assassin’s Creed, welke genoemd is in het
hoofdstuk ‘Special moves’. Hierbij springt het spelkarakter van grote
hoogte naar beneden, om daar precies goed uit te komen.

Mijn bedrijfsbegeleider stelde een tweede nieuwe move voor, de wall-


hop. Deze is onder andere bekend uit Gears of War en Call of Duty.
Hierbij kan de speler snel over een muurtje heen springen.

Ladder
De ladder move bestaat uit het beklimmen en afdalen van een ladder.
Deze ladder wordt door de leveldesigner in het level neergezet. Wan-
neer de speler tegen de ladder aan loopt, zal het spelkarakter de ladder
een stukje opklimmen. Daarna is er vrije controle op de ladder—de
speler kan omhoog en omlaag klimmen, omlaag glijden, en van de lad-
der af springen.

Er is een duidelijk verschil tussen de technische ladder, en dat wat het


speler ziet in het spel. Dit zijn twee verschillende objecten. Wat de spe-
ler ziet in de level is niets anders dan zichtbare geometrie, het heeft
geen effect op de werking. De technische ladder is een apart object dat
de designer neerzet om aan te geven dat daar een ladder beklommen
kan worden. Dit is zo gedaan zodat de designer niet beperkt is tot een

Centraal Station Rotterdam


29
kleine set bruikbare levels, maar alle levelgeometrie kan gebruiken als
ladder.

Het beklimmen van de ladder wordt voor een groot deel afgehandeld
met animaties. Het omhoog- en het omlaagklimmen en het op- en af-
stappen zijn blokkerende animaties met beweging. Ze worden door de
ladder move gestart, ze zorgen zelf voor de verplaatsing van het karak-
ter, en wanneer ze klaar zijn neemt de ladder move het weer over.

Het naar beneden glijden wordt wel helemaal door de code afgehan-
deld. Dit wordt overigens ook gebruikt bij het vastgrijpen van een lad-
der vanuit een val of sprong. De kans is groot dat de het spelkarakter
daar niet precies met zijn handen en voeten op een spaak uitkomt.
Daarom wordt er een stukje omlaag gegleden zodat de handen en voe-
ten precies goed uitlijnen met de spaken, wat er realistisch uitziet.

De physics van het ladder gebeuren wordt door de move zelf afgehan-
deld. Dit omdat het spelkarakter wanneer het zich op de ladder bevind
bijvoorbeeld niet (zichtbaar) onderhevig is aan zwaartekracht.

Bij de Unreal Engine werd er een werkende ladder move meegeleverd,


maar dit werkte niet zoals de bedoeling was in Haven. Om die reden is
deze move herschreven, speciaal voor het spel, met als resultaat het
laddersysteem dat ik hierboven beschreven heb.

Het uitwerken van de ladder naar een aparte move was een interessante
onderneming. Omdat de ladder zoveel aspecten omvat, was de code
door een aantal klassen verspreid. Zo was er een state in de PlayerCon-
troller, een gigantische animatie node in de AnimTree, en een HLadder
klasse voor de ladder zelf.

30
Ik ben begonnen door de state code uit PlayerController over te zetten
naar een aparte move. In tegenstelling tot wat ik verwachtte, heb ik hier
veel tijd aan moeten besteden. Er waren veel kleine probleempjes met
dingen die zich vanuit een aparte move klasse net iets anders gedroe-
gen, door net iets ander timing bijvoorbeeld.

Ook moest ik de logica herschrijven die regelt wanneer de ladder move


actief wordt, zodat deze gebruik zou maken van het prioriteitensysteem
zoals ik in een eerder hoofdstuk beschreef.

Nadat de foutjes eruit waren die bij het overzetten er in geslopen wa-
ren, werkte het goed. Maar eigenlijk was het nog niet een echte move,
maar gewoon de oude implementatie in een aparte klasse. Omdat het
een directe overzetting was van de oude code, gebruikte het ook nog
niet de animatiemogelijkheden en de physics van het special move sys-
teem.

Deze heb ik gaandeweg toegevoegd. Eerst ben ik alle animaties over


gaan zetten om gebruik te maken van het blocking anim systeem. Om-
dat al deze functionaliteit eerst handmatig was geïmplementeerd in de
ladder move, en al deze stukken vervangen werden door slechts enkele
functiecalls naar StartBlockingAnim, werd de code een stuk compacter.

Terwijl ik bezig was heb ik de code nog een stuk opgeschoond door
stukjes code apart te zetten in eigen functies, of op andere manieren
wat heen en weer te verplaatsen.

Ook dit overzetten naar de animaties kostte veel werk. Er waren veel
uitzonderingssituaties, zeker met betrekking tot het naar beneden glij-
den langs de ladder, hiervoor werden blokkerende animaties afgebro-

31
ken. Voor de communicatie met de physics moesten er ook nog veel
variabelen in de Pawn blijven staan.

Op dat moment werkte de ladder move goed, met één uitzondering: de


physics stonden nog apart. Deze heb ik zonder problemen overgezet
naar de move. Daarbij kon ik ook nog bijna alle overgebleven ladder
variabelen verplaatsen naar de ladder move, zodat de eerste compleet
geïmplementeerde move een feit was.

Leap of Faith
De Leap of Faith is, zoals in het ‘Special moves’ hoofdstuk beschreven,
gebaseerd op de gelijknamige move uit Assassin’s Creed waarbij de
speler vanaf een grote hoogte naar beneden kan springen, en met per-
fecte precisie goed neerkomt.

Hoe de move er ongeveer uit zou zien voor de leveldesigner stond ei-
genlijk al vanaf het begin vast: er zou een plek in de level komen waar-
vandaan er gesprongen zou kunnen worden, en een plek waar deze
sprong dan zou kunnen eindigen.

Er hoefde geen apart object te komen om de eindplek aan te geven, er


zijn al een aantal andere objecten beschikbaar die puur dienen om een
punt in de level te markeren.

Voor het startpunt heb ik een nieuw object gemaakt. In de leveleditor


wordt deze weergeven met een pijltje, dat symbool staat voor de
sprong. Bij het object kan worden aangegeven wat het doelpuntobject
is. Dit is alles wat de leveldesigner hoeft te doen.

32
Als extraatje heb ik ondersteuning voor meerdere startpunten rond de-
zelfde plek ingebouwd. Wanneer de speler in het bereik van meerdere
startpunten staat, wordt het punt gekozen waar het spelkarakter het
De Leap of Faith in actie. meeste naartoe gericht is (de kleinste hoek). Dit voegt wat extra realis-
(In beweging is het fantastisch mooi, echt ) me toe omdat de speler vanaf één punt meerdere kanten op kan sprin-
gen.

Voor de eigenlijke beweging heb ik verschillende ideeën overwogen. Ik


was eerst van plan om de hele sprong handmatig te anime-
ren/interpoleren met eigen physics code. Dit bleek echter nogal veel
werk te zijn, vanwege bijvoorbeeld de collision detection. Om dit te
voorkomen, heb ik ervoor gekozen om het spelkarakter een impuls te
geven en hem vervolgens over te geven aan de zwaartekracht. Het
grootste vraagstuk bij deze move was dus eigenlijk hoe de impuls het
beste berekend kon worden.

Er zijn bij het geven van de impuls twee variabelen: de horizontale en


verticale impulssnelheid. Het is verreweg het meest eenvoudig om één
daarvan constant te houden, zodat alleen de ander berekend hoeft te
worden.

In eerste instantie wilde ik de horizontale snelheid constant maken. Zo


hoefde ik alleen de opwaartse snelheid te berekenen, wat tamelijk een-
voudig was omdat dit een lineaire vergelijking is. Het bleek echter dat
voor mikpunten die iets verder lagen het spelkarakter onrealistisch hoog
moest springen, wat stoorde bij het spelen van het spel. Een ander pro-
bleem was dat bij dichtbij gelegen punten, de sprong vaak niet hoog
genoeg was om tot over het randje te komen.

Als alternatief moest ik uitgaan van een constante opwaartse snelheid,


Leap punten in de editor. iets harder dan de normale sprong, zodat het er ook een beetje bijzon-
(Vanaf deze plek kan er in drie verschillende
richtingen gesprongen worden.)
33
der uit zou zien. De vergelijking hier was een stuk lastiger, omdat er
zaken als zwaartekracht bij de berekening kwamen. Met de bekende
ABC formule bleek het gelukkig goed op te lossen.

Voor de animatie heb ik 3 verschillende statussen gemaakt: voor het


opspringen, het toppunt, en het vallen. Als test heb ik deze in de Anim-
Tree aangesloten op respectievelijk springen, rollen, en vallen. De ti-
ming van de rol in het toppunt laat het lijken alsof het spelkarakter een
salto maakt, een leuk effect in een prototype.

Wallhop
De wallhop is de naam die ik heb gegeven aan de move waarbij de
speler over een muurtje kan springen/klimmen, in één snelle vloeiende
animatie. Deze move zou het voorbeeld moeten worden van een move
die snel te implementeren is met het special move systeem.

De move werkt ongeveer zo: de speler loopt naar een muurtje toe dat er
op een bepaalde manier uitziet alsof er snel overheen te springen of
klimmen is. Dit is iets dat de leveldesigner bedenkt. Wanneer het spel-
karakter ervoor staat, drukt de speler op de spring knop om snel over
het muurtje te springen of klimmen.

Hier gebruik ik springen en klimmen door elkaar, omdat dat alleen een
kwestie van animatie is. De move zelf is namelijk niet veel meer dan
één blokkerende animatie.

Technisch werkt het zo: er is een object beschikbaar in de leveleditor,


een HWallHopPoint. De leveldesigner plaatst deze bij een muurtje, en
zorgt dat hij precies goed uitgelijnd staat met het muurtje. Het object
tekent een aantal hulplijnen om dit te vereenvoudigen.Wanneer de spe-
Een wallhop in de editor.
(Daarachter zichtbaar zijn twee leap punten 34
op een lift.)
ler in het spel op springen drukt vanuit de ‘loop’ move, wordt er geke-
ken of het spelkarakter zich op een HWallHopPoint bevindt, en of het
spelkarakter in de goede richting staat. Als dat zo is, wordt de wall hop
gestart.

Omdat er nog geen animatie beschikbaar was voor deze move, is het in
het prototype geen visueel spektakel. Om het ontbreken van een anima-
tie met verplaatsing af te vangen, heeft StartBlockingAnim() een handige
mogelijkheid die hier gebruikt wordt: het opgeven van een time-out en
handmatige verplaatsing. Als de time-out verstrijkt en er is geen anima-
tie gestart in de AnimTree, wordt de verplaatsing handmatig toegepast.
Dit noemde we ‘ploppen’.

Met de wallhop heb ik laten zien dat het met special move in ieder ge-
val tot op zekere hoogte mogelijk is om snel nieuwe moves te maken.
In totaal heeft de wallhop move me ongeveer twee dagen gekost, vanaf
overleg met mijn bedrijfsbegeleider tot een werkende versie.

35
6–Afsluiting
Het hele project is voor mij een leerzame ervaring geweest. Ik heb met
succes een special move systeem ontworpen en geïmplementeerd. Ik
heb met de animator overlegd hoe hij het liefst het special move sys-
teem zou zien, en een systeem gemaakt waar ook hij zich goed in kan
vinden.

Behalve het special move project heb ik ook in de vorm van bugfixes en
een aantal features bijgedragen aan het spel.

Als laatste onderdeel in dit document kijk ik nog terug naar de stage, en
vooruit, naar de toekomst en potentie van het special move systeem, en
zelfs special move systemen in het algemeen.

Wat er minder ging


Over het ontwerp van het systeem ben ik in het algemeen tevreden. Het
doet wat het ongeveer moet doen. Wat jammer is, is dat ik het nog niet
heb kunnen inzetten in wat meer realistische scenario’s: de implemen-
taties van de special moves zoals in het prototype gaat uit van de best
case. Er is met veel dingen nog geen rekening gehouden, zoals hoe er
gereageerd wordt op onverwachte situaties. Dit zou bijvoorbeeld kun-
nen zijn wanneer er op een spelkarakter geschoten wordt terwijl deze
de wallhop aan het doen is.

De implementatie is ook nog niet helemaal compleet. Er is een ontwerp


voor overgangen tussen moves—zoals in dit verslag gedocumenteerd,
maar voor de implementatie hiervan ontbrak het mij aan tijd.

36
Verder is er wel een mogelijkheid voor integratie voor de AI, maar in de
praktijk is dit nog in geen van de moves geïmplementeerd. Bij de origi-
nele moves was de code van de move sterk verwikkeld met de Player-
Controller. Hierdoor stond veel code in TickPlayerController(), en wei-
nig in TickPawn(). Als ik dit nog beter had gescheiden, was de interface
met een AI mogelijk geweest. Ook hier was tijd de beperkende factor.

Persoonlijk viel mijn eigen productiviteit me tegen. Dit komt voorname-


lijk komt door de lange compilatietijden. Elke keer als er iets in de de-
claratie een ‘native’ object verandert wordt moet de code bijna hele-
maal opnieuw gecompileerd worden. Dit kost heel veel tijd en haalde
me telkens uit mijn werkritme. Als alternatief probeerde ik zo min mo-
gelijk te compileren, maar dit kon niet altijd.

Wat er goed ging


Bovenal ben ik tevreden met wat ik heb opgeleverd. Voordat ik begon
aan dit project, waren juist dit soort dingen als special moves magie
voor me, een ‘black box’. In de loop van de stage ben ik steeds meer
gaan leren over de engine en de (on)mogelijkheden daarvan.

Ondanks dat het ontwerp nog niet allesomvattend is en nog niet alles is
geïmplementeerd, ben ik tevreden met dat wat ik heb opgeleverd in de-
ze maanden. De opgedane kennis is duidelijk van waarde voor het be-
drijf, dat nu een duidelijk beeld heeft van wat er wel en niet mogelijk is,
en waar de complicaties liggen.

Blokkerende animaties met de StartBlockingAnim() functies zijn uiterst


nuttig.

37
Het animatiesysteem is goed uitgewerkt, past bij de werkwijze van de
animator, en werkt goed. Met dit animatiesysteem is het eenvoudig om
de special moves te integreren in de animatieboom.

Conclusie
Mijn uiteindelijke conclusie is dat mijn special move systeem nog niet
genoeg uitontwikkeld is om gebruikt te worden in een productieomge-
ving. De potentie is er zeker—met wat verdere ontwikkeling zou een
systeem als deze goed bruikbaar zijn.

Een aantal onderdelen uit het special move systeem kunnen direct ge-
bruikt worden, in het bijzonder de aansturing van blokkerende anima-
ties en de AnimNodes.

Hoewel Coded Illusions uiteindelijk heeft besloten om het special move


systeem niet te gaan, is het voor het bedrijf en mij een bijzonder leer-
zame ervaring geweest.

–Sijmen Mulder

38
Summary in English
Assignment: be (re)written in a more generic way. Because all code related
to moves is now gathered at one place, debugging and mainte-
“Design and build a system which handles non-standard nance is easier.
moves. Implement two existing, and two new moves. Option-
ally, design a way for the AI to use the system” This new special move system also supplies support for auto-
matic move selection, animation, and even custom physics.
The internship described in this document was at a game de-
velopment company called Coded Illusions, which is currently What went wrong:
producing its first title, the action-adventure Haven. To be able • Unexpected events during moves are not handled in a
to more easily create new ‘moves’, a system for special move is standardized way. Custom code is required;
required.
• Not everything is fully implemented due to time con-
Examples of special moves include climbing ladders and roll- straints;
ing. During the course of the development, the scope widened
to ‘pretty much anything that temporarily affects a game charac- • AI support marginal, not yet implemented. Again, time
ter’s state’, so even ‘walking’ was considered a move. constraints to blame;
The development platform is Unreal Engine, a well-known • Working with such a large code base slows down devel-
commercial game engine. Most code is written in UnrealScript opment severely, especially to someone new to it.
or C++.
What went right:
Before the special move system, moves were scattered through-
out the source code. A move did not consist of a concise set of • Three moves successfully implemented. One was
classes to define and implement it. To solve this, I have created skipped; this was the second re-implementation for an
an HMove base class, of which all special moves now derive. existing move. This was deemed unnecessary as the
other re-implementation, the ladder move, had already
The idea of the HMove class is that every move has similarities. proven it possible;
By implementing the common denominators, the moves could

39
• Valuable knowledge was acquired;

• Blocking animations with displacement (‘root motion’)


proved very usable throughout multiple moves;

• The animation support was great, and was well received


by the animator.

My final conclusion is that due to the relatively short timeframe,


the current design is not mature enough to be considered useful
in a production environment. While it would certainly improve
readability and scalability of the move code, it does not out-
weigh the time and work needed to implement it throughout
the game.

Certain features however, such as the blocking animation, are


very usable and could be ported over with relative ease.

Coded Illusions thus decided not to use the system, but for both
the company and for me, it has proven a valuable learning ex-
perience.

Sijmen Mulder.

40