# Een inleiding tot het programmeren van Ultimate Stunts

### Inhoud

- Wat u al moet weten
- Programmeergewoonten
- Layout van de broncode
- Een Ultimate Stunts spel in de broncode volgen

### Inleiding

Dit document is geschreven voor mensen die geïnteresseerd zijn in het lezen van de Ultimate Stunts broncode, of die willen bijdragen aan Ultimate Stunts door nieuwe features te programmeren. De reden om dit te schrijven is om nieuwe Ultimate Stunts ontwikkelaars aan te trekken, en om het nieuwe ontwikkelaars zo makkelijk mogelijk te maken om aan de slag te gaan.

### Voordat u code begint toe te voegen

Wees u ervan bewust dat Ultimate Stunts wordt uitgegeven onder de GNU General Public License. U hoort uw code-veranderingen ook onder deze licentie uit te geven. Let er op dat alle voorwaarden in deze licentie aanvaardbaar zijn voor u, voordat u code uitgeeft. Het kan nuttig zijn om deze licentie minimaal één keer door te lezen. U kunt deze licentie vinden in uw Ultimate Stunts pakket als een tekst-bestand met de naam 'COPYING'. U kunt het ook verkrijgen op de website van de Free Software Foundation.

# Wat u al moet weten

Als u slechts aan een beperkt deel van Ultimate Stunts gaat werken, dan hoeft u misschien geen ervaring te hebben met al deze onderwerpen. Ik stel voor dat u dit hele hoofdstuk doorleest, en dan voor uzelf beslist hoe bruikbaar u bent voor het Ultimate Stunts project, en indien noodzakelijk, zorg dan eerst voor wat extra ervaring voordat u zich bij het project aansluit.

### C en C++

Bijna de hele Ultimate Stunts broncode is geschreven in de programmeertaal C++, en de rest is geschreven in C. Als u de broncode wilt bewerken, dan heeft u beslist ervaring nodig met deze talen. Er zijn voldoende gratis online cursussen beschikbaar op het internet, en er zijn ook vrij verkrijgbare compilers voor deze talen. Zorg ervoor dat u ervaring heeft (niet alleen kennis) met onderwerpen zoals afgeleide klassen, virtuele functies en het overloaden daarvan, operator overloading, en C++ templates. Als u om te oefenen moet kiezen tussen verschillende compilers, dan stel ik voor om de zelfde compiler te gebruiken als Ultimate Stunts: de GNU Compiler Collection (gcc). In windows kunt u deze verkrijgen door Cygwin te installeren, maar het is aan te bevelen om Linux te gebruiken, of een ander vrij UNIX-achtig besturingssysteem.

### UNIX/Linux

Ultimate Stunts is ontworpen om voornamelijk op UNIX-achtige platforms te werken, zoals Linux; de windows-versie wordt gecompileerd door de UNIX-emulatiesoftware Cygwin te gebruiken. Het is daarom nuttig om te weten hoe u kunt werken met deze systemen. Het bouw-proces van Ultimate Stunts is gebaseerd op programma's als autoconf en automake, dus als u daaraan iets moet veranderen (bijv. voor het linken met een nieuwe bibliotheek, of voor het fixen van het bouwsysteem voor een bepaald platform), dan moet u weten hoe u deze programma's kunt gebruiken. Zelfs als u nieuwe broncodebestanden toe voegt aan Ultimate Stunts, dan moet u weten hoe de juiste Makefile.am aangepast moet worden (maar dat is niet echt moeilijk).

Voor het vertalen van de gebruikersinterface in andere talen gebruikt Ultimate Stunts de GNU gettext bibliotheek. Als het goed is ingesteld, dan is het niet zo moeilijk om gettext te gebruiken, maar u moet zich ervan bewust zijn dat gettext gebruikt wordt.

### OpenGL

Alle grafische uitvoer in Ultimate Stunts (2D en 3D graphics, menus, teksten, enz.) zijn gebaseerd op OpenGL. Dus, als u iets in Ultimate Stunts wilt aanpassen dat te maken heeft met grafische uitvoer, dan heeft u OpenGL ervaring nodig. Zelfs als u op een hoger niveau werkt, waar u niet direct OpenGL-aanroepen hoeft te doen, kan het handig zijn om het gedrag van OpenGL te kennen. U heeft wat basale theoretische kennis nodig over hoe 3D rendering werkt in OpenGL, wat de verschillende transformatiematrices doen, en natuurlijk hoe u wat lijnen en driehoeken kunt tekenen. Het is ook nuttig om wat ervaring te hebben met het schrijven van uw eigen OpenGL programma's.

### SDL

SDL wordt gebruikt voor het maken van een venster voor de OpenGL context, voor gebruikers-invoer (toetsenbord/muis/joystick) en voor multithreading. Als uw werk niet direct is gerelateerd aan deze onderwerpen, dan hoeft u SDL niet te leren. Zelfs als u in een later stadium iets met SDL moet doen, dan is dit geen groot probleem, omdat SDL vrij eenvoudig te leren is.

De extra bibliotheek SDL_image wordt ook gebruikt. Deze bibliotheek heeft zelfs een nog eenvoudigere programmeerinterface, maar als u niet betrokken bent bij de functies die textures uit bestanden laden, dan hoeft u het niet te kennen.

### FMOD / OpenAL

De bibliotheken FMOD en OpenAL worden beide gebruikt voor geluids-uitvoer. Als u niet aan het geluids-subsysteem werkt, dan hoeft u ze niet te kennen, maar als u er wel aan werkt, dan heeft u kennis van beide nodig. De reden is dat op sommige platforms FMOD wordt gebruikt, en op andere OpenAL, zodat als er iets verandert aan het geluidssysteem, u er voor moet zorgen dat beide nog steeds werken. Het is ook nuttig om in staat te zijn om met beide te compileren (en iets af te weten van autoconf om tussen de twee te kunnen kiezen).

### Wiskunde

Ultimate Stunts is een erg driedimensionaal programma, dus in het grootste deel van de broncode zult u moeten werken met 3D vectoren en hun operatoren, en vaak ook met 3*3 transformatiematrices. Dus, zorg ervoor dat uw kennis van lineaire algebra up-to-date is, en dat u het concept van een rotatiematrix begrijpt (dat kunt u op het internet opzoeken). Voor enkele delen van de broncode, vooral de physics engine, heeft u ook kennis nodig van andere wiskundige concepten, zoals analyse, of numerieke integratie.

### Natuurkunde (van de auto)

U heeft dan en slechts dan gedetailleerde kennis van natuurkunde nodig, als u gaat werken aan de physics engine. Ultimate Stunts gebruikt veel trucs en benaderingen in de simulatie om het makkelijker te spelen te maken, maar er wordt nog steeds een boel echte fysica gebruikt in de simulatie. U moet de wetten van Newtoniaanse mechanica kennen (u weet wel, over snelheden, acceleratie, massa, krachten enz.). en rotatie-mechanica (krachtmomenten, traagheidsmomenten enz.). Het is ook handig om iets te weten van hoe een auto werkt, met dingen als de motorkoppel-curve, versnellings-verhoudingen, wielophanging, gewichtsverdeling tussen de wielen, het gedrag van de banden, enz..

### Ultimate Stunts

Het is essentieel om te weten wat er al aanwezig is in Ultimate Stunts. Dit document zal u helpen om uw weg te vinden in de broncode, maar het bevat niet genoeg detail om alles uit te leggen. Zorg ervoor dat u het spel genoeg gespeeld heeft om alle mogelijkheden te kennen, speel een beetje met de instellingen, maak misschien uw eigen tracks om wat gevoel te krijgen voor hoe Ultimate Stunts tracs 'werken', en lees op de Ultimate Stunts website over de geschiedenis van het project, en de geplande features. De manier hoe dingen geïmplementeerd zijn zal duidelijker zijn als u weet wat het moet doen, en wat het zal moeten doen in de toekomst.

# Programmeergewoonten

Als verschillende programmeurs aan het zelfde stuk code werken, dan is het vaak frustrerend wanneer de code van anderen niet je eigen gewoonten volgt. Daarom beschrijft deze sectie enkele dingen die u moet doen om geen ergernis te veroorzaken bij andere programmeurs. De onderstaande regels zijn bedoeld als algemene richtlijnen, niet als regels die altijd gevolgd moeten worden, zelfs als het resultaat er dom uit ziet. Er worden geen voorbeelden gegeven: de bestaande broncode bevat voldoende voorbeelden.

### Bestandsformaat

Alle broncode-bestanden moeten UNIX-stijl tekstbestanden zijn, niet DOS/windows-stijl of Macintosh-stijl. Als u in windows werkt (niet aanbevolen), zorg er dan voor dat uw tekst-editor documenten opslaat als UNIX-stijl tekstbestanden. De meeste tekst editors in windows doen dat niet. De tekenset zou zo veel mogelijk ASCII moeten zijn, dus alleen de eerste 128 tekens mogen gebruikt worden, en unicode dient niet gebruikt te worden. In de enkele gevallen dat andere tekens nodig zijn, wordt de ISO 8859-1 standaard gevolgd. Alle variabele-namen, functienamen, commentaar enz. moeten in het engels geschreven zijn. Een uitzondering zijn natuurlijk korte variabele-namen zoals x, y of i. Houd als regel aan dat er geen andere natuurlijke talen gebruikt mogen worden in de broncode.

### Indentatie

Indentatie wordt gedaan met tabs, niet met spaties. Zorg ervoor dat u geen tekst-editor gebruikt die tabs omzet in spaties. Voor elk indentatie-niveau wordt één extra tab gebruikt. Er wordt geen aanname gedaan over de breedte van een tab. Een mogelijke uitzondering op de regel voor het gebruik van tabs is er voor het uitlijnen van expressies die in op elkaar volgende regels staan, en die een overeenkomst hebben. Het uitlijnen van zulke expressies maakt het duidelijker om de overeenkomst te zien, wat de code leesbaarder maakt. Omdat er geen aanname gedaan wordt over de breedte van een tab, kan zulke uitlijning vaak alleen gedaan worden met spaties.

De plaatsing van accolades ('{' en '}') moet op de zelfde manier gedaan worden als in de bestaande code. Dus, de openende en de sluitende acollade moeten beide op hun eigen regel geplaatst worden, die niets bevat behalve de acollade en mogelijk een commentaartekst. De indentatie hoort het zelfde te zijn als die van het statement waar het code-blok onderdeel van uit maakt; de code binnen de acollades moet een indentatie meer krijgen. Er wordt een uitzondering gemaakt voor erg kleine code-blokken die op een enkele regel passen. Zie de broncode hoe dit gedaan wordt.

### Namen van identifiers

Voor namen van identifiers, zoals functienamen, class namen en variable-namen, worden de volgende conventies gebruikt:

- Class namen hebben een 'C' prefix, maar de overeenkomstige bestandsnamen van de broncode hebben deze prefix niet.
- Struct namen hebben een 'S' prefix. Wanneer methods worden toegevoegd aan een struct, wordt het geconverteerd naar een class, en wordt de prefix veranderd in een 'C'.
- Member variabelen van classes hebben een "m_" prefix.
- Enum typen hebben een 'e' prefix
- Indien mogelijk, bevatten functie- en method-namen een werkwoord. Dus het is CTrack::getWidth() en niet CTrack::width(). Functie- and method-namen beginnen altijd met een kleine letter. Hoofdletters worden gebruikt voor de eerste letter van elk woord in de naam, behalve het eerste woord.

### Hergebruik van code

Veel classes in Ultimate Stunts zijn gemaakt met als doel om herbruikbaar te zijn. Het gebruik van deze classes zal uw code er netter uit laten zien, en uw code zal makkelijker te begrijpen zijn voor Ultimate Stunts ontwikkelaars die bekend zijn met deze classes. Het kan u ook tijd besparen omdat u niet het wiel opnieuw hoeft uit te vinden, en toekomstige verbeteringen aan deze classes, zoals bug fixes en snelheidsverbeteringen zullen ook uw code verbeteren.

Als u een class wilt gebruiken die nuttig lijkt te zijn, maar uiteindelijk blijkt dat er wat functionaliteit mist die essentieel is voor uw code, dan kan het nog steeds nuttig zijn om de class te gebruiken, en de missende functionaliteit te implementeren door een afgeleide class te maken of door de class zelf aan te passen. Die laatste mogelijkheid wordt aangemoedigd als er een goede mogelijkheid is dat de nieuwe functionaliteit ook nuttig zal zijn in andere delen van het programma. Als u twijfelt over waar u nieuwe functionaliteit moet plaatsen, vraag het aan de project-beheerder. Het is veel beter om te veel te vragen over architectuurbeslissingen dan om te weinig te vragen.

Er zijn een aantal algemene classes die altijd gebruikt moeten worden wanneer ze van toepassing zijn:

- De CString class. Haal het niet in uw hoofd om char * strings intern in Ultimate Stunts te gebruiken! char * strings mogen alleen gebruikt worden binnen CString en in aanroepen naar externe bibliotheken.
- De CVector en CMatrix classes. Gebruikze voor elke 3D vector en 3*3 transformatie-matrix. Zelfs kleuren (rood/groen/blauw componenten) kunnen behandeld worden als CVector objecten.
- De standard template library (STL) classes. Het gebruik van std::vector templates in plaats van arrays kan u een boel geheugenbeheer-problemen besparen.

### Toevoegen van nieuwe broncode-bestanden

In het grootste deel van de Ultimate Stunts broncode is elke class in zijn eigen bestanden geplaatst. Elke class heeft twee bestanden: een headerbestand dat de class definitie zonder memberfunctie-implementaties bevat (behalve voor de meest triviale functies), en een .cpp bestand dat de memberfunctie-implementaties bevat. Het wordt op prijs gesteld als u deze gewoonte volgt. C++ broncodebestanden hebben de .cpp extensie, niet .cxx, .c++, .cc of wat dan ook. Headerbestanden (zowel C als C++) hebben de .h extensie. Geef bij het toevoegen de nieuwe bestanden de zelfde opmaak als de bestaande broncodebestanden, met een bestandsnaam, een korte beschrijving, de auteursnaam enz. (u kunt uw eigen naam invullen).

Let er elke keer op bij het toevoegen van nieuwe broncodebestanden dat ze toegevoegd worden aan het Makefile.am bestand in de directory van het broncodebestand, en aan het ultimatestunts.kdevelop.filelist bestand in de hoofddirectory van de Ultimate Stunts broncode. Toevoegen aan de Makefile.am voegt ze toe aan het bouwproces, zodat ze daadwerkelijk gecompileerd worden, en toevoegen aan ultimatestunts.kdevelop.filelist laat gebruikers van de KDevelop omgeving zien dat deze bestanden onderdeel zijn van de broncode.

### Schoon houden van de code

Er zijn wat algemene richtlijnen voor C++ programmeurs voor het schoon houden van hun code. Het gebruik van goed gestructureerde code houdt het beter leesbaar en begrijpelijk voor uzelf en andere programmeurs in de toekomst, en het helpt u om geen bugs te maken. Er zijn door anderen al een boel richtlijnen geschreven: ik zal niet elke herhalen. Er zijn een paar belangrijke:

- Geheugencorrupties zijn een belangrijke categorie van bugs in C en C++ programma's. Zorg ervoor dat voor elk dynamisch gealloceerd ding in uw code het helemaal duidelijk is welke code verantwoordelijk is voor het alloceren en voor het vrijgeven van dat geheugen. Het is gebruikelijk om een class verantwoordelijk te maken voor het geheugenbeheer van al zijn membervariabelen. Op deze manier zorgt het aanroepen van de constructor en/of destructor van instanties van een class automatisch voor het juiste geheugenbeheer-gedrag. Als u ooit het geheugenbeheer op een andere manier wilt doen (zoals het ene object aanpassingen laten doen aan de geheugenallocatie van members van een ander object), zorg er dan voor dat u een erg goed excuus heeft.
- Bescherm membervariabelen van classes door ze protected of private te maken. In het ideale geval kan de data van een object nooit gecorrumpeerd raken door het aanroepen van publieke memberfuncties. In de praktijk van de Ultimate Stunts broncode worden veel consistentie-controles niet gedaan, en membervariabelen zijn soms public. Dit is vaak gedaan om snelheidsredenen, of uit luiheid.
- Bescherm zo veel mogelijk dingen met het const keyword. Dit maakt het makkelijker om mogelijkheden te reduceren bij het debuggen.

# Layout van de broncode

Op dit punt zou u klaar moeten zijn om een inleiding te krijgen in hoe de Ultimate Stunts broncode is georganiseerd, zodat u uw weg er in kunt vinden.

### Directories

De Ultimate Stunts broncode is opgedeeld in verschillende directories. De reden hiervoor is dat Ultimate Stunts bestaat uit meerdere programma's bestaat (het hoofdprogramma, het server-programma, de track editor, de 3D editor en de AI client). Het was mogelijk om al hun broncode in een enkele directory te stoppen, maar dit zou het niet makkelijk gemaakt hebben om de juiste bestanden te vinden waar je naar op zoek bent. Sommige van deze directories bevatten code die specifiek is voor één van deze programma's, en de andere directories bevatten code die gedeeld wordt tussen programma's. Deze gedeelde code wordt gecompileerd naar statische bibliotheken, en de programma's die deze code gebruiken worden gelinkt tegen deze bibliotheken.

Dit zijn de broncode-directories:

- shared: deze code wordt gedeeld tussen alle programma's. Ze bevatten classes voor algemeen gebruik, bijv. voor string-manipulatie, bestanden laden of opslaan, of data management.
- simulation: deze code wordt gedeeld tussen alle programma's die iets doen met simulatie. Aangezien de CTrack class ook in deze directory staat, wordt de track editor ook gelinkt met deze code. Deze directory bevat ook veel van de netwerkcommunicatie-code.
- graphics: deze code wordt gedeeld tussen alle programma's die iets met graphics doen. Alleen de AI client en het serverprogramma gebruiken deze code niet. Deze code bevat zowel 3D graphics classes (zoals textures of 3D geometrie) en 2D objecten zoals menu interface widgets.
- stuntsserver: deze code is specifiek voor het ustuntsserver programma. Dit bevat veel delen van de server-kant van de netwerkcommunicatie, en ook het multi-threaded model van de server.
- stuntsai: deze code is specifiek voor het ustuntsai programma.
- stunts3dedit: deze code is specifiek voor het ustunts3dedit programma. Bevat veel functies voor het bewerken van 3D geometrie, en wat importeer-functies voor verschillende 3D formaten.
- trackedit: deze code is specifiek voor het ustuntstrackedit programma.
- ultimatestunts: deze code is specifiek voor het ustunts programma. Dit bevat ook alle broncode voor het geluids-subsysteem.

### Classes

Ultimate Stunts bevat veel classes, en tijdens de ontwikkeling worden voortdurend nieuwe classes gemaakt, en soms worden classes hernoemd of verwijderd. Daardoor is het niet mogelijk om een complete lijst te geven van alle classes in de meest recente Ultimate Stunts versie. In deze sectie worden een paar belangrijke classes genoemd.

##### CGameCore/CUSCore

CGameCore is waarschijnlijk de belangrijkste class om te begrijpen als u gaat werken binnen de "game loop". Deze class levert een eenvoudige programmeerinterface aan andere code voor het initialiseren van een spel, het starten ervan, stoppen, unloading enz.. De CGameCore class zelf behandelt alleen de simulatie, en is aanwezig in simulation/gamecore.*. De CUSCore class in ultimatestunts/uscore.*, die is afgeleid van CGameCore, behandelt ook geluid en graphics.

##### CFile/CDataFile/CFileControl

CFile is een generieke class voor het laden en opslaan van bestanden. Het kan zowel tekstbestanden als binaire bestanden aan. Bij het lezen van tekstbestanden met de readl() method kan het zowel UNIX-stijl als windows-stijl tekstbestanden inlezen. De shared/cfile.h header definieert ook een paar bestandssysteem-hulpfuncties.

CDataFile, afgeleid van CFile, levert de zelfde programmeerinterface. Het verschil is dat CDataFile niet de absolute bestandsnaam als parameter krijgt. In plaats daarvan geeft de programmeur het pad relatief t.o.v. de Ultimate Stunts datadir. Daarna bepaalt CDataFile automatisch de echt locatie van het bestand, en haalt het daar vandaan. Het is in staat om op meerdere locaties op het locale bestandssysteem te zoeken, maar het kan ook het bestand downloaden van een Ultimate Stunts game server. Het uiteindelijke gedrag kan aardig ingewikkeld zijn, dus het is belangrijk dat dit maar één keer geïmplementeerd wordt. Aangezien bijna elk databestand zich ergens in de datadir bevindt, zou CDataFile bij het laden van al deze bestanden op de één of andere manier betrokken moeten zijn. Als het laden van een bestand wordt gedaan met een library call, zodat het fysieke pad nodig is i.p.v. een CDataFile object, dan is de useExtern method nuttig.

Het gedrag van CDataFile wordt aangestuurd door een instantie van de CFileControl class. Er hoort maar één instantie te zijn van deze class. Het gedrag moet veranderd worden bijv. als er een verbinding gemaakt wordt met een Ultimate Stunts game server, om het mogelijk te maken om automatisch bestanden te downloaden.

##### CDataObject/CDataManager/CWorld/CGraphicWorld

Ultimate Stunts moet een boel verschillende data objecten laden: tracks, textures, tiles, auto's enz., en er zijn veel afhankelijkheden tussen deze bestanden. Om deze afhankelijkheden efficiënt te beheren, en om er voor te zorgen dat objecten niet twee keer geladen worden, zijn alle object typen afgeleid van CDataObject (shared/dataobject.*). Classes zoals CTexture of CTrack zijn allemaal afgeleid van CDataObject.

Een CDataObject object wordt geïdentificeerd met het type (een enum-type variabele), de bestandsnaam en een aantal parameters. Normaal gesproken zijn CDataObject objecten verbonden aan een CDataManager object. Het object wordt aangemaakt door het CDataManager object als het een aanvraag krijgt voor het object, en het wordt verwijderd door het CDataManager object wanneer dat nodig is. Ook wordt er een pointer naar het CDataManager object meegegeven aan de CDataObject constructor, zodat het CDataObject object aan het CDataManager object kan vragen om bestanden waar het van afhankelijk is. Als de opgevraagde bestanden al geladen zijn, dan wordt een pointer naar het al eerder geladen object teruggegeven, zodat objecten nooit twee keer geladen worden.

Er zijn verschillende instanties van CDataManager-afgeleide classes in een Ultimate Stunts spel-sessie. De meest centrale is de instantie van CWorld (simulation/world.*). In CWorld::createObject kunt u zien hoe het type-enum verbonden wordt aan class typen in CWorld. De meeste zijn verbonden met een bepaalde class, maar bijv. het eSample type is verbonden met een leeg CDataObject. Dit is zo omdat Ultimate Stunts op dit niveau niets doet met de informatie uit geluidsbestanden, het slaat alleen de bestandsinformatie op zodat het geluids-subsysteem weet welke bestanden in een later stadium geladen kunnen worden.

Een andere CDataManager-afgeleide instantie is van het type CGraphicWorld (graphics/graphicworld.*). Terwijl CWorld alleen de data laadt die nodig is voor het simulatie-subsysteem, laadt CGraphicWorld alle data die nodig is voor het graphics-subsysteem. Dat is waarom het enum-type van objecten in CGraphicWorld is verbonden met andere classes dan in CWorld: in CWorld zou eTileModel verbonden zijn met een tile botsings-model, maar in CGraphicsWorld is het verbonden met een CGraphObj, dat de grafische mesh-data van een tile bevat. Vanwege deze scheiding is het mogelijk om het server-programma alle simulatie-data te laten laden zonder dat het nodig is om alle grafische data te laden.

##### CWinSystem

In Ultimate Stunts levert de CWinSystem class (graphics/winsystem.*) een interface naar het graphics venster. Het initialiseert het venster en beheert een aantal events, zoals resize events, toetsenbord- en muis-invoer events, en joystick invoer. Het heeft twee methods met de naam runLoop; beide starten een loop die reageert op events. Één van deze communiceert met de rest van het programma via een callback-functie: dez wordt gebruikt in het spel zelf. De andere is gebaseerd op een CWidget object, en wordt gebruikt in de menu-gebruikersinterface.

CWinSystem heeft ook twee systemen voor het bepalen van de toestenbord-toestand (drie als we het widget-systeem meetellen, maar die hoort niet op het zelfde moment gebruikt te worden als de andere twee systemen). Één methode, de getKeyState(..) method, geeft de huidige toestand van een toets (ingedrukt of losgelaten), wat handig is voor toetsen die gebruikt worden als spel-invoer, zoals de pijltjestoetsen voor het besturen van een auto. De andere method, wasPressed(..), geeft terug of een toets was ingedrukt sinds de laatste aanroep van wasPressed. Dit is handig voor toetsen die een meer event-achtige aard hebben, zoals de escape-toets voor het verlaten van een spel. En, om dingen nog wat ingewikkelder te maken, er is een afgeleide class CGameWinsystem (ultimatestunts/gamewinsystem.*) die een aantal functies levert voor het bepalen van de toestand van gebruiker-gedefinieerde toetsen.

##### CWidget/CGUIPage/CGUI

De menu-gebruikersinterface is gebaseerd op een event-gebaseerd widget-systeem. Elg gebruikersinterface-element, zoals een lijst van opties, of een message box, is gebaseerd op een van CWidget afgeleide class. Zo'n widget is verantwoordelijk voor het updaten van zijn deel van het scherm, en voor het op de juiste manier afhandelen van invoer-events. Events worden aan een widget doorgegeven door methods van het object aan te roepen, en een widget kan deze events afhandelen door overloaded versies van deze methods te hebben. Bijvoorbeeld, de meeste widget-types zullen een overloaded onRedraw method hebben, die aangeroepen zal worden als de widget opnieuw getekend moet worden. Als een widget sub-widgets bevat, dan is de widget verantwoordelijk voor het aanroepen van de event-methods van de sub-widgets. Alle grafische uitvoer wordt gedaan met OpenGL, alle invoer-parameters (bijv. toetsenbord toetsen-waarden) worden gedaan met SDL constanten.

Een veelgebruikt widget-type is de CGUIPAge class. Het bevat een achtergrond en een titel, en een aantal sub-widgets. De sub-widgets zijn van onder naar boven gesorteerd en worden in die volgorde getekend, en de bovenste widhet heeft de toetsenbord- en muis-invoerfocus. Meestal is de top-level widget van het CGUI type, en zijn enige sub-widget is van het CGUIPage type.

CGUI is een widget-type dat slechts één enkele full-screen CGUIPage sub-widget heeft. Het heeft enkele features die het nuttig maken als een top-level widget. Bijvoorbeeld, het bevat methods om te switchen tussen 2D en 3D mode in OpenGL, en het bevat methods voor het eenvoudig maken van message boxes en andere veelgebruikte gebruikersinterfacecomponenten.

##### CLConfig

De CLConfig class (shared/lconfig.*) wordt gebruikt voor .ini-stijl configuratiebestanden in Ultimate Stunts, zoals ultimatestunts.conf en de auto-configuratiebestanden. Doordat Ultimate Stunts in een vroeg ontwikel-stadium was toen dit werd geïmplementeerd, is het niet gebaseerd op CFile. Het is ook niet gebaseerd op CDataFile, omdat ultimatestunts.conf geladen moet worden voordat bekend is waar de datadir locaties zijn, omdat deze informatie in ultimatestunts.conf is opgeslagen.

# Een Ultimate Stunts spel in de broncode volgen

U zult waarschijnlijk veel leren over de Ultimate Stunts broncode door te zien welke functies waar aangeroepen worden in een Ultimate Stunts spelsessie. Het scenario dat in deze sectie gevolgd wordt is als volgt: een speler start Ultimate Stunts, klikt op "Race!" om een spel te starten, speelt totdat hij/zij finisht, en verlaat dan het programma. Het is sterk aangeraden om de bijbehorende broncodebestanden te lezen bij het lezen van deze tekst.

### ultimatestunts/main.cpp

Net zoals bij elk ander C/C++ programma begint de uitvoering in de main functie. Voor het ustunts-programma bevindt deze zich in ultimatestunts/main.cpp. U kunt zien dat het in feite vrij kort is:

- shared_main wordt aangeroepen
- een CGameWinSystem en CGameGUI object worden gemaakt
- De start method van het CGameGUI object wordt aangeroepen
- De CGameWinSystem en CGameGUI objecten worden vernietigd
- main functie wordt verlaten

De shared_main functie (in shared/usmisc.cpp) stelt een paar algemene dingen in. Bijvoorbeeld, het vindt ultimatestunts.conf en het laadt het, en het stelt de gettext localisatie-instellingen in. Het maken van het CGameWinSystem object (afgeleid van CWinSystem in graphics/winsystem.cpp) creëert een venster voor Ultimate Stunts, overeenkomstig de instellingen in het theMainConfig object, dat een CLConfig object is dat geladen is van ultimatestunts.conf. Het venster zal vernietigd worden zodra het CGameWinSystem object vernietigd wordt, aan het einde van de main functie.

Al het interessante werk gebeurt in de CGameGUI::start method. Deze functie zal pas verlaten worden wanneer de speler beslist om Ultimate Stunts af te sluiten.

### ultimatestunts/gamegui.cpp

Het eerste dat we deden met het CGameGUI object was het aanroepen van de constructor. CGameGUI::CGameGUI(..) doet verschillende dingen, maar het meeste heeft te maken met het initialiseren van de verschillende pagina's van het spel-menu en de menu-teksten. Een ander belangrijk ding om te zien is dat het een instantie maakt van de CUSCore class, die pas zal worden vernietigd wanneer het CGameGUI object wordt vernietigd.

Het tweede wat we deden was het aanroepen van CGameGUI::start(). Deze method brengt OpenGL eerst in 2D mode door CGUI::enter2DMode() aan te roepen. Daarna voert het een while-loop uit. Binnen deze loop worden de verschillende delen van de menus geïdentificeerd met string-waarden, en ze zijn allemaal geïmplementeerd met hun eigen CGameGUI method. Elke menu-method retourneert de string identifier van het volgende menu dat moet worden weergegeven. In ons voorbeeld is section in eerste instantie "mainmenu", zodat viewMainMenu() aangeroepen wordt. Wanneer de speler de "Race!" knop selecteert, geeft viewMainMenu() het resultaat "playgame" terug, zodat de playGame() method aangeroepen wordt. Zodra het spel is afgelopen, komt playGame() terug met de waarde "hiscore", enz., totdat de speler "Afsluiten" selecteert in het hoofdmenu. Als de speler dan bevestigt dat hij/zij echt wil afsluiten, dan wordt de loop verlaten, leave2DMode() wordt aangeroepen, en start() method wordt verlaten.

CGameGUI::viewMainMenu() laat een eenvoudige implementatie van een spelmenu zien. Eerst wordt het juiste child widget geselecteerd, dan wordt het CGameWinSystem object geïnstrueerd om de event loop te starten, en wanneer die klaar is, wordt het resultaat geïnspecteerd en verwerkt. De CWinSystem::runLoop(CWidget *widget) method die hier gebruikt wordt bevat een loop die continu pollt naar SDL events, en die de event handler methods van de widget aanroept wanneer ze zich voor doen. De loop wordt verlaten als een event handler een return-waarde terug geeft die de WIDGET_QUIT vlag bevat. Dit gebeurt bijvoorbeeld als de speler op de "Race!" menu-optie klikt. Dan wordt runLoop verlaten, en de uitvoering gaat verder in viewMainMenu, waar deze keuze verwerkt wordt.

CGameGUI::playGame() is wat anders dan de andere menus, omdat het een spel opzet, het spel laat draaien, en dan het spel weer opruimt. De method zelf is vrij eenvoudig:

- CGameGUI::load() wordt aangeroepen
- CGUI::leave2DMode() wordt aangeroepen
- Het spel draait door de CWinSystem::runLoop method
- CGUI::enter2DMode() wordt aangeroepen
- CGameGUI::unload() wordt aangeroepen

CGameGUI::load() doet het vuile werk van het opzetten van het spel, gebaseerd op de invoer van de speler in de verschillende sub-menus. Het roept een boel andere dingen aan in het m_GameCore object, en soms ook in het m_Server object (dit is in ons scenario niet het geval, omdat we een lokaal spel spelen). CGameGUI::unload() doet het minder vuile werk van het opruimen van alles.

We dalen steeds dieper af in de kern van Ultimate Stunts. De aanroep van CWinSystem::runLoop in playGame() krijgt de kleine functie game_mainloop() mee als een callback-parameter. game_mainloop() (in ultimatestunts/gamegui.cpp) doet niets anders dan het aanroepen van de update() method in het CUSCore object. Zolang deze functie true teruggeeft, gaat CWinSystem::runLoop verder mett het pollen naar events en het aanroepen van game_mainloop(). Dus, nu is alle controle in handen van het CUSCore object.

### ultimatestunts/uscore.cpp

Het laden van alle bestanden wordt gedaan door een aanroep van CUSCore::readyAndLoad() vanuit CGameGUI::load(). Als eerste roept CUSCore::readyAndLoad() de zelfde method aan van de base class, CGameCore::readyAndLoad(). Deze method initialiseert dingen zoals netwerk-communicatie, en laadt dan alle data door loadTrackData en loadMovObjData aan te roepen. Deze methods, die ook overloaded zijn in CUSCore, laden de data van de track en de bewegende objecten. In de overloaded versies van CUSCore wordt de bijbehorende graphics en geluids-data ook geladen. Het laden van de track data in CGameCore::loadTrackData() wordt eenvoudigweg gedaan door het m_World object de opdracht te geven om het track-object te laden. Alle afhankelijkheden, zoals tiles, worden automatisch opgelost doordat CWorld is afgeleid van CDataManager. Het laden van bewegende objecten is iets gecompliceerder, omdat in een multiplayer-spel de remote spelers ook auto's kunnen toevoegen. Het managen van het laden van bewegende objecten is de verantwoordelijkheid van CPlayerControl en de daarvan afgeleide classes. In ons scenario wordt alleen een lokaal spel gespeeld, dus m_PCtrl is van het type CPlayerControl, en de implementatie van CPlayerControl::loadObjects() is vrij eenvoudig. Ook hier is het laden van objecten weer gebaseerd op de CDataManager::loadObject method van de CWorld class.

Het laden van de grafische data wordt gedaan wanneer CUSCore::loadTrackData() en CUSCore::loadMovObjData() klaar zijn met het aanroepen van hun CGameCore equivalenten, en de methods loadTrackdata() en loadObjData() van het m_Renderer object aanroepen. Zoals u kunt zien in ultimatestunts/gamerenderer.cpp, roepen deze vergelijkbare methods aan in een CGraphicWorld object. En in graphics/graphicworld.cpp ziet u hoe deze methods alle grafische data laden op basis van de al eerder geladen data in het CWorld object.

Zodra alles is geladen, zal CUSCore::update() herhaald worden aangeroepen. CUSCore::update() doet alle dingen die nodig zijn in een spel-periode. Het controleert de toestand van sommige toetsen, het updatet de spel-simulatie door CGameCore::update() aan te roepen, en tot slot updatet het alle uitvoer-elementen, zoals graphics en geluid. Dus, de kern van de simulatie is in CGameCore::update().

CGameCore::update() doet veel dingen die alleen relevant zijn voor netwerkcommunicatie in multiplayer-spellen, maar de kern-activiteiten zijn het aanroepen van de update() methods van alle objecten in m_Players en m_Simulations. De methods van de m_Player objecten zorgen er voor dat de verschillende spelers hun acties toepassen. Dit kan bijvoorbeeld de routines van een AI speler bevatten, of het controleren van de toestand van invoer-apparaten voor een menselijke speler. De update() methods van objecten in m_Simulations bepalen het gedrag van de bewegende objecten. In ons scenario zijn er drie simulatie-objecten: één CRuleControl object, dat alle spelregels implementeert, zoals penalty-tijd of finishen, één CPhysics object dat alle fysische berekeningen doet, en een CReplayer object dat de toestanden van alle objecten vastlegt in een replay-bestand.

Als de speler finisht, wordt dit gedetecteerd door het CRuleControl object (simulation/rulecontrol.*), en een korte tijd na het finishen zal zijn update method false terug geven. Dit zorgt er voor dat CGameCore::update() en CUSCore::update() false terug geven. Dit zorgt er voor dat het CWinSystem object de runLoop-functie verlaat, en de speler zal terug keren in de menu interface.