TM
Is XNA™ geschikt voor serious games ? Academiejaar 2007-2008
Eindwerk voorgedragen door Jens Wouters tot het behalen van het diploma Bachelor in de Grafische en Digitale Media, afstudeerrichting Multimediaproductie.
Interne Promotor: Externe Promotor:
Dhr. De Pauw-Waterschoot Philippe Dhr. Kets Brecht
Arteveldehogeschool Opleiding Grafische en Digitale Media Industrieweg 232 9030 Mariakerke
Lid van de Associatie Universiteit Gent
TM
Is XNA™ geschikt voor serious games ?
Eindwerk voorgedragen door Jens Wouters tot het behalen van het diploma Bachelor in de Grafische en Digitale Media, afstudeerrichting Multimediaproductie.
Interne Promotor:
Dhr. De Pauw-Waterschoot Philippe
Externe Promotor:
Dhr. Kets Brecht
Academiejaar 2007-2008
Arteveldehogeschool Opleiding Grafische en Digitale Media Industrieweg 232 9030 Mariakerke
Lid van de Associatie Universiteit Gent
Woord vooraf Games zijn al altijd één van mijn grootste interesses geweest. Aangezien ik later in de game-industrie werkzaam wil zijn, had ik besloten om een onderwerp te kiezen die verband hield met games. Na een paar mogelijkheden te hebben afgewogen, heb ik gekozen voor XNA. De zoektocht naar een externe promotor is via via verlopen. Zo had ik eerst Microsoft gecontacteerd, maar daar kreeg ik de melding dat ze mij niet konden helpen. Nadat ik een oproep had gedaan op het 9lives forum, had Killgore mij de raad gegeven om contact op te nemen met de organisatie BGIn. Daar heeft CEO Tommy Goffin voor mij contact opgenomen met Walter Stiers, op dat moment nog medewerker van Microsoft Belgium en lid van Microsoft’s Academic Relations Team BeLux. Walter Stiers heeft mij dan de gegevens van 3 personen doorgegeven die ik het beste kon contacteren. Dit waren Brecht Kets (lector game design aan de HOWEST - departement PIH), Ir. Riemer Grootjans (PHD-student aan de VUB) en Loic Dansart (hobby game developer en maker van het spel Little Gamers). Uiteindelijk wou Brecht Kets mijn externe promotor wel zijn. Bij deze wil ik al deze mensen bedanken en zeker Brecht Kets voor de steun bij het schrijven van het eindwerk. Als deel van mijn onderzoek ben ik ook naar het XNA 2.0 launch event geweest. XNA stond natuurlijk centraal, maar er waren ook sprekers die in de game-industrie werkzaam zijn. Na dit event heb ik nog meer zin gekregen om in de gamesector te werken. Graag wil ik alle bedrijven en mensen bedanken die hebben meegeholpen aan mijn onderzoek in verband met serious games. Natuurlijk wil ik ook nog mijn ouders, Sabine Van de Sompele en Jozef Wouters, bedanken om mij de mogelijkheden te bieden om deze opleiding te kunnen volgen en het eindwerk te financieren. Vervolgens ben ik mijn zus, Yn Wouters, dankbaar voor het verbeteren van het schrijfwerk, ondanks dat de technische delen Chinees voor haar waren. Tenslotte wil ik ook nog mijn interne promotor Philippe De Pauw-Waterschoot bedanken.
Woord vooraf
Inhoudsopgave Inleiding
3
Deel I : XNA Hoofdstuk 1 : Wat is XNA ? 1.1 XNA Framework 1.2 Tools 1.3 IDE : Game Studio Hoofdstuk 2 : Geschiedenis Hoofdstuk 3 : XNA basic file Hoofdstuk 4 : Content Pipeline Hoofdstuk 5 : Grafische Mogelijkheden 5.1 2D 5.1.1 Afbeeldingen 5.1.2 Tekst 5.2 3D 5.2.1 Assenstelsel 5.2.2 3D en matrices 5.2.3 3D plaatsen en weergeven 5.2.4 3D in XNA Hoofdstuk 6 : Audio (XACT) 6.1 XACT, het programma 6.2 Geluid in XNA Hoofdstuk 7 : Xbox 360 Hoofdstuk 8 : Voorbeeld Game : Rat Maze 8.1 Input 8.2 Muurdetectie
5 6 7 7 8 9 11 17 19 19 19 21 22 22 23 25 25 29 29 31 34 35 36 37
Deel II : XNA & serious games Hoofdstuk 9 : Wat zijn serious games ? Hoofdstuk 10 : Onderzoek : Is XNA geschikt voor serious games ?
38 39 41
Deel III : Algemeen besluit
43
Inhoudsopgave
Bronnen
45
Bijlagen A. Systeemvereisten & installatie B. XNA Basic File Game1.cs (code) C. Afbeeldingen & Tekst (code) D. Transformatiematrices E. 3D in XNA (code) F. Geluid in XNA (code)
47
Inhoudsopgave
II
Inleiding Om de probleemstelling beter te begrijpen, is het misschien beter dat er eerst een korte uitleg volgt over XNA en serious games. Wat beide exact inhouden kun je lezen in Hoofdstuk 1, respectievelijk Hoofdstuk 9. Voorlopig volstaat het hier om te weten dat XNA een makkelijkere en goedkopere manier is om games te maken voor zowel Windows als Xbox 360 en dat serious games een ander doel hebben dan pure entertainment. Is XNA geschikt voor serious games ? Serious games zijn enorm in populariteit aan het toenemen. Daar zij niet altijd veel budget nodig hebben en soms in een zeer korte tijdspanne gemaakt dienen te worden, kan XNA hier eventueel een oplossing bieden. Of dit zo is kun je lezen in Deel II. Om de onderdelen uit Deel I te begrijpen, is het handig dat je vertrouwd bent met programmeren en het liefst nog met de programmeertaal C#. De code is echter telkens op een zo makkelijk mogelijke manier uitgelegd. Ondanks dat veel programmeurs de voorkeur hebben om klassen, variabelen en methodes te benoemen via een Engelse naam (omdat de programmeertalen in het Engels zijn), wordt er toch gebruik gemaakt van Nederlandse benamingen zodat alles toch iets eenvoudiger te lezen valt. Deel I geeft uitleg over wat XNA is en wat je ermee kan doen. Gezien de beperkte omvang van het eindwerk is het niet mogelijk om alles uit te leggen. Er is getracht om de basis uit te leggen zodat je zelf aan de slag kunt. Er is reeds een grote community online waar je de nodige info kunt vinden. Vergeet ook niet de product informatie ( = help, F1) erop na te lezen, deze bevat soms de complete uitleg over hoe je iets moet doen. Deel II behandelt naast het onderzoek ook een beknopte theorie over serious games. Het onderzoek is gebaseerd op meningen en feiten van zowel mensen uit de game-industrie als de mensen die geïnteresseerd zijn in games. Aangezien XNA oorspronkelijk bedoeld is voor studenten en hobbyisten, kan ook hun mening niet achterwege blijven. Deel I en Deel II zijn min of meer onafhankelijk van elkaar. Deel I hoeft niet te worden gelezen om Deel II te begrijpen. Dit eindwerk kan handig zijn voor mensen die zich dezelfde vraag stellen die hier behandeld wordt, of zelfs voor mensen die enkel iets willen weten over XNA of over serious games. Op het ogenblik dat dit eindwerk geschreven wordt, is XNA aan zijn 2de versie toe : XNA 2.0. Dit is dus ook de versie waarin de voorbeelden geschreven zijn. Er is veel kans dat het moment waarop je dit leest, er reeds een nieuwe versie van XNA beschikbaar is, want XNA evolueert razendsnel. Het zou geen grote problemen met zich mogen meebrengen om de in dit eindwerk gebruikte code om te zetten in een nieuwe XNA versie. Bij de overschakeling van XNA 1.0 Refresh naar XNA 2.0 was er trouwens een tool beschikbaar die 1.0 bestanden 2.0-compatibel maakte. Elke nieuwe versie zal wel over zo’n tool beschikken. Hier en daar moet je echter wel handmatig aanpassingen doen om het volledig compatibel te maken.
Inleiding
In het eindwerk wordt gesproken over games en niet over computerspelletjes. Het woord game is in de loop der de tijd bij de gamers (computerspelletjesspelers) ingeburgerd geraakt. Het woord spel wordt meer gebruikt om een fysiek spel aan te duiden (tikkertje, kaarten, gezelschapspelen, …). BELANGRIJK : vooraleer je de bestanden test, is het aangeraden om minstens de XNA Runtime of de complete XNA Game Studio geïnstalleerd te hebben, want anders zullen de bestanden niet uitgevoerd kunnen worden. Wil je zelf ontwikkelen, dan kun je terugvinden welke programma’s je nodig hebt in bijlage A. Systeemvereisten & installatie. De gratis downloads vind je ook terug op de cd-rom in de map Downloads. Alle code kun je terug vinden op de cd-rom in bijlage. Het is aangeraden om alle code op je harde schijf te plaatsen vooraleer je zelf wilt debuggen. Er worden dan bestanden aangemaakt en deze kunnen natuurlijk niet op de cd-rom weggeschreven worden. Tijdens het debuggen bouw je je game eerst op. Dit wordt een build genoemd. Je kan ook een build laten uitvoeren zonder dat je de game hierna opstart. Dit kun je doen via Build > Build Solution of door gewoonweg F6 in te drukken in Visual Studio. Dit kan handig zijn om je game op code fouten te controleren zonder dat de game opgestart moet worden. Indien je echter gewoon de resultaten wil zien die de code voortgebracht heeft, dan kun je terecht bij elke solution op de locatie Solution Root > bin > x86 > Debug > GameNaam.exe. Bij de voorbeeldgame kun je dit echter niet doen omdat de game tijdens het lopen bestanden opslaat. Deze moet dus op de harde schijf opgeslagen worden. Het eindwerk is ook op de cd-rom te vinden in PDF formaat, handig als je iets specifiek wil vinden. Je kan dan Ctrl + F gebruiken om te zoeken.
Inleiding
Deel I : XNA
Hoofdstuk 1 : Wat is XNA ? XNA, deze drie letters ben je nu toch al enkele keren tegengekomen, maar waar staan ze nu eigenlijk voor ? De drie letters X, N en A zijn een afkorting voor XNA’s Not Acronymed, wat dus wil zeggen dat XNA helemaal geen afkorting is ! Het kind had gewoon een naam nodig en dit is dus gewoonweg XNA geworden. De X is waarschijnlijk afkomstig van het meer gekende DirectX en aangezien men niet op een goede naam kwam voor de nieuwe technologie, heeft men dan maar voor XNA gekozen. Wat het verband is tussen DirectX en XNA kun je lezen in Hoofdstuk 2. Nu je weet wat de afkorting betekent, is het tijd voor het echte werk : XNA, de technologie. XNA is eigenlijk een zeer ruime term en duidt op alles wat Microsoft produceert die verband houdt met game developers. XNA is echter meer gekend onder de vorm zoals hieronder volgt en wordt dan ook zo behandeld in de rest van het eindwerk. XNA is een set van tools om een game te ontwikkelen. Het bestaat uit drie belangrijke onderdelen: • het XNA Framework; • een aantal tools; • een IDE, namelijk XNA Game Studio. Het is oorspronkelijk ontworpen voor studenten, hobbyisten en beginnende game developers. Vandaar de (betrekkelijke) eenvoud ervan. Betrekkelijke staat tussen haakjes omdat niet alles op één plaats geregeld wordt en dit voor sommige mensen ingewikkeld kan zijn. Het grote voordeel van XNA is dat het de eerste technologie is die het mogelijk maakt om makkelijk te ontwikkelen voor een console. Games voor consoles kun je niet zomaar beginnen schrijven, laat staan erop uitvoeren. Je hebt hiervoor een SDK (Software Development Kit) nodig die je pas krijgt als je toestemming hebt gekregen van de consolefabrikant om games voor zijn platform te ontwikkelen. Niet (meer) dus voor de Xbox 360. Let wel : de Xbox 360 SDK, XDK (Xbox Development Kit) genaamd, bestaat wel nog. Zoals eerder vermeld : XNA is ontworpen met meer onervaren mensen in het achterhoofd. In de toekomst zullen misschien professionele gamebedrijven XNA wel gaan gebruiken al dan niet in samenwerking met andere technologieën. Het ontwikkelen voor de Xbox 360 met XNA heeft wel een prijskaartje. Vooraleer je het game op de Xbox 360 kunt testen en spelen moet je lid zijn van de XNA Creators Club en dit kost $99 voor één jaar en $49 voor 4 maand. Voor een student is er gelukkig wel een oplossing : Microsoft Dreamspark. Microsoft biedt tal van producten gratis aan studenten aan zolang ze maar kunnen bewijzen dat ze student zijn. Voor meer info, surf naar https://downloads.channel8.msdn.com/.
H1 : Wat is XNA ?
1.1 XNA Framework Het hart van het complete XNA gebeuren is het XNA Framework. Dit framework is verantwoordelijk voor het beheren en uitvoeren van de games, bevat een klasse bibliotheek en zorgt voor een stabiele omgeving om de game te laten lopen. Het framework is een API (Application Programming Interface), het bepaalt hoe het ene computerprogramma kan communiceren met het andere computerprogramma. Het XNA Framework is gebaseerd op het krachtige en succesvolle .NET 2.0 framework, die de dag van vandaag voor tal van applicaties op pc gebruikt wordt. Het XNA Framework is dan wel gebaseerd op het .NET 2.0 framework, maar het is weldegelijk helemaal vanaf nul opgebouwd. Het Framework bestaat uit 3 delen: • XNA Graphic Engine : Microsoft.Xna.Framework.dll; • XNA Game Application Model : Microsoft.Xna.Framework.Game.dll; • XNA Content Pipeline : Microsoft.Xna.Framework.Content.dll. Dit zijn alledrie dll’s. Ondanks dat het eerste deel XNA Graphic Engine genoemd wordt, bevat deze alle types en functies die te maken hebben met content. Met content wordt hier bedoeld : afbeeldingen, muziek, 3D models, … Ook types en functies omtrent opslaan van bestanden en input zijn hierin opgeslagen. De XNA Game Application Model bevat dan weer alle types en functies die te maken hebben met het laten lopen van een game. De Content Pipeline wordt uitgelegd in Hoofdstuk 4. Het framework idee is zeer handig om mee te ontwikkelen, maar als je een game aan iemand anders wil geven, heeft die wel de runtime van het framework nodig, anders zal het niet werken.
1.2 Tools Bij het installeren worden ook enkele tools op je pc geplaatst. Deze zijn : • • • •
XNA Game Studio Command Prompt; XNA Framework Remote Performance Monitor for Xbox 360; XACT *; XACT Auditioning Utility *.
* : deze twee tools zijn eigenlijk niet ontwikkeld voor XNA. Deze zijn oorspronkelijk afkomstig uit de XDK (Xbox Development Kit) XNA Game Studio Command Prompt Voor wie lekker old-school wil programmeren, is er de XNA Game Studio Command Prompt. Je kan je games schrijven in een tekstverwerker (bijvoorbeeld Kladblok) en dan via de Command Prompt laten compileren en opstarten. Als je dit doet, moet je echter met heel wat dingen rekening houden en ben je eigenlijk beter om alles te doen via Visual Studio (die doet alles voor jou).
H1 : Wat is XNA ?
XNA Framework Remote Performance Monitor for Xbox 360 Deze tool dient om de performantie te analyseren van een game die op de Xbox 360 draait. Zo kun je onderzoeken of je game goed draait op de Xbox 360 of als het teveel werk vraagt van de hardware. XACT XACT is een programma die gebruikt wordt om geluid in XNA te kunnen gebruiken. Meer uitleg hierover in Hoofdstuk 6. XACT Auditioning Utility Deze kleine tool opent een command prompt venstertje waar je aanvankelijk niks in kunt doen. Wat het precies doet, kan je vinden in Hoofdstuk 6. Wat er wel al kan gezegd worden : het heeft iets te maken met XACT …
1.3 IDE : Game Studio IDE betekent Integrated Development Environment, een software ontwikkel omgeving. Dit is het onderdeel van XNA waar je het meeste van jouw tijd zal doorbrengen. Het is een soort plug-in die Visual Studio uitbreidt om er XNA games mee te kunnen maken. Oorspronkelijk, ten tijde van XNA 1.0, was deze plug-in enkel toepasbaar op Microsoft Visual C# 2005 Express Edition, een gratis, maar meer beperkte versie van Visual Studio 2005. Door de grote vraag van veel mensen om XNA compatibel te maken met Visual Studio, hebben de ontwikkelaars besloten om bij de volgende versie van XNA ervoor te zorgen dat dit ook het geval was. XNA loopt zelfs op een groot aantal versies van Visual Studio 2005 (mits de Service Pack 1 voor Visual Studio geïnstalleerd is). Visual Studio 2008 is ook reeds verschenen, maar de compatibiliteit hiermee zal voor een latere versie van XNA zijn. Visual Studio 2005 is een krachtige programmeeromgeving (IDE), maar niet gratis. Natuurlijk is men niet uit het oog verloren dat XNA oorspronkelijk ook bedoeld was voor studenten en hobbyisten. Het is dus nog steeds mogelijk om te werken met XNA via de gratis IDE Visual C# 2005 Express Edition. XNA Game Studio is jouw visuele contact met het XNA Framework. Hier zal je je gehele project organiseren en alle code schrijven om jouw game te doen werken. Aangezien het een plug-in is voor Visual Studio, biedt het ook de hulpmiddelen die Visual Studio biedt zoals IntelliSense, verschillende kleuren voor stukken code met een specifieke betekenis en natuurlijk de Debug functie. Hiermee kan je je game laten testdraaien. Het game wordt gecompileerd en uitgevoerd. Als er zich problemen voordoen zal Visual Studio deze melden en eventueel zelfs in de code aanduiden.
H1 : Wat is XNA ?
Hoofdstuk 2 : Geschiedenis Vooraleer we aan de slag gaan met XNA is het misschien wel interessant om eens te weten waar XNA eigenlijk vandaan komt. Ten tijde van DOS moesten game programmeurs low-level code schrijven om de hardware aan te spreken. Een moeilijke opdracht, vanwege de mogelijke verschillen in hardware die konden voor komen. Toen kwam Windows 95 op de markt. Dit operating system had een beveiligd geheugen model waardoor programmeurs niet meer rechtstreeks de hardware konden aanspreken en dit kon wel eens performantie problemen opleveren. Als antwoord hierop heeft Microsoft DirectX ontwikkeld. DirectX loste niet enkel het probleem van het beveiligd geheugen op, maar maakte het ook mogelijk om hardware-onafhankelijk te programmeren. Een aantal versies van DirectX zijn ver schenen, met DirectX 9 de laatste voor Windows XP en DirectX 10 de recentste op het ogenblik dat dit eindwerk geschreven wordt. DirectX 9 is de basis geweest voor Managed DirectX, een API die het mogelijk maakte voor .NET programmeurs om de werking van de DirectX structuur zichtbaar te maken en gebruik te maken van de DirectX functionaliteiten. Toen men begon aan de ontwikkeling van XNA heeft men de basis van Managed DirectX gebruikt. XNA is echter niet op Managed DirectX gebouwd, maar wel degelijk van de grond af aan opgebouwd. De eerste vermelding van XNA was op de Game Developers Conferende in 2004. Daarna duurde het echter tot 2006 vooraleer men er meer over vernam. Toen lanceerde Microsoft de Microsoft XNA Build March 2006 CTP. Dit was een complexe tool die het mogelijk maakte om de productie van een programma te beheren. Later dit jaar werd op 30 augustus XNA Game Studio Express beta 1 gereleased. Dit was de eerste keer dat de mensen zich een goed beeld konden vormen van het tot op dit moment duistere begrip XNA. De beta bevatte één starter kit, “Space Wars” en 3D mogelijkheden waren aan de magere kant. Een volgende beta werd vrijgegeven in november 2006 en in december werd dan uiteindelijk het volledige programma gereleased : XNA Game Studio Express. Op 24 april 2007 volgde een update, namelijk XNA Game Studio Express 1.0 Refresh. Op 13 december 2007 werd XNA Game Studio 2.0 losgelaten op het publiek en is op het ogenblik van het schrijven van dit eindwerk de nieuwste versie. Wel is er reeds een melding van het verschijnen van XNA 3.0. Tijdens het verschijnen van 1.0 was er ook sprake dat er een professional versie van XNA op de markt zou komen. De professional versie is uiteindelijk niet verschenen. Als je wil kan je eventueel stellen dat naargelang je Visual C# 2005 Express Edition of Visual Studio 2005 gebruikt, je in het eerste geval bezig bent met de niet-professionele versie en in het tweede geval met de professional versie van XNA 2.0. De belangrijkste vernieuwingen die XNA 2.0 met zich meebracht zijn : • mogelijkheid om met Visual Studio 2005 te werken in plaats van enkel Visual C# 2005 Express Edition • Cross-Platform Game Project Converter : zet je spellen op een eenvoudige wijze om van Windows versie naar Xbox 360 versie en omgekeerd. De Xbox 360 versie ondersteund sommige PC zaken niet zoals keyboard en muis. Deze moeten handmatig aangepast worden.
H2 : Geschiedenis
• netwerkmogelijkheden : niet alleen per platform, ook cross-platform mogelijk, namelijk tussen Windows en Xbox 360. Bij het ontwikkelen voor Xbox 360 heb je wel een Xbox LIVE lidmaatschap en een Creators Club lidmaatschap nodig. • verschillende gamepad types : de gamepad types zijn aangevuld. Het is mogelijk om input te verwerken van andere type controllers. Wat de toekomst voor XNA zal brengen : XNA 3.0 • Zune functionaliteit : je zal de mogelijkheid krijgen om games te maken voor de Zune. Dit kunnen enkel 2D games zijn. Twee minpunten echter : het scherm is redelijk klein en de Zune is nog niet verkrijgbaar in Europa. • Visual Studio 2008 : enkel Visual Studio 2008 en Visual C# 2008 Express Edition zullen ondersteund zijn. Jammer voor de mensen die Visual Studio 2005 hebben … zij moeten dan met de beperkte Visual C# 2008 Express edition werken. Xbox LIVE Community Games Je zal de mogelijkheid krijgen om je games voor de Xbox 360 online te plaatsen. Natuurlijk heb je wel een XNA Creators Club inschrijving nodig. Niet elke game zal echter online beschikbaar komen. De games worden eerst goed onderzocht door mensen die ingeschreven zijn bij de XNA Creators club. Dit wordt gedaan om er zeker van te zijn dat het game degelijk opgebouwd is en dat er geen destruc tieve code aanwezig is die schade aan de Xbox 360 zou toebrengen. Hun mening zal bepalen of het game geslaagd is om online te worden geplaatst en indien dit het geval is, zal het in de Xbox LIVE Marketplace beschikbaar worden voor iedereen over de hele wereld.
H2 : Geschiedenis
10
Hoofdstuk 3 : XNA basic file Tijd om eens een kijkje in XNA Game Studio te nemen en de eerste “game” te schrijven. Ga naar Start > Programma’s > Microsoft XNA Game Studio 2.0 (standaard installatie locatie) en open in deze map XNA Game Studio. XNA Game Studio staat echter niet in deze map, althans, zo lijkt het op het eerste zicht. Herinner je je dat in Hoofdstuk 1 vermeld werd dat XNA eigenlijk een plug-in is voor Visual Studio ? Wel, dan zal je wel snappen dat je hier Microsoft Visual Studio 2005 (of Microsoft Visual C# 2005 Express Edition bij gebrek aan het vorige) moet openen. Nu krijg je de startpagina van Visual Studio voorgeschoteld, indien deze niet uitgeschakeld is. Voor mensen die reeds met Visual Studio gewerkt hebben zal je merken dat je eigenlijk geen verschil ziet met de “gewone” Visual Studio. Wat een tegenvaller, niet ? En op zich is het ook niks anders dan de doodgewone Visual Studio. Maar door de installatie van XNA heb je nu wel een aantal nieuwe opties gekregen. Ga naar File > New > Project. Ervaren Visual Studio gebruikers zullen merken dat er nu een nieuwe node beschikbaar is onder Visual C#, namelijk XNA Game Studio 2.0. Klik hierop om de mogelijk heden te zien. Om te starten selecteer je hier Windows Game (2.0). Geef dan de naam MijnEerste XNAGame in en kies de locatie waar je de game wil opslaan. Als je Create directory for solution aanvinkt zal de solution file opgeslagen worden ter hoogte van de map die alle gegevens van het spel bevat. Vink je dit af, dan komt de solution file in het toplevel van je spel terecht. Het is aan te raden om Create directory for solution aan te vinken, kwestie van wat meer structuur te hebben. Als je nieuwe game nu geopend is, kijk eerst eens naar de Solution Explorer (zie Figuur 3.1). Hier zie je de structuur van je game. Properties en references zijn voor advanced users en zullen hier niet besproken worden. De inhoud per Visual Studio project kan verschillen, maar de betekenissen zijn gelijk voor alle Visual Studio projecten. De eerste map die je ziet staan is Con tent. Deze map zal gebruikt worden om alle content van je game in te bewaren. In deze map zit ook reeds een map references, die hier dus niet besproken zal worden. Het is aangeraden om in de Figuur 3.1 - Solution Explorer toekomst per type content een map aan te maken. Je hoeft dit natuurlijk niet te doen, maar dan wordt het al vlug een onoverzichtelijk zootje. Het volgende belangrijke item is Game.ico. Dit is standaard een afbeelding van een Xbox 360 controller. Het heeft tot doel om later als icoon voor de .exe file gebruikt te worden en zal ook linksboven in je window verschijnen. Als je dit wil vervangen door een eigen gemaakt icoon, benoem je jouw eigen icoon Game.ico. Laat deze de standaard Game.ico overschrijven en klaar is kees, jouw icoon zal nu gebruikt worden in plaats van het standaard icoon.
H3 : XNA Basic File
11
Laten we eerst overgaan naar GameThumbnail.png. Deze afbeelding wordt gebruikt op de Xbox 360 om jouw spel te vertegenwoordigen in het menu. Om jouw eigen afbeelding te gebruiken doe je juist hetzelfde als bij Game.ico. Game1.cs en Program.cs zijn beide Visual C# bronbestanden. Ze bevatten de code die de game opbouwt en uitvoert. using System;
Laten we eerst een kijkje nemen in Program. cs (zie Code 3.1). Het bovenste stukje code namespace MijnEersteXNAGame using System; dient om de compiler te { tonen waar hij klassen kan vinden die in dit static class Program { programma gebruikt worden. In dit geval kan /// <summary> hij deze vinden in de System namespace. Een /// The main entry point for namespace is verzameling van klassen. /// the application. Namespace MijnEersteXNAGame is dus /// static void Main(string[] args) de verzamelnaam voor alle klassen die je zal { gebruiken voor je game. XNA maakt deze auusing (Game1 game = new Game1()) tomatisch voor jou aan wanneer je een nieuw { project start en plaatst de namespace bij elke game.Run(); } nieuwe klasse die je aanmaakt. } De klasse Program is statisch, wat wil zeg} gen dat ze geen instantie nodig heeft om te } kunnen werken. Ze bevat 1 methode, namelijk Code 3.1 - Program.cs Main. Deze is ook statisch, wat logisch is aangezien je een statische klasse hebt en je dus geen instantie kunt gebruiken om de methode Main uit te voeren. Deze methode is de standaard methode die het startpunt vormt van je programma. Dus als je het programma opstart, zal Main uitgevoerd worden. De code van deze methode maakt hier jouw game aan en start het op. Het aanmaken van de game maakt gebruik van een using statement. Dit zorgt ervoor dat het object dat je aangemaakt hebt (hier game van het type Game1) zeker opgeruimd wordt na het beëindigen van de methode en zelfs als er zich een exception (een fout) zou voordoen. Als dit niet het geval zou zijn, is er kans dat game nog blijft bestaan, maar niet meer volledig functioneel is. Game1 game = new Game1() maakt een nieuwe instantie aan van de klasse Game1 en game.Run(); start het spel op. Normaalgezien is het niet nodig om in dit bestand wijzigingen aan te brengen. Verwijder het ook zeker niet, want anders zal je spel niet werken omdat het niet eens opgestart kan worden. Game1.cs is uiteindelijk de code voor jouw game. Program.cs zorgt er enkel voor dat jouw game opgestart kan worden, Game1.cs IS jouw game. In theorie kan je alle code in dit ene bestand plaatsen, maar praktisch gezien zal je de code verspreiden over verschillenden klassen. Om de code hier makkelijk te kunnen bespreken, zal het bestand in stukjes weergegeven worden. Zie blijage B. Basic File Game1.cs voor de volledige code. Zoals je ziet bevat Game1.cs veel meer using directives dan Program.cs. Je moet immers verwijzen naar alle XNA namespaces om gebruik te kunnen maken van de XNA klassen. Game1 behoort zelf tot de MijnEersteXNAGame klasse, waar Program ook toe behoort. Een handige tip : aangezien het menselijk oog het gevoeligst is voor groene kleuren, springen deze het meest in het zicht. Dit kan storen en ervoor zorgen dat je code onoverzichtelijk wordt.
H3 : XNA Basic File
12
Om dit storende effect weg te nemen, kun je de summaries inklappen door op het kruisje voor de regel te klikken of kan je de kleur van de commentaar veranderen. Dit doe je via Tools > Options > Fonts and Colors. Selecteer in Show settings for : Text Editor. Ga in Display Items : naar XML Doc Comment en dan kan ja via Item foreground en Item background zelf de kleur aanpassen. Summaries zullen hier niet uitgelegd worden, ze spreken voor zich. De klasse hier is Game1, die erft van de klasse Game. Deze bevat basisfuncties en variabelen. Ze is opgesteld om het ons makkelijk te maken zodat we basiszaken niet meer zelf hoeven te programmeren. ... De klasse bevat 2 instantievariabelen (varia } belen eigen aan een klasse), namelijk de Code 3.2 - Klasse Game GraphicsDeviceManager graphics en de SpriteBatch spriteBatch. Graphics is een variabele die de instellingen van de grafische kaart bevat en diens beheer doet specifiek voor deze game. De grafische kaart is een zeer belangrijk element voor games. Deze heeft er immers voor gezorgd dat de games er steeds realistischer uitzien / zullen uitzien. Vandaar dat deze variabele in het standaardbestand voorkomt. De volgende variabele is spriteBatch. Deze heeft ook een grafisch doel. Ze dient om een aantal 2D objecten, in de gamewereld sprites genoemd, te tekenen op het scherm. De sprites die tot één SpriteBatch behoren worden allemaal volgens dezelfde instellingen getekend. Meer over SpriteBatch in hoofdstuk 5. public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch;
Na de variabelen vind je de constructor Game1 die het mogelijk maakt dat je een instantie van je game kunt maken en je game kunt laten draaien. Hier gebeuren twee zaken. Ten eerste wordt graphics geïnitialiseerd. Om Code 3.3 - Constructor van de klasse Game een nieuwe GraphicsDeviceManager te maken moet je een game als parameter meegeven. Aangezien deze GraphicsDeviceMana ger tot deze game behoort, maak je gebruik van het keyword this. Dit verwijst naar de Game1 klasse zelf, die door overerving een Game met aanvullingen is en dus kan gebruikt worden als parameter. Content is een variabele die zich standaard in de klasse Game bevindt. Hij is van het type ContentManager, een klasse die dus instaat voor het beheren van alle content. Via Content. Rootdirectory stel je de locatie in waar de ContentManager de content terug kan vinden. Hier dus de map Content. Je hoeft enkel Content in te vullen omdat XNA sowieso paden zal bepalen vanaf de root. De root is je WindowsGame, of anders gezegd, het niveau waarin je Game1.cs en Program. cs terugvindt. Sinds XNA 2.0 krijg je de map Content standaard als je een nieuwe game maakt. Vóór XNA 2.0 moest je zelf deze map aanmaken. public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = “Content”; }
Game1 bevat 5 methodes die elk instaan voor een aspect tijdens het lopen van de game. Zie Figuur 3.2 voor een overzicht van de methodes in functie van de duur van het spel.
H3 : XNA Basic File
13
gameloop
LoadContent()
Start gameloop
Start game
Update(...) Draw(...)
Initialize()
Stop game
Stop gameloop
Sluit game
UnloadContent()
Figuur 3.2 - Wanneer wordt welke methode uitgevoerd ? protected override void Initialize() { base.Initialize(); }
Als eerste hebben we Initialize(). Deze functie wordt uitgevoerd vooraleer alle andere onderstaande functies uitgevoerd worden. Code 3.4 - Initialize() Deze functie wordt uitgevoerd vooraleer de game zichtbaar wordt. Hier kun je alle gegevens initializeren die niet grafisch zijn, bijvoorbeeld een variabele die het aantal vijanden aanduidt de waarde 5 geven. Ook worden hier de nodige game services geladen. Game services zijn een mechanisme om koppelingen tussen objecten te behouden die met elkaar moeten communiceren. In Initialize() staat al een stuk code die zegt dat hij na het uitvoeren van bovenstaande code de methode Initialize() van de klasse Game moet uitvoeren. base slaat op de klasse waarvan een andere klasse erft en is in dit geval dus Game. De volgende functie is LoadContent(). Hier wordt alle grafische content geladen. Daarom wordt de SpriteBatch spriteBatch hier geladen. Om een nieuwe SpriteBatch Code 3.5 - Loadcontent() te initializeren moet je aan de constructor als parameter de grafische kaart meegeven. De verwijzing hiernaar bevindt zich in de variabele GraphicsDevice van de klasse Game. Opmerking : in de commentaar onder de initialisatie van spriteBatch wordt vermeld dat je om te verwijzen naar content gebruik kun maken van this. Content, maar Content alleen werkt even goed.
protected override void LoadContent() { spriteBatch = new SpriteBatch (GraphicsDevice); }
protected override void UnloadContent(){ }
Code 3.6 - Unloadcontent() De derde methode UnloadContent() wordt enkel gebruikt als je zaken geladen hebt zonder de hulp van de ContentManager Content. Zaken geladen met Content worden door Content zelf ontladen in geval van afsluiten van je game. protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex. One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); }
base.Update(gameTime);
Code 3.7 - Update(GameTime gameTime)
Als voorlaatste hebben we Update(GameTime gameTime). Update wordt gebruikt om alles te controleren en te verwerken wat niet grafisch is. Dit kan game logica zijn, controleren op input, afspelen van audio of een combinatie van alles te samen. Update heeft een parameter gameTime nodig. Deze parameter wordt via de overgeërfde methode Run()van Game ingevuld.
H3 : XNA Basic File
14
Update bevat al één controle die kijkt of de Back toets op de Xbox 360 controller ingedrukt wordt. Is dit het geval, dan wordt het spel afgesloten (= this.Exit). Na alle code die je in deze methode stopt wordt nog de Update methode van Game uitgevoerd die op zich ook de paramater game Time nodig heeft. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice. Clear(Color.CornflowerBlue);
Als vijfde en laatste methode hebben we Draw(GameTime gameTime) die instaat voor het updaten van alle grafische content. Ze heeft ook een parameter gameTime nodig die dezelfde is als de parameter gameTime in base.Draw(gameTime); Update. De code die reeds ingevuld is, zorgt } ervoor dat de grafische kaart toch al iets op het Code 3.8 - Draw(GameTime gameTime) scherm tekent. Het vult het scherm met een blauwe kleur. Hier zal je alles laten tekenen door de grafische kaart zoals tekst, sprites, 3D models, user interfaces, etc. Ook hier wordt na de eigen code de Draw methode van Game uitgevoerd. Update en Draw worden in verschillende tijdsintervallen aangeroepen, afhankelijk of de eigenschap IsFixedTimeStep van de klasse Game false of true is. Als IsFixedTimeStep false is, zullen Update en Draw zoveel mogelijk aangeroepen worden, maar enkel in fullscreen mode. Als IsFixedTimeStep true is, zal Update aangeroepen worden met een interval gelijk aan de variabele TargetElapsedTime van de klasse Game. Draw zal dan zoveel keer aangeroepen worden als mogelijk is. Dit wil zeggen dat Update sowieso uitgevoerd wordt, maar Draw soms niet. Standaard heeft een game een IsFixedTimeStep die true is. TargetElapsedTime heeft dan een waarde van 1/60 van een seconde. Dit getal zou ervoor moeten zorgen dat je spel de methodes Update en Draw dan 60 keer per seconde doorloopt, wat dan 60 complete beelden per seconde oplevert. Deze 60 beelden per seconde kunnen aangeduid worden met 60 fps (frames per seconde), wat een ideale framerate is voor games om ze vloeiend te laten lijken. Het komt veel voor dat de framerate echter minder dan 60 bedraagt of even onder 60 valt, zodat het kan zijn dat je beeld hapert of zelfs pauzeert en dan plots verspringt. Draw wordt hier dan aan een trager tempo uitgevoerd, maar Update niet. Dit is de reden waarom er een verspringing van het beeld kan optreden. De game logica wordt nog aan 60 keer per seconde uitgevoerd, maar Draw niet meer, zodat je beelden niet meer op elkaar aansluiten. De keren dat Draw niet uitgevoerd wordt, zorgt ervoor dat er beelden wegvallen. Het “pauzeren” is in feite je laatst getekende beeld die zichtbaar blijft omdat Draw over een langere periode niet uitgevoerd wordt. Deze code zorgt er voor dat je nu reeds een game hebt. Ook al kun je nu niet zo veel doen, je kunt de game opstarten via F5 of de groene pijl (Start Debugging) bovenaan in de taakbalk. Je krijgt een scherm te zien met als achtergrond de kleur die je ingegeven hebt bij graphics.Graphics Device.Clear() (zie Figuur 3.3). Indien je een Xbox 360 controller hebt die je kunt aansluiten op je computer, kun je het spel afsluiten door op de Back toets te drukken. Het is altijd een goede gewoonte om vanaf het begin ook de functionaliteit voor het afsluiten met de Escape toets te voorzien (Zie Code 3.9). Later kun je deze altijd nog wijzigen, maar het zal vlugger werken om het venster af te sluiten met Escape in plaats van met de muis. Alt + F4 is natuurlijk ook een mooi alternatief.
H3 : XNA Basic File
15
Figuur 3.3 - MijnEersteXNAGame if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) { this.Exit(); }
Code 3.9 - Game ook stoppen door op Esc te drukken
H3 : XNA Basic File
16
Hoofdstuk 4 : Content Pipeline Als men een game zonder XNA maakt, moet men voor alle content een laadfunctie schrijven. Dit houdt in dat er per type bestand een importer en een processor nodig is. De importer zegt tegen de computer hoe hij een bepaald bestand moet lezen. De processor zegt tegen de computer hoe hij het verkregen bestand nu moet verwerken voor verder gebruik en zet deze om in objecten die bruikbaar zijn om te programmeren. Vanwege de XNA content pipeline hoef je dit niet meer te doen voor een aantal bestandstypes. Er zijn een aantal bestandtypes vastgelegd bij het ontwikkelen van XNA, die door de content pipeline verwerkt kunnen worden. Maar natuurlijk zijn er een groot deel niet ondersteund en voor deze zal je jammergenoeg dus zelf nog een importer en processor moeten schrijven. De content pipeline verwerkt alles wat hij herkent. Via de properties van een ondersteund content bestandstype kun je zien wat de content pipeline gebruikt om het bestand te verwerken. Voeg in MijnEersteXNAGame een afbeelding van het type .jpg toe. Ga in de solution explorer naar de map Content, rechtermuisklik, Add > Existing Item. Browse naar een .jpg bestand en klik Add. Jouw afbeelding verschijnt nu in de map Content in de solution explorer. Om nu de properties van dit bestand te zien heb je 3 mogelijkheden : het Properties Window staat al open, je kunt het Properties Window openen via View > Properties Window of je kunt het Properties Window openen via rechtermuisklik op het bestand, helemaal onderaan Properties. Onder het deel XNA Framework Content Pipeline (zie Figuur 4.1) vind je drie properties die belangrijk zijn voor de content pipeline. Als eerst hebben we Asset Name. De Asset Name is gelijk aan de naam van het bestand zonder de bestandsextensie. Deze benaming wordt gebruikt om in de code de content te kunnen bereiken. Vervolgens heb je de Content Importer die zoals je kunt zien op het einde van de propertie waarde XNA Framework bevat. Dit toont aan dat de importer beschreven is in het XNA Framework. Over de Content Processor valt hetzelfde te zeggen. Als je zowel bij Content Importer, als bij Content Processor de Figuur 4.1 - Content Pipeline Properties dropdownlijst opent, zie je dat er dus een beperkt aantal importers en processors beschreven zijn, maar dat ze wel allemaal tot het XNA Framework behoren. Bij de Content Processor heb je zelfs de mogelijkheid om geen processor te gebruiken, waardoor logischerwijs de content dan ook niet verwerkt zal worden. Meestal moet men aan deze properties geen wijzigingen aanbrengen.
H4 : Content Pipeline
17
De content pipeline is ook slim genoeg om alle bestanden die met elkaar verbonden zijn te vinden tijdens het verwerken ervan. Een voorbeeld hierbij is een 3D model die een texture en een shader nodig heeft om het bedoelde uitzicht te krijgen. Dit zijn drie files die afzonderlijk in de Content map geplaatst zullen worden, maar die tijdens het draaien van het spel aan elkaar gelinkt worden. Als je een spel uitvoert, wordt alle content door de content pipeline omgezet naar een binair formaat, namelijk .xnb (audio gebruikt andere binaire formaten). Deze files kun je op volgende wijze zelf zien staan : ga eerst naar de root van je game, dit is hetzelfde niveau die je ziet in de solution explorer. In mijn geval is dit Schijfletter:\…\MijnEersteXNAGame\MijnEersteXNAGame\. Indien je bij het maken van je project Create Directory For Solution niet aangevinkt hebt, is het pad Schijfletter:\…\ MijnEersteXNAGame\. De solution staat in dit geval in je root. In beide gevallen, ga naar de map bin, dan x86, vervolgens Debug en tenslotte Content. Niet spectaculair, maar zo kan je met eigen ogen zien dat het spel niet de oorspronkelijke bestandstypes van de bestanden gebruikt tijdens het draaien ervan. Belangrijke opmerking : indien je het spel nog nooit hebt gedebuged en met andere woorden nog nooit hebt laten opbouwen, zal Debug een lege map zijn. De content pipeline bestaat uit 5 dll’s : • Microsoft.Xna.Framework.Content.Pipeline : basisfunctionaliteiten content pipeline • Microsoft.Xna.Framework.Content.Pipeline.EffectImporter : hoe omgaan met effecten (shaders) • Microsoft.Xna.Framework.Content.Pipeline.FbxImporter : hoe omgaan met .fbx bestanden • Microsoft.Xna.Framework.Content.Pipeline.TextureImporter : hoe omgaan met 2D bestanden • Microsoft.Xna.Framework.Content.Pipeline.XImporter : hoe omgaan met .x bestanden De bestandstypes die ondersteund worden zijn de volgende : • • • •
Textures : .dds, .jpg, .png, .bmp, . tga Geluid : .xap 3D models : .fbx, .x Shaders : .fx
Zoals je gezien hebt is het heel eenvoudig om met de ondersteunde formaten te werken, maar als je afwijkt hiervan moet je toch alles zelf programmeren om een bestandtype te kunnen gebruiken. Hopelijk worden meer types beschikbaar in de toekomst.
H4 : Content Pipeline
18
Hoofdstuk 5 : Grafische Mogelijkheden Vooraleer je een spel kunt programmeren moet je natuurlijk een manier vinden om de code visueel voor te stellen. De allereerste games toonden visueel de game-elementen via grote groepen pixels die een sterk vereenvoudigde voorstelling van bijvoorbeeld een auto, mens of dier waren. Gelukkig heeft de technologie niet stil gestaan en werden de groepen pixels fijner en fijner waardoor er meer detail in de figuren gestoken kon worden. Al deze figuurtjes waren vlak, maar via slim kleurgebruik kon men een gevoel van diepte creëren die in het prille begin niet mogelijk was. Dit gevoel van diepte werd vanaf het 64-bit tijdperk heel realistisch via de driedimensionale objecten die nu gebruikt konden worden. Deze 3D modellen waren op het moment van verschijnen heel revolutionair. Door het realisme die 3D modellen de dag van vandaag uitstralen, kunnen de eerste echte 3D figuren beschouwd worden als de eerste pixel figuren : een abstracte voorstelling van iets. Zoals je dus bij de eerste pixel beelden grote, afzonderlijk waarneembare pixels had, had je hier grote, afzonderlijk waarneembare polygonen.
5.1 2D Er is natuurlijk een reden waarom eerst de 2 dimensionale beelden gebruikt zijn : 3D objecten vergen een complexere methode om weer te geven op het scherm en om ze te verplaatsten en bewegen. In XNA is dit ook het geval, dus zullen we eerst de makkelijkere 2de dimensie beschouwen. Op zich worden de 2D beelden in XNA getekend in de 3de dimensie. Maar omdat je alle tekeningen op een zelfde vlak plaatst, ziet het eruit alsof je in 2D aan het werken bent. De reden waarom je in bovenstaande delen nergens afbeeldingen of sprites hebt zien staan, komt dus omdat je in 3D werkt. Afbeeldingen in de 3de dimensie worden benoemd als textures.
5.1.1 Afbeeldingen Laten we een afbeelding op het scherm tekenen. Hiervoor kan je MijnEersteXNAGame gebruiken, aangezien je in het vorige hoofdstuk een afbeelding aan de Content hebt toegevoegd. De volledige code kan je terugvinden in bijlage C. Afbeeldingen & Tekst. Om een afbeelding op het scherm te kunnen tekenen zullen we gebruik maken van de sprite Batch die standaard aanwezig is bij het aanmaken van een nieuwe game. Declareer onder SpriteBatch spriteBatch een nieuwe variabele Texture2D achter grond. Zoals je merkt is een afbeelding hier dus van het type Texture2D waar ze evengoed een klasse image of sprite hadden kunnen gebruiken, maar aangezien je in 3D werkt, noemt het dus een Texture2D. Ga naar de methode LoadContent(). Hier zullen we de afbeelding laden en in de variabele achtergrond stoppen.
H5 : Grafische Mogelijkheden
19
Laten we de code eens ontleden om te begrijpen wat er zal gebeuren bij het uitvoeren ervan. Het eerste stuk, Content, spreekt voor zich. Dit is de ContentManager die een klasse Game standaard in zich heeft om de content te beheren (zie hoofdstuk 3). Load
(“XnaLogo 3D”) is een functie die twee parameters nodig heeft. Het deel tussen de groter dan - kleinder dan tekens duidt het type bestand aan dat geladen moet worden. In ons geval is het een afbeelding, dus een Texture2D. Het stuk tussen de ronde haken duidt de locatie aan van het bestand dat je wil laden. Omdat de RootDirectory van Content standaard ingesteld wordt op de Content map, is het pad hier niets meer dan de Asset Name van het bestand. Let op : de Asset Name, niet de naam van het bestand zelf en zijn extensie. De Asset Name is een content pipeline property (zie hoofdstuk 4). Indien je de afbeelding in een onderliggende map van Content hebt staan, moet je natuurlijk deze map vermelden en dan de Asset Name, dus zo : “mapNaam\\Asset Name”. Hier staan 2 backslashes omdat een backslash een gereserveerd teken is in C#, maar het betekent hetzelfde als één enkele backslash. De afbeelding is nu geladen in de variabele achtergrond, tijd om de afbeelding op het scherm te tekenen. Hiervoor ga je gebruik maken van spriteBatch. Vanzelfsprekend wordt dit gedaan in de methode Draw(GameTime gameTime). Vooraleer je kan beginnen met tekenen moet je eerst nog zeggen aan de grafische kaart dat hij een 2 dimensionale afbeelding zal moeten tekenen. Een grafische kaart staat namelijk standaard klaar om 3 dimensionale afbeeldingen te tekenen. Het aankondigen gebeurt door spriteBatch.Begin();. Nu kan je de afbeelding tekenen via sprite Batch.Draw(achtergrond, new Vector2 (0, 0), Color.White);. Een Draw methode van de klasse SpriteBatch heeft in totaal 7 overloads, wat wil zeggen dat je op 7 verschillende manieren parameters kunt ingeven. De makkelijkste methode is via de gebruikte overload in het voorbeeld. Zoals je ziet is de eerste parameter een Texture2D, hier achtergrond. De tweede parameter is een 2 dimensionale vector, vertegenwoordigd door de klasse Vector2. Deze vector bepaalt waar de linkerbovenhoek van de afbeelding zal komen. In ons geval is dit op de locatie (0, 0), dus linksboven in het gamewindow. Er wordt geen aparte variabele gemaakt voor de vector, maar op zich kan je dit wel doen als de locatie meerdere malen gebruikt moet worden. Hier wordt een instantie rechtstreeks in de parameter aangemaakt. Indien de locatie zal moeten veranderen, bijvoorbeeld om de afbeelding van links naar rechts te laten bewegen, dan moet je wel gebruik maken van een variabele. Een alternatief voor new Vector2(0, 0) is Vector2.Zero. Het stukje Color.White is een mengoptie. Als je hier wit ingeeft krijg je de normale afbeelding. Als je hier een andere kleur opgeeft krijg je een mix van die kleur met de originele afbeelding. De eerste overload die tevoorschijn komt bij spriteBatch.Draw() is via een rechthoek die je moet bepalen. Je kunt met gemak hetzelfde resultaat bekomen als met de Vector2, maar hier moet je wel nog de breedte en hoogte opgeven. Als deze niet dezelfde zijn als van de gebruikte afbeelding, zal deze laatste vervormd worden. In plaatst van new Vector2 kun je hier new Rec tangle(0, 0, achtergrond.Width, achtergrond.Height) invullen. De eerste twee parameters zijn de locatie van de linkerbovenhoek, de volgende twee de breedte en de hoogte. Zo krijg je hetzelfde resultaat als met Vector2. Het spreekt voor zich, als je de afbeelding op zijn normale grootte wil laten zien, je beter kiest voor Vector2 dan voor Rectangle. Het enige wat je nu nog moet doen is de grafische kaart zeggen dat je klaar bent met tekenen in 2D en dit doe je als volgt : spriteBatch.End();.
H5 : Grafische Mogelijkheden
20
Ziezo, druk op Debug of F5 en daar is jouw afbeelding, met zijn linkerbovenhoek in de bovenhoek van het gamewindow.
5.1.2 Tekst XNA ondersteunt geen fonts. Wil je tekst op het scherm, dan moet je gebruik maken van een texture die dan alle mogelijke tekens van dat font bevat. Je moet zelf de tekens in een matrixvorm plaatsen, alle locaties van elke letter ingeven en per toets op het toetstenbord die je wil gebruiken, instellen met welke locatie in de matrix deze toets overeenkomt. Maak je geen zorgen, dit was vroeger zo. Nu is er een veel eenvoudiger en minder tijdrovende manier. Sinds XNA 1.0 Refresh heeft men een nieuw type aan het XNA Framework toegevoegd, namelijk de SpriteFont. Dit zorgt ervoor dat een door jou gekozen font die op je computer staat, in een texture geplaatst wordt zoals hierboven beschreven is. Het is op zich nog steeds omslachtig, maar nu doet de computer al het werk. Het plaatsen van tekst op het scherm maakt ook gebruik van spriteBatch, maar gebruikt een andere methode. Maak een SpriteFont door op de map Content te staan, rechtermuis, Add > New Item … Daar zie je Sprite Font staan. Klik op het icoon (zie links) en geef een naam in voor je SpriteFont. In het voorbeeld zullen we de standaard naam gebruiken, namelijk SpriteFont1. Klik op Add. In de Content wordt de SpriteFont toegevoegd en de SpriteFont opent vanzelf. Zoals je kunt zien is een SpriteFont bestand eigenlijk een XML bestand. De gegevens die je hier invult bepalen bij het uit voeren van je game hoe de font texture er zal uitzien. Als eerste label heb je FontName. Hier vul je de naam in van het lettertype die je wil gebruiken. Het volgende label Size, bepaalt de corps van het font. Het derde label Spacing bepaalt de letterspatiëring. UseKerning bepaalt of er kerning moet gebeuren. Het verschil tussen spacing en kerning is als volgt : spacing gebeurt tussen alle letters, kerning gebeurt enkel als er een grote witruimte zou ontstaan tussen 2 letters. Dé voorbeeldletters voor kerning zijn de A en V. Indien er geen correctie (de kerning) toegepast zou worden, krijg je een optisch grotere witruimte tussen beide letters. Het voorlaatste label Style bepaalt of een letter in romein, italic, bold of italic-bold moet weerge geven worden. Tenslotte is er nog het label CharacterRegions die bepaalt welke tekens er in de texture opgenomen moeten worden. Dit wordt gedaan via hexadecimale waardes van ASCII-code die het begin en het einde uit de ASCII-code lijst aangeven. Door deze waarden aan te passen kun je tekens toevoegen of uitsluiten. Het is wel jammer dat er geen mogelijkheid is om de regelafstand aan te passen. XNA zal altijd de standaard 120% van de corpsgrootte als regelafstand gebruiken. Als je alles naar wens ingevuld hebt, sla je de SpriteFont op en keer je terug naar Game1.cs. Bovenaan declareer je onder de variabele afbeelding, spriteFont. In loadContent() initialiseer je spriteFont. Dit doe je terug via de ContentManager Content. Hier is het type tussen de groter dan - kleiner dan tekens echter SpriteFont. Het laten tekenen van de tekst doe je terug in de Draw() methode. Tekst wordt niet via de Draw() methode van de klasse Sprite Batch getekend, maar wel via de DrawString() methode.
H5 : Grafische Mogelijkheden
21
Als eerste parameter geef je de variabele spriteFont in. De daarop volgende parameter wordt de tekst die op het scherm weergegeven zal worden. Parameter nummer 3 verlangt een Vector2, die de locatie van de linkerbovenhoek van de tekst bepaalt. De vierde parameter voor Color staat hier in voor de kleur die de tekst zal krijgen. Als je tekst in een vast vierkant wil plaatsen, zal je een methode moeten schrijven die het maximum aantal karakters op één lijn plaats en de volgende op de lijn daaronder. Zie de statische klasse Tekst opsplitsen.cs in Code > H5 > MijnEersteXNAGame > MijnEersteXNAGame. De volledige code om tekst op het scherm te plaatsen is terug te vinden in bijlage C. Afbeeldingen & tekst.
5.2 3D Vooraleer je een 3D model op het scherm plaatst, hebben we eerst wat theorie in verband met 3D nodig.
5.2.1 Assenstelsel Een 3 dimensionale ruimte heeft 3 assen : de X-, de Y- en de Z-as. We weten uit de 2de dimensie dat de X- en Y-as loodrecht op elkaar staan. De oriëntatie van de X-as is naar rechts en de oriëntatie van de Y-as is naar boven. De 3de dimensie is een uitbreiding van de 2de, er wordt een derde as, de Z-as, aan toegevoegd. Nu moet deze Z-as loodrecht staan op zowel de X-as als de Y-as, met andere woorden loodrecht op het XY-vlak. Dit kan gebeuren op twee methodes : via een rechtshandig coördinaten stelsel of een linkshandig coördinatenstelsel. Als je je duim volgens de X-as oriënteert en je wijsvinger volgens de Y-as, dan bepaalt de middelvinger de Z-as. Zie Figuur 5.1. XNA werkt met het rechtshandig coördinatensysteem. Als je echter een 3D model zal invoegen die gebruik maakt van het linkshandig coördinatensysteem, zal het model gespiegeld worden ten opzichte van het XY-vlak. Geen probleem bij symmetrische modellen, maar als je met niet-symmetrische modellen werkt, moet je ervan bewust zijn.
Y
Y
+
+
-
-
+ Z +
-
+
X + Z
X -
-
-
Rechtshandig
Linkshandig
Figuur 5.1 - Links- en rechtshandig coördinatenstelsel
H5 : Grafische Mogelijkheden
22
5.2.2 3D en matrices Om wat beter te begrijpen waar de matrices bij de 3de dimensie vandaan komen, volgt hier een korte, vereenvoudigde uitleg. De oorsprong ervan is logisch als je het puur wiskundig nagaat, maar dit zou het voor sommige mensen misschien te complex kunnen maken. Toch wordt het één en ander via wiskunde uitgelegd. Een reële vectorruimte Rn is een verzameling van geordende n-tallen. Een n-tal is een verzameling van getallen die elk tot de reële getallen R behoren en is van de vorm (a1, a2, … an). Met elk n-tal komt een punt in de n-dimensionale ruimte overeen, wat vaak aangeduid wordt als een vector van de vectorruimte. De 3 dimensionale ruimte is dus een reële vectorruimte met n = 3 (R3). Een 3-tal hier is van de vorm (a1, a2, a3). Dit 3-tal komt overeen met een punt die aangeduid wordt door een vector. De 3 getallen in het drietal zijn de coördinaten van een punt ten opzichte van een basis. In XNA maakt men gebruik van de plaatsvectoren van punten : dit zijn gebonden vectoren met als beginpunt de oorsprong. Deze vectoren zullen dan gebruikt worden om berekeningen te doen om een punt te verplaatsten. Lineaire afbeelding = “Een afbeelding van Rn naar Rn als en slechts als het beeld van een lineaire combinatie van vectoren gelijk is aan de lineaire combinatie van de beelden.” Nachtegael, Buysse (2001 : 48). Bovenstaande definitie is enkel vermeld voor de volledigheid. Neem gewoon aan dat een afbeelding van R3 naar R3 (van 3D naar 3D) en een afbeelding van R3 naar R2 (van 3D naar 2D) voldoet aan deze definitie en dat ze dus lineaire afbeeldingen zijn. Het resultaat van een lineaire afbeelding noemt men een beeld. Een lineaire afbeelding wordt een transformatie genoemd als het origineel en het beeld tot dezelfde vectorruimte behoren. Een lineaire afbeelding van 3D naar 3D is dus een transformatie. Een lineaire afbeelding van 3D naar 2D is dit echter niet. Om het beeld van een vector P te berekenen in het geval van een lineaire afbeelding van 3D naar 2D ziet de formule er als volgt uit :
A
B
C
A: De coördinaten van P ten opzichte van de nieuwe basis. B: De matrix van de lineaire afbeelding. De exacte opbouw hiervan zal niet uitgelegd worden, het is gewoon belangrijk dat je ziet dat hier met matrices gerekend wordt. C: De oorspronkelijke coördinaten van P ; de coördinaten van P ten opzichte van de oude basis.
H5 : Grafische Mogelijkheden
23
Om het beeld van een vector P te berekenen in het geval van een transformatie van 3D naar 3D ziet de formule er als volgt uit :
A
B
C
A: De coördinaten van P ten opzichte van de nieuwe basis. B: De transformatiematrix. Elke kolom is de nieuwe locatie van een basisvector ten opzichte van de oude basis. C: De oorspronkelijke coördinaten van P ; de coördinaten van P ten opzichte van de oude basis. Ook hier wordt dus met matrices gewerkt. De matrix van een transformatie is echter eenvoudiger dan deze van een lineaire afbeelding die geen transformatie is. Elke transformatie in 3D kan gemaakt worden uit een combinatie van 3 basis transformaties : • translatie (een verschuiving); • rotatie; • schalen. Voor zowel rotatie als schalen kan men een 3x3-matrix als transformatiematrix gebruiken. De vermenigvuldiging hiervan levert het beeld op. Een translatie is echter een optelling van een waarde bij elk getal van de coördinaten. Het beeld hiervan kan met een translatiematrix in de vorm van een 3x3-matrix echter niet berekend worden. Als oplossing hiervoor zal men een rij en kolom bijvoegen. Deze hebben geen enkele nuttige info omtrent positie en dergelijke. Ze zijn een hulpmiddel om de translatie matrix toch te kunnen gebruiken in een matrixvermenigvuldiging. Beide zijn ze van opbouw hetzelfde : 0, 0, 0, 1. Dit verklaart waarom XNA met 4x4-matrices werkt. Dit kan je zien indien je een matrix zou converteren naar een string. Om hiervan een beeld te kunnen vormen, verwijs ik naar bijlage D. Transformatiematrices die de translatie, rotatie en schaal matrices bevat. Zoals gezegd kan elke transformatie gevormd worden door een combinatie van deze drie basistransformaties. De transformatiematrices worden dan met elkaar vermenigvuldigd om zo de algemene transformatiematrix te verkrijgen. Het is belangrijk dat je beseft dat matrixvermenigvuldigen niet commutatief is, wat wil zeggen dat het niet zoals bij de gewone getallen gelijk is in welke volgorde de factoren staan (bijvoorbeeld : 5 x 3 = 3 x 5). Een translatie uitvoeren en vervolgens een rotatie geeft een ander resultaat dan eerst de rotatie uit te voeren en dan de translatie (T x R ≠ R x T).
H5 : Grafische Mogelijkheden
24
5.2.3 3D plaatsen en weergeven Als je een 3D model in een nieuwe ruimte plaatst, zal het assenstelsel van de ruimte gekopieerd worden op de locatie waar jij wilt dat het 3D model komt ten opzichte van het assenstelsel van de ruimte. Deze transformatie is specifiek per model. Het oorspronkelijk assenstelsel blijft op zijn oorspronkelijke locatie staan. Het is een transformatie om de modellen te kunnen plaatsen. Dit wordt een wereldtransformatie genoemd. De matrix die hiermee overeenkomt is de wereldmatrix. Als je een 3D object wil zien op je scherm, dan moet dit omgezet worden naar een 2D object, vandaar de vermelding van de lineaire afbeelding van 3D naar 2D. Een scherm is nu eenmaal in 2D en kan dus geen echte 3D weergeven. Je krijgt als het ware een foto van de 3D ruimte. Net als bij het maken van een gewone foto heb je dus iets nodig dat in de 3D wereld zelf staat om de foto vast te leggen. Dit is je fotocamera. De positie, kijkrichting en het gezichtsveld bepalen dan wat er op je foto zal staan. De positie slaat op de locatie in de 3D wereld, de kijkrichting op naar waar de camera georiënteerd is en het gezichtsveld op hoe breed en hoog een camera kan zien (zie Figuur 5.2). Het gezichtsveld wordt dikwijls aangeduid met een hoek (α in Figuur 5.2). In de 3D wereld heb je dus een camera nodig om het beeld om te zetten naar 2D en bepalen bovenstaande criteria dus ook hoe je beeld eruit zal zien.
Y y
Y
(x, y, z) x z
X
X
α
Z
Z Positie Figuur 5.2 - Positie, oriëntatie, gezichtsveld
Oriëntatie
Gezichtsveld
5.2.4 3D in XNA Je kunt met XNA je eigen models bouwen door het definiëren van vertices ( = hoekpunten van polygonen, ze bevatten een aantal eigenschappen en zijn geen loutere punten). Dit is ook de methode waarop 3D modelling programma’s werken. Ze slaan de locatie van de vertices op en de volgorde waarin ze verbonden worden tot polygonen. Naarmate het 3D model complexer wordt, zullen er meer vertices nodig zijn en zal je in XNA geen korte functie kunnen schrijven die het model genereert. Daarom is het beter om complexe models in een 3D moddeling programma te maken. Zorg er wel voor dat je het aantal polygonen zo laag mogelijk houdt, anders kan het zijn dat de framerate van het spel enorm zal dalen.
H5 : Grafische Mogelijkheden
25
XNA aanvaardt echter maar 2 types 3D models, namelijk .fbx en .x. .fbx is een bestandstype die ontwikkeld is door Autodesk (3ds Max en nu ook Maya) om compatibel te zijn tussen verschillende programma’s. .x is een bestandstype die voortkomt van DirectX. Eender welke moddeling software je ook gebruikt, er bestaat hoogstwaarschijnlijk wel een exporter die ervoor zorgt dat je de native software bestanden kunt exporteren naar .fbx of .x. Het is tijd om eindelijk 3D in XNA zelf te plaatsen. Je kunt hiervoor terug gebruik maken van MijnEersteXNAGame. Open de solution en ga naar de Content map in de solution explorer. Rechter muisklik, Add > Existing Item … Ga op de cd-rom naar de map Code > H5 en selecteer daar het bestand p1_saucer.fbx. Dit model is afkomstig uit de Space War Starter Kit, maar er is een kleine wijziging aangebracht in verband met de texture. Voeg eveneens in de Content map de texture saucer_p1_diff_v1.tga toe die zich op dezelfde locatie als het model bevindt. Wat moet er nu gebeuren ? We zullen eerst het model moeten laden, daarna moeten we het in de 3D ruimte plaatsen en tenslotte moeten we ook nog een camera plaatsen om het model te kunnen zien. De texture moeten we niet laden in de code, deze wordt automatisch door de content pipeline opgepikt tijdens het opbouwen van de game. Voor de volledige code kan je terecht in bijlage E. 3D in XNA. Het laden van het model gebeurt op dezelfde wijze als bij het laden van een afbeelding. Declareer eerst een variabele mijnModel van het type Model. Ga dan naar de functie Loadcontent() en voeg hier het volgende toe : mijnModel = Content.Load<Model>(“p1_saucer”);. Vooraleer we nu verder kunnen nog een kleine uitleg : een 3D bestand kan uit verschillende meshes (letterlijk vertaald : mazen) bestaan. Een mesh zijn vertices (en dus ook polygonen) die tot eenzelfde laag in het 3D bestand horen. Ook al zit de complete 3D afbeelding in één laag, toch kan men een 3D object enkel tekenen via het mesh.Draw(); commando. Aangezien je dit voor elke mesh moet doen ben je het beste om een iteratie (herhaling) te schrijven. We zullen gebruik maken van een foreach herhaling. Hiermee kan je een verzameling (het 3D bestand is een verzameling van meshes) doorlopen tot op het einde. Plaats het volgende in de Draw methode : foreach (ModelMesh mesh in mijnModel.Meshes){mesh.Draw();}. Je zal dus alle meshes uit je model doorlopen en tekenen in de 3D ruimte, maar als je de game nu start zal je niks zien. Dit is logisch omdat je nog geen camera gedefinieerd hebt. Maar dit kunnen we niet zomaar doen. XNA werkt intern met shaders, wat in XNA zelf een Effect genoemd wordt. Een effect bevat alle informatie over hoe een 3D object getekend moet worden. Hier zal niet veel gezegd worden over shaders, omdat dit bijna een studie op zich is. Wel is het misschien interessant om te weten dat XNA alle grafische aspecten op de achtergrond via shaders uitvoert, zelfs 2D (herinner je dat je eigenlijk de 2D afbeeldingen in 3D plaatst). Als je meer over shaders wil weten, dan kun je er veel over vinden op het internet en er bestaan ook tal van boeken die enkel over shaders gaan. De camera wordt ook gedefinieerd via een effect. Om het de beginnende mensen makkelijk te maken hebben de makers van XNA een type BasicEffect gemaakt zodanig dat je in 3D kan werken zonder zelf een effect te moeten schrijven. Op één mesh kunnen echter meerdere effects geplaatst worden, zodat men speciale effecten kan creëren. Om die reden moeten we ook hier terug gebruik maken van de foreach iteratie.
H5 : Grafische Mogelijkheden
26
Plaats boven mesh.Draw(); volgend stukje code : foreach (BasicEffect effect in mesh.Effects){}. Hierin zullen alle nodige effecten eerst uitgevoerd worden vooraleer de mesh getekend wordt. Nu zal blijken dat 3D inderdaad gebruik maakt van matrices. Gelukkig hebben de makers van XNA het vereenvoudigd en moet je geen volledige matrix invullen. Als eerste zullen we de camera plaatsen. De camerapositie zullen we bewaren in een Vector3 variabele cameraPositie. Voeg deze variabele toe onder Model mijnModel;. Het initializeren zullen we doen in Initialize() : cameraPositie = new Vector3(0.0f, 0.0f, 1000.0f);. De camera komt dus op een afstand van 1 000 eenheden voor de oorsprong. We bewegen de camera 1 000 eenheden in de positieve richting van de Z-as. Vanaf het scherm gezien beweegt de oorsprong hierdoor achteruit. Laat je niet afschrikken door de notatie van de getallen, het geeft alleen aan de computer aan dat het floats zijn en dit doe je dus door er een f achter te plaatsen. Verder hebben we ook nog de kijkrichting nodig Vector3 cameraKijkrichting. Initialiseer deze variabele na cameraPositie en declareer ze in Initialize() : cameraKijkrichting = new Vector3(0.0f, 0.0f, 0.0f);. Je kan ook wat tijd besparen door cameraKijkrich ting = Vector3.Zero; te plaatsen. Dit betekent hetzelfde als het vorige stuk code. De camera zal dus naar de oorsprong kijken. We hebben nog een 3de Vector3 nodig die zal aangeven wat voor de camera als boven beschouwd wordt. Declareer bovenaan Vector3 cameraOmhoog;, initialiseer in Initialize(): cameraOmhoog = new Vector3(0.0f, 1.0f, 0.0f);, alternatief : cameraOmhoog = Vector3.Up;. Dit betekent dus dat de positieve kant van de Y-as als omhoog wordt beschouwd. Met deze variabelen kunnen we de camera plaatsen. Plaats in de foreach van effect : effect.View = Matrix.CreateLookAt(cameraPos itie, cameraKijkrichting, cameraOmhoog);. Je maakt nu een matrix aan die XNA zelf opbouwt aan de hand van de parameters. Deze matrix zal gebruikt worden voor de lineaire transformatie van 3D naar 2D. Maar we zijn er nóg niet. De camera heeft namelijk nog geen gezichtsveld. Er komen trouwens nog enkele aspecten kijken bij 3D projecteren op een vlak. We zullen beginnen met effect.Projection = Matrix.CreatePerspectiveFieldOfV iew(gezichtsVeld, aspectRatio, 1.0f, 10000.0f);. De eerste parameter is het gezichtsveld in radialen. We kunnen de variabele gezichtsVeld declareren en in Initialize() initializeren. Gezien graden voor ons makkelijker is dan radialen, kunnen we gebruik maken van MathHelper.toRadians() die de graden zal omrekenen naar radialen. De MathHelper klasse is een klasse die zoals de naam zegt, helpt bij wiskunde. Verken gerust de andere methodes voor later gebruik. Als ons gezichtsveld nemen we 45°. Dit noteer je dan als een float in de methode MathHelper.toRadians(45.0f);. Als tweede parameter hebben we de aspectRatio. Hiervoor gebruiken we een variabele aspect Ratio. Hetzelfde liedje :declareren, initializeren. De aspectRatio is de verhouding van de breedte en de hoogte van een scherm. Deze kunnen we makkelijk berekenen door de breedte van het venster te delen door de hoogte van het venster : aspectRatio = graphics.GraphicsDevice.View port.Width / graphics.GraphicsDevice.Viewport.Height;. De derde parameter bepaalt vanaf welke afstand de camera de objecten pas zal kunnen zien. Indien een object zich dichter dan 1 eenheid bij de camera bevindt, zal deze niet weergegeven worden.
H5 : Grafische Mogelijkheden
27
De vierde parameter bepaalt vanaf welke afstand een camera objecten niet meer zal kunnen zien. Hier kan je camera dus zien vanaf één eenheid voor zich tot 10 000 eenheden ver. Als je nu het programma laat lopen zie je terug niks. Nochthans hebben we alles ingesteld zoals het zou moeten. De reden waarom je niks ziet, is omdat het gebruikte model veel te groot is. Als we deze verkleinen zullen we eindelijk het model zien. Het schalen doe je via de reeds vermelde wereldmatrix. Aangezien schalen de enige transformatie is die je nu uitvoert, is de schaalmatrix eveneens jouw wereldmatrix. Voeg volgend stukje code toe boven de camera instellingen die je hiervoor gedaan hebt : effect.World = Matrix.CreateScale(0.2f);. Wijzig de Y-waarde van de camerapositie nog naar 1000.0f voor een beter zicht. Eindelijk heb je een 3D object op je scherm kunnen toveren. Er is gebruik gemaakt van de wereldmatrix om het model te schalen, maar in principe hoef je in dit geval geen wereldmatrix te gebruiken. Hier wordt gewoonweg de oorsprong van de 3D ruimte genomen om je model te tekenen. Een andere oplossing om je model te zien was de camera verder van de oorsprong plaatsen. Indien je de objecten zal laten bewegen, zal je wel gebruik moeten maken van de wereldmatrix. De verplaatsing gebeurt dan door het aanpassen van de parameters van de matrices die de wereldmatrix vormen. Laten we dit nog vlug aantonen door het model rond de Y-as te laten draaien. Maak bovenaan een variabele float rotYas aan. Initialiseer deze in Initialize() via de code rotYas = MathHelper.ToRadians(0.0f);. Als we nu de rotatie willen uitvoeren op het model, moeten we effect.World aanvullen als volgt : effect.World = Matrix. CreateScale(0.2f) * Matrix.CreateRotationY(rotYas);. Omdat rotYas een variabele is, kunnen we deze laten wijzigen over de loop van tijd. We kunnen het beste gebruik maken van de Update() methode, deze wordt standaard 60 keer per seconde uitgevoerd. Plaats hier : rotYas += MathHelper.ToRadians(1.5f); if (rotYas >= MathHelper.ToRadians(360.0f)) {rotYas = 0.0f;} Update() wordt hier 60 keer per seconde uitgevoerd, dus op één seconde zal het model 60 x 1,5° = 90° graden gedraaid zijn. Het model zal dus in een tijd van 4 seconden helemaal rondgedraaid zijn. De controle wordt gedaan om te voorkomen dat rotYas groter en groter zou worden. 0° en 360° zijn immers gelijk.
H5 : Grafische Mogelijkheden
28
Hoofdstuk 6 : Audio (XACT) XACT staat voor Cross-Platform Audio Creation Tool. Het is oorspronkelijk een tool van de XDK (Xbox Development Kit). Het is de enige optie die je hebt om met geluid te werken in XNA. Je zou het wel kunnen bereiken dat je op een andere manier met audio kan werken op pc, maar dit zal niet werken op de Xbox 360. Vroeger was audio toevoegen aan games enorm veel werk. Via XACT is het gelukkig allemaal veel eenvoudiger geworden. Een nadeel van XACT is wel dat je enkel .wav files kunt gebruiken. Bovendien is het ook aangeraden om je .wav files in 16-bit 44 KHz Stereo PCM te zetten, andere instellingen kunnen problemen opleveren. Het geluid kan maar in één formaat gecomprimeerd worden per platform. Een pc heeft de ADPCM compressie, de Xbox 360 de XMA compressie. Geluid kan ook niet dynamisch afgespeeld worden. Als het spel opgestart wordt zal alle geluid geladen worden. Tijdens het lopen van het spel kan je geen geluid meer toevoegen. XACT biedt de sound artist de mogelijkheid om aanpassingen te maken zonder dat hij de programmeurs moet lastigvallen. Volume, pitch, etc. worden in XACT zelf ingesteld. Als je deze wil kunnen aanpassen via XNA moet je variabelen aanmaken (zie later, Cue Instance). Met geluid moet zorgzaam omgesprongen worden. Aan de ene kant mag het niet te groot zijn omdat het anders te veel ruimte inneemt, aan de andere kant mag de kwaliteit niet slecht worden. Muziek kun je best comprimeren. Voor geluidseffecten ben je beter om de kwaliteit te verminderen dan ze te comprimeren. Als je deze wel zou comprimeren moet de CPU elke keer bij het afspelen decomprimeren en dit kan de performantie doen dalen.
6.1 XACT, het programma Laten we gebruik maken van XACT om geluid aan MijnEersteXNAGame toe te voegen. Ga naar Start > Programma’s > Microsoft XNA Game Studio 2.0 > Tools > XACT. Bij het opstarten zie je de project tree aan je linkerkant. De belangrijkste onderdelen die je hier ziet zijn de Wave Banks en Sound Banks nodes. De kindelementen die er in zullen komen, zullen gebruikt worden in XNA om geluid af te spelen. Om te beginnen moet je een nieuw project aanmaken. Dit kan je doen via File > New Project, Ctrl + N of klikken op het uiterst linkse icoontje in de taakbalk. Sla het nieuwe bestand op met de naam Audio in de Content map van MijnEersteXNAGame. De volgende stap is een Wave Bank toevoegen. Een Wave Bank zal al het geluid opslaan die je zal gebruiken. Het is mogelijk om meerdere Wave Banks te maken om bijvoorbeeld bij elk level van een spel enkel specifieke muziek te laden. Om een nieuwe Wave Bank te maken heb je terug 3 opties : ga naar Wave Banks > New Wave Bank, klik op het New Wave Bank icoon in de taakbalk of rechtermuisklik op Wave Banks in project tree en dan New Wave Bank. Op het ogenblik dat je een nieuwe Wave Bank aangemaakt hebt, zie je in het rechterdeel van het programma een scherm verschijnen. Dit leidt echter de aandacht af van het feit dat de naam van je nieuwe Wave Bank geselecteerd staat en je dus de naam kunt wijzigen.
H6 : Audio (XACT)
29
Aangezien we toch maar met één Wave Bank zullen werken, kun je de standaardnaam laten staan. Nu kun je een geluidsbestand toevoegen aan de Wave Bank. Er zijn terug 3 opties. Voor het verdere verloop van de bespreking van XACT zal telkens voor de rechtermuisoptie gekozen worden, gezien het overbodig is om steeds alle opties op te sommen. Rechtermuisklik in het verschenen venster, Insert Wave File(s) … Selecteer hier het bestand menu_select2.wav die je kunt vinden in de map Code > H6 > XACT op de Cd-rom. Het geluid is afkomstig uit de Space War Starter Kit. Het bestand zal rood italiek weergegeven worden in het venster. Dit duidt erop dat het geluid nog niet gebruikt wordt door een Sound Bank. Een nieuwe Sound Bank wordt op een gelijkaardige manier aangemaakt als een Wave Bank. Rechtermuisklik op Sound Banks in de project tree, New Sound Bank. Opnieuw zal naam van de nieuw aan gemaakte Sound Bank geselecteerd staan en kun je een naam ingeven. Omdat we maar één Sound Bank zullen gebruiken is de standaard naam voldoende. Sound Banks dienen om sound cues op te slaan. Deze sound cues heb je nodig om het geluid uit de Wave Banks af te spelen. Je kan een .wav uit de Wave Bank toevoegen door simpelweg de .wav te verslepen op de Sound Bank. Het gevolg hiervan is dat je .wav nu groen is geworden in de Wave Bank, omdat hij gebruikt wordt. Als je de .wav op het bovenste gedeelte van de Sound Bank, het sound gedeelte, hebt gesleept, dan verschijnt de .wav enkel hier. Sleep je echter de .wav op het cue gedeelte, dan wordt de wave aan het cue gedeelte en het sound gedeelte toegevoegd. Om de geluiden uiteindelijk te kunnen afspelen in XNA moet je ze nog toewijzen aan cues. Als je de .wav reeds op het cue name gedeelte hebt gesleept heb je al een cue. Indien je nog geen cue hebt, kun je de sound uit het sound gedeelte verslepen naar het cue gedeelte en zo wordt een cue aangemaakt. Je kan aan een cue meerdere sounds toewijzen. Dit kan je doen door de sounds op de cue te slepen. Op deze wijze kan je een afwisseling in je geluiden steken. Je kan ook het gewicht van elke sound instellen, zodanig dat een bepaalde sound meer zal voorkomen dan een andere. Het spreekt voor zich dat je meerdere cues kan aanmaken. Indien je aanpassingen wil maken aan je geluid, dan moet je dit doen via de sound. Als je de sound selecteert, verschijnen onder de project tree de eigenschappen. Hier kan je onder andere instellen of de sound moet loopen, hoeveel keer er gelooped moet worden, het volume, de prioriteit om het geluid af te spelen tegenover andere geluiden, … De prioriteit die je hier instelt is niet het gewicht voor in een cue met een aantal geluiden. Dit gewicht stel je dus in in de eigenschappen van de cue. Deze eigenschappen vind je op dezelfde plaatst terug als deze van de sounds. Wijzig het volume van je sound hier naar -3 dB zodat het later wat luider klinkt en je het volume van je computer niet hoeft op te drijven. Indien je sommige eigenschappen van een sound via XNA wil aansturen, dan moet je dit doen via een Cue Instance. Dubbelklik in de project tree op cue Instance. Er verschijnt een venster Variable Settings. Om hier een variabele aan toe te voegen, rechtermuisklik in het venster, Add new variable. Elk veld kan je nu aanpassen. Laten we een variabele PitchVar toevoegen. Vul in het eerste veld de naam PitchVar in. Als je kijkt naar de eigenschappen van een sound kun je zien dat een pitch een minimumwaarde -12 en een maximum waarde 12 heeft. Vul deze waarden in bij Min, respectievelijk Max. Laat de rest van de velden op de startwaarden staan.
H6 : Audio (XACT)
30
De variabele moet je nu nog toevoegen aan een RPC (Runtime Parameter Control) Preset. Rechtermuisklik op RPC preset in project tree, New RPC Preset. In het nieuw verschenen venster kan je nu als variabele de zopas aangemaakte variabele PitchVar selecteren. Value laat je op 0 staan, kies bij Object sound en neem als parameter Pitch. Je kan effecten maken (bijvoorbeeld infaden van geluid) door de ankerpunten in de grafiek te verplaatsen of een waarde in te vullen bij Points rechtsonder. Als je nu geluiden wil toewijzen die hiervan gebruik maken kun je simpelweg een sound verslepen op de RPC Preset. Als je voor de punten bij PitchVar -12 en 12 ingeeft en bij Pitch -12 en 12, zie je dat de curve een stijgende rechte van de linkeronderhoek naar de rechterbovenhoek vormt. Hoe je deze RPC Preset gebruikt, zal uitgelegd worden bij het gebruiken van geluid in XNA. Als je op een play button in gelijk welk venster klikt, krijg je een foutmelding, namelijk Could not connect to a Windows auditioning server. Dit komt omdat er nog een programma te weinig opgestart is, namelijk de XACT Auditioning Utility. Dit programma zorgt ervoor dat je de geluiden uit XACT ook kunt afspelen. Op zich een beetje omslachtig en verwarrend als je dit niet weet. Misschien dat het in de toekomst in XACT zelf zal verwerkt worden. Er wordt verteld dat je best eerst XACT Auditioning Utility opstart, maar het verloopt meestal vlotter als je eerst XACT opstart, dan XACT Auditioning Utility en om te verbinden gewoon een .wav aanduidt en op play klikt. Niet professioneel, maar de correcte methode heeft al problemen opgeleverd, de niet professionele geen. Als je op pc aan het werken bent moet je er ook voor zorgen dat je View op View Windows Properties staat, anders zal je een foutmelding krijgen als er geen Xbox 360 aangesloten is. Als je wil kan je de .wav bestanden comprimeren. Dit doe je via rechtermuis op Compression Presets in de project tree, New Compression Preset. Als je op de nieuwe Compression Preset klikt, kun je de instellingen in het eigenschapvenster wijzigen. Je heb de keuze tussen PCM of XMA voor Xbox 360 en PCM of ADPCM voor Windows (PCM = niet gecomprimeerd). Wil je deze compressie aan een .wav toewijzen, dan selecteer je de .wav, ga je naar zijn eigenschappen en kies je daar bij Compression Preset jouw compression preset, die in dit geval nog compression preset zou moeten noemen aan gezien de standaardnaam niet gewijzigd is. In geval van een Windows compressie kun je het resultaat van de compressie meteen op het scherm zien : hoeveel is het bestand kleiner geworden en hoeveel procent is het bestand kleiner dan het oorspronkelijk bestand. Als je de compressie voor de Xbox 360 wil zien, moet je XACT Auditioning Utility uitschakelen, View > View Xbox 360 Properties en dan op build klikken. In principe zou je ook bij een Windows creatie op build moeten klikken, maar als je dit niet gedaan hebt, zal XNA het automatisch voor jou doen.
6.2 Geluid in XNA We zullen de XACT file in MijnEersteXNAGame inbrengen. Ga naar de Content map, rechtermuisklik, Add > New Item … en selecteer Audio.xap. Voor de volledige code, zie bijlage F. Geluid in XNA.Om nu alles te laten werken moet je alles omtrent geluid zelf laden. Niet alleen de wave- en sound banks, maar ook een audio engine die de bestanden zal beheren.
H6 : Audio (XACT)
31
Voeg bovenaan volgende 3 regels toe : AudioEngine audioEngine; WaveBank wave Bank; SoundBank soundBank; Initialiseer de 3 variabelen in de LoadContent() methode. audioEngine = new AudioEngine(“Content\\Audio.xgs”); Het bestand Audio.xgs kun je terugvinden in de map Win, die ter hoogte van de .xap file staat. Als je niet op build geklikt hebt tijdens het werken in XACT kan het zijn dat deze map nog leeg is. Als je echter de build van je game start, dan zal XNA zelf de XACT file builden zodat het bestand Audio.xgs zal verschijnen. Voor de tweede parameter in waveBank = new WaveBank(audioEngine, “Content\\ Wave Bank.xnb”); en soundBank = new SoundBank(audioEngine, “Content\\ Sound Bank.xsb”); geldt hetzelfde als voor Audio.xgs. Je moet hier nog Content voor de bestanden plaatsen omdat XNA er anders vanuit gaat dat de bestanden op dezelfde locatie staan als Game1.cs. De string die hier de locatie aangeeft is niet verbonden met de Contentmanager Content waarvan de RootDirectory op Content is geplaatst. Als je nu geluid wil afspelen moet je simpelweg soundBank.PlayCue(“menu_select2”); plaatsen. menu_select2 is de naam van de cue. Het geluid kun je afspelen bij het indrukken van een toets, maar kan gerust ook verwerkt worden in de Update() methode, wat wij dus zullen doen. Op het ogenblik dat het 3D model volledig rond is geweest, zullen we het geluid laten afspelen. Je kan de code dus in de controle steken waar gekeken wordt of de hoek 360° bedraagt : op dit moment is het model éénmaal volledig rondgedraaid. We kunnen het geluid wat dynamischer maken door gebruik te maken van onze Cue Instance PitchVar. Om dit te illustreren gaan we MijnEersteXNA game aanpassen zodanig dat het 3D model afzwakt in het ronddraaien en het inderdaad klinkt alsof het model aan het afzwakken is. Nadat het voldoende afgezwakt is, gaan we de draaisnelheid terug opvoeren en het geluid laten mee evolueren. Als het model dan zijn topsnelheid bereikt heeft, laten we het opnieuw afzwakken en zo maken we een lus waarin het model afwisselend vertraagt en versnelt. Om dit te kunnen maken hebben we 4 nieuwe variabelen nodig die bovenaan mogen toegevoegd worden : Cue cue; float cueVar; bool afzwakken; float draaiSnelheid;. cue zal gebruikt worden om de cue van een Sound Bank in op te slaan, cueVar wordt gebruikt om de Cue Instance te wijzigen, afzwakken is een boolean die zegt of het model nu aan het afzwakken is of niet en draaiSnelheid is het aantal graden waarmee je de positie van het model laat toe nemen, wat dus kan beschouwd worden als de snelheid waarmee het model ronddraait. 3 van de 4 variabelen initializeren we in Initialize(), namelijk cueVar = 12.0f; afzwakken = true; en draaiSnelheid = 12.0f;. cue initializeren we in Update(), zoals je kan zien in Code 6.1. Code 6.1 voeg je in in de methode Update(), boven base.Update(gameTime);. De eerste regel is niks nieuws, dit is gewoon om je model te laten ronddraaien. Naast rotYas terug op 0 plaatsen wijzen we aan cue een waarde toe. De reden waarom we het hier doen en niet bij LoadContent() is omdat cue steeds opnieuw moet toegewezen worden om het te kunnen afspelen. cue.Play(); laat het geluid afspelen.
H6 : Audio (XACT)
32
if (rotYas >= MathHelper.ToRadians(360.0f)) { rotYas = 0.0f; cue = soundBank.GetCue( “menu_select2”); cue.Play();
if (afzwakken) { cueVar -= 1.0f; draaiSnelheid -= 0.4f; if (cueVar <= -12.0f) { afzwakken = false; }
cue.SetVariable(“PitchVar”, cueVar); } else { cueVar += 1.0f; draaiSnelheid += 0.4f; if (cueVar >= 12.0f) { afzwakken = true; } cue.SetVariable(“PitchVar”, cueVar); } }
Dan zijn we gekomen aan de controles. Beide controles zijn min of meer gelijk aan elkaar, dus wordt er maar één uitgelegd. Er wordt gecontroleerd of afzwakken true is. Is dit het geval, dan wordt cueVar met 1 verminderd en wordt de draaisnelheid met 0,4° verminderd. Als cueVar nu kleiner of gelijk aan -12 wordt (-12 is de minimumwaarde van de Cue Instance PitchVar), dan wordt afzwakken false, wat ervoor zal zorgen dat bij de volgende keer dat Update() doorlopen wordt, het stuk code bij else zal worden uitgevoerd. Dan is er enkel nog cue.Set Variable(“PitchVar”, cueVar); die de variabele PitchVar zal selecteren en er de waarde cueVar zal aan toewijzen. De Cue Instance is zo opgesteld dat de waarde van PitchVar overeenkomt met de Pitch waarde. Als je de grafiek in XACT bekijkt (Figuur 6.1), zal je wel begrijpen dat bij het verminderen van PitchVar in XNA, de Pitch zal dalen. Je volgt de grafiek dan naar beneden. In het geval van toenemen zal je de grafiek naar boven volgen. Als je nu op Run klikt, zal je ervaren wat de code beschrijft.
Code 6.1 - Een 3D model variabel laten ronddraaien
Figuur 6.1 - Grafiek PitchVar - Pitch
H6 : Audio (XACT)
33
Hoofdstuk 7 : Xbox 360 Één van de aantrekkelijkste aspecten van XNA is dat je voor het eerst makkelijk games kunt ontwikkelen voor een console. Qua code wijkt een Xbox 360 XNA game weinig af van de code van een Windows game. Daarom zal dit hoofdstuk ook niet groot zijn en enkel de zaken uitleggen waarmee je rekening moet houden bij ontwikkelen voor de Xbox 360. Het belangrijkste verschil met Windows code is dat je op de Xbox 360 niet over het volledige .NET framework beschikt, maar over het .NET compact framework. Dit kan ervoor zorgen dat sommige commando’s die op het Windows platform wel werken, niet zullen werken op de Xbox 360. Een logisch voorbeeld hiervan zijn keyboard en de muis commando’s. De Xbox 360 heeft als input enkel de Xbox 360 controller. Je kan wel je Windows game code aanpassen via conditionele preprocessor directives. Dit is code van de vorm #if … #endif. Deze zorgen ervoor dat de code die zich binnen de begin- en einddeclaratie bevinden enkel zullen uitgevoerd worden als aan de voorwaarde bij de #if voldaan is. Zoals de naam al doet vermoeden wordt deze code uitgevoerd voordat de complete code gecompileerd wordt. Als aan de voorwaarde voldaan is, zal de code in het bestand opgenomen worden vooraleer gecompileerd te worden. Om pc specifieke code uit Xbox code te laten kun je gebruik maken van #if !XBOX … #endif. Als je deze aanpassingen hebt gemaakt, kun je het spel laten omzetten naar een Xbox 360 game. Dit kun je via Project > Create copy of projectnaam for Xbox 360 … Het voordeel hiervan is ook dat dezelfde Content map gebruikt zal worden. Als je ontwikkelt voor de Xbox 360 moet je de Xbox 360 in hetzelfde netwerk van je computer plaatsen. Je hebt ook zeker een verbinding met het internet nodig voor de Xbox 360 omdat deze gebruik maakt van Xbox Live. De Xbox 360 moet over een harde schijf beschikken en natuurlijk moet je ook over twee schermen beschikken. Het is aangeraden om (indien mogelijk) de Xbox 360 aan te sluiten op een TV scherm. Op een computerscherm zie je het volle aantal pixels, op een TV scherm heb je echter overscan. Dit is een techniek die ervoor zorgt dat er aan alle randen van het scherm een stuk van het beeld afgesneden wordt. Als je hier geen rekening mee houdt tijdens het ontwikkelen van je spel, zouden er wel eens belangrijke stukken van het beeld kunnen verdwijnen. Als je het spel wil testen op de Xbox 360 zit er wel een addertje onder het gras. Vooraleer je dit kan doen moet je je inschrijven in de XNA Creators Club via de Xbox Live Marketplace. Dit kost $99 voor 12 maanden en $49 voor 4 maanden. Ben je student, vergeet Microsoft Dreamspark dan niet (zie hoofdstuk 1). De reden waarom je ingeschreven moet zijn is omdat Microsoft op deze manier ongeveer op de hoogte is wat iedereen doet zodat er geen mensen zouden zijn die misbruik zouden maken van de console. De controle van Microsoft is terecht, maar als je maar één spelletje op een jaar maakt en test, zal je waarschijnlijk wel te veel betalen. De meeste Xbox 360 controllers kan je ook aansluiten op pc, dus ben je in voorgaand geval beter van op pc te testen. Let wel : de meeste Xbox 360 controllers, het kan dus voorvallen dat sommige niet werken … Het kan gebeuren dat een spel op de Xbox 360 trager of vlugger zal lopen dan op pc. Dit komt omdat een console anders werkt. Vaak kan je het probleem verhelpen door de code opnieuw te bekijken en aanpassingen te maken.
H7 : Xbox 360
34
Hoofdstuk 8 : Voorbeeld Game : Rat Maze Als een ondersteuning van het eindwerk is ook een game gemaakt. Het is een 2D doolhofspel waar een rat de uitgang moet zien te bereiken (zie Figuur 8.1). Terwijl hij dit doet kan hij onderweg kaas eten die punten oplevert. Maar natuurlijk zijn er ook vijanden in het spel aanwezig waarvoor de rat moet oppassen. De game heeft het levenslicht gezien voor de cursus Javascript. Deze versie was niet echt performant aangezien er per keer dat je een knop indrukte, er voor 77 muren gecontroleerd werd of de rat al dan niet op dezelfde locatie was. Gelukkig geldt dit niet meer voor de XNA versie en is de game nu op een meer professionele en meer performante manier opgebouwd via matrixen. Waar je in de Javascript versie voor elke muur ging controleren, maak je hier gewoon gebruik van de positie van de rat en via een kleine berekening zet je dit om in een positie in een matrix zodat je dus maar in één vak van de matrix gaat kijken of de rat ergens doorkan of niet. Niet alle uitleg over het hele spel zal hier gebeuren. Voor een complete uitleg over de code kun je terecht op de cd-rom in de map H8 > RatMaze met uitleg. Aangezien er hier redelijk wat commentaar staat, kan het voor sommige mensen onoverzichtelijk zijn. Daarom is er ook een versie op de cd-rom beschikbaar zonder uitleg. De code is niet toegevoegd als bijlage. Om de code te zien ben je verplicht om het bestand op de cdrom te bekijken. Om er zeker van te zijn dat er geen rechten geschonden worden, wordt er geen gebruik gemaakt van geluid. Zie dit als een uitdaging en tracht zelf muziek aan het spel toe te voegen.
Figuur 8.1 - Rat Maze
H8 : Voorbeeld game : Rat Maze
35
8.1 Input Een belangrijk aspect van XNA die nog niet aan bod gekomen is, is input. De input in XNA kan geleverd worden door de muis, het toetsenbord en / of de Xbox 360 controller. Om te weten welke knop ingedrukt is, maak je gebruik van de state van het input toestel. Een state bevat de waarden over alle mogelijke toesten en bewegingen van het input toestel. Wil je weten of een bepaalde knop ingedrukt is, dan haal je dit gegeven gewoon op uit de state. Dit doe je via de statische klasse GetState van de klassenaam van het toestel. In het geval van de Xbox 360 controller en de muis kun je dan van hieruit via een puntnotatie opgeven wat je precies nodig hebt uit de state. Bij een toetsenbord werkt dit iets anders. Elke toets op het toetsenbord kan enkel ingedrukt of losgelaten zijn, andere toestanden zijn niet mogelijk. Bij een Xbox 360 controller heb je nog 2 analoge sticks en 2 analoge triggers die een aantal waarden kunnen aannemen. Bij de muis heb je nog de positie van de muis en het scrollwiel. Omdat het toetsenbord dus enkel ingedrukt of losgelaten kan waarnemen, wordt er gebruik gemaakt van de functie IsKeyDown van de KeyboardState klasse. Keyboard.GetState() levert een object van de klasse KeyboardState op. Als parameter geven we dan de knop mee waarvan we de toestand willen weten. Zie Code 8.1 voor de correcte input van elk inputtoestel. // Xbox 360 controller GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed // Toetsenbord Keyboard.GetState().IsKeyDown(Keys.Enter) // Muis Mouse.GetState().LeftButton == ButtonState.Pressed
Code 8.1 - Controle of een knop is ingedrukt voor de verschillende input toestellen Een inputtoestel zal continu een signaal doorgeven. Dit heeft als gevolg dat je bij het indrukken van aan knop een reeks van “knop is ingedrukt”-signalen verstuurt. Voor het besturen van de rat is dit prima, die moet doorlopen als we een knop ingedrukt houden, anders zouden we per stap die de rat doet, de toets opnieuw moeten indrukken. In het menu is dit niet wenselijk. Als dit in het menu toegelaten zou zijn, dan zou het zeer moeilijk zijn om een knop exact te selecteren, ook al ben je vlug om de knop terug los te laten. Het doorsturen van de signalen gebeurt in fracties van een seconde, waardoor een enkele druk volgens de mens een aantal signalen zijn voor de computer. Dit probleem valt op te lossen door de vorige state op te slaan in een variabele. Als de vorige state van een knop losgelaten was én de volgende ingedrukt, dan kan dit beschouwd worden als één signaal van een knop. Het tweede signaal zal niet geïnterpreteerd worden als ingedrukt, omdat de voorgaande state ook ingedrukt was. Op deze wijze kun je per indrukken van een knop, één stap vooruitgaan in het menu. Om een tweede keer het ingedrukt signaal te kunnen doorgeven is men verplicht om de knop eerst los te laten.
H8 : Voorbeeld game : Rat Maze
36
8.2 Muurdetectie De goede methode om te kijken of er op een bepaalde locatie een muur staat en de rat hier dus niet doorkan is via een matrix. Het speelveld bestaat eigenlijk uit een vakjes van 20px x 20px. Deze afmetingen gelden ook voor de rat en de vijanden. De muren zijn eigenlijk rijen blokjes van 20px x 20px. Het totale speelveld is 500px x 500px, wat kan beschouwd worden als een matrix van 25 rijen en 25 kolommen (500px / 20px = 25). Als men nu een 25 x 25-matrix aanmaakt, kan men deze gebruiken om per blok van 20px x 20px een waarde op te slaan. In het geval van de muren kunnen we false plaatsen waar de rat niet doorkan en er dus een muur staat. Door de positie van de rat dus te delen door 20 en dit resultaat af te ronden, komen we aan een waarde tussen 0 en 25. In het geval van het spel moet er bij de ratpositie nog 20px opgeteld worden aangezien het speelveld tussen muren zit en deze muren niet voorkomen in de matrix. Als de muren wel waren meegerekend hadden we een 27 x 27-matrix en hoefden we deze correctie niet te doen. Zie Figuur 8.1 om de volgende uitleg beter te begrijpen. De positie van de rat wordt bepaald door de linkerbovenhoek. Het kan dus gebeuren dat de afbeelding van de rat voor een deel in een volgend vak van de matrix zit. Als de rat in het geval van de afbeelding dan naar links zou bewegen, dan geldt voor de positie dat de weg vrij is, maar de afbeelding zit voor een deel op het volgende vak waardoor de rat door de muur zal lopen ! Om dit te voorkomen moeten we rekening houden met de hoogte van de rat in geval van horizontaal bewegen en de breedte van de rat in geval van verticaal bewegen. Daarom gaan we nog een bijkomende controle uitvoeren, namelijk of de weg wel vrij is voor de andere zijde van de rat. De berekening verloopt bijna hetzelfde als van de gewone berekening met het enige verschil dat je van het bekomen kommagetal nog eerst 0.1f aftrekt vooraleer af te ronden. Dit is om te voorkomen dat de rat zou vastzitten bij het passeren van een muur. Als de rat op de goede hoogte of breedte zou zitten zodat zijn locatie een veelvoud van 20 vormt, zou bij de bijkomende controle gekeken worden in het volgende vak indien er geen 0.1f afgetrokken wordt. Als daar dan een muur zou staan, zou de rat niet meer bewegen, terwijl hij zich wel in een vrij vak zou bevinden. 0
1
2
3
0 1 2 3 Linkerbovenhoek
Rat
Linkerbovenhoek
Rat
Rechteronderhoek
Muur
Rechterbovenhoek
Muur
Breedte
Hoogte
Indien geen -0.1f behoort tot het vak (2, 1), waar een muur staat, zodat de rat zou vastzitten.
Figuur 8.2 - Rat Maze, muurdetectie
H8 : Voorbeeld game : Rat Maze
37
Deel II : XNA & serious games
Hoofstuk 9 : Wat zijn serious games ? Serious games is een nog betrekkelijk jonge term. Serious games is het gebruik van games en gametechnologiën voor een doel hoger dan entertainment. Het is dus een zeer ruim begrip en gaat verder dan games (software) alleen. Game hardware telt eveneens. Ook slaan serious games op het feit dat het niet alleen gaat over vernieuwde toepassingen van een technologie, het kan zo simpel zijn als een normaal commercieel spel te gebruiken voor training, onderwijs, etc. In dit geval spreekt men over Serious COTS (Commercial Of The Shelf). Deze games zijn als het ware tijdelijke serious games, het zijn normaalgezien GEEN serious games. Ze zijn enkel serious games in de tijd dat ze op een niet-entertainende manier gebruikt worden. Enkele voorbeelden : • Serious game (software) : America’s Army, Darfur Is Dying, Pats en Co • Harware matig : Guitar Hero guitaar kunnen gebruiken om echte muziek te spelen; Wii controller kunnen gebruiken (hacken) voor andere doeleinden dan Wii zelf; het Amerikaanse leger die Xbox 360 controllers gebruikt voor het besturen van onbemande voertuigen en robots • Serious COTS : Age of Empires in functie van geschiedenis; SWAT 4 wordt gebruikt door het Canadese leger; Half-Life 2 engine kan gebruikt worden voor architectuur en visualisaties Voor dit eindwerk wordt echter enkel gefocust op game software om serious games te maken. XNA is immers software en een gametechnologie die men voor serious games zou kunnen aanwenden. Serious games doorbreken het stereotype beeld dat de mensen hebben over games, namelijk dat ze gewelddadig zijn, tijdverspilling, nutteloos, … Het stereotype beeld van serious games doet denken dat serious games droge games zijn, maar niets is minder waar. Indien men met mensen uit de game-industrie samenwerkt kunnen de games toch nog verrassend onderhoudend blijken. Het komt natuurlijk wel voor dat er een aantal mensen zijn die serious games proberen maken, maar die geen kennis van game design hebben en zo het spel droog en slecht maken. Dit komt omdat de andere industrieën games als een nuttig item beginnen te zien en sommige denken dat ze zelf een game in elkaar kunnen knutselen. Een vergelijkbare tendens met het web. Daar vind je ook tal van slechte websites. Om goede serious games te maken, heb je dus mensen uit de game-industrie nodig om mensen uit de non-game-industrie te helpen om nog nooit in games verwerkte onderwerpen in een game te steken zoals hongersnood, sociale vaardigeheden, etc. Een foute opvatting is dat de term serious op de inhoud van het spel slaat. Een spel kan er namelijk heel absurd uitzien, maar als de boodschap die het wil overbrengen van bijvoorbeeld maatschappelijk nut is, dan spreekt met ook hier over serious games. Catch the Sperm is hier een voorbeeld van. Het is een game die gemaakt is in opdracht van de Zwitserse overheid om AIDS preventie te promoten.
H9 : Wat zijn serious games ?
39
Een tweede foute opvatting is dat serious games er zijn om mensen dingen aan te leren. De educa tieve spellen vormen wel een deel van serious games, maar serious games zijn niet enkel educatieve games. Als men games in het onderwijs inzet, wil men een verrijking bieden, maar zeker geen vervanging van het onderwijs zelf. De softwarematige kant van serious games kan onderverdeeld worden in 7 categorieën : A. Educatieve games dit zijn de games die men gebruikt om mensen zaken bij te leren B. Overheid / beleidgames het moet de burger iets bijbrengen over beleid en de werking van de overheid om zo meer begrip te krijgen voor de overheid C. Politieke / sociale games de mensen bewust maken van sociale problemen en politieke keuzes D. Gezondheid / welzijngames gezondheid of welzijn van spelers verbeteren E. Business / managementgames leren hoe het is om een bedrijf te runnen en de nodige vaardigheden te verwerven F. Militaire games deze hebben veel gelijkenissen met FPS (First Person Shooter) games. Deze proberen echter realistischere situaties te creëren dan FPS games. G. Commerciële games dit zijn games die verschijnen op hetzelfde niveau als de Triple-A games. Sommigen zijn bewust serious, anderen worden het onbewust (het was oorspronkelijk niet de bedoeling). Serious games zijn snel aan het groeien, maar het is nog steeds experimenteel en nog niet duidelijk afgebakend. De toekomst ervan is wel al verzekerd aangezien blijkt dat games een groter aandeel krijgen in de tijd die mensen besteden aan entertainment.
H9 : Wat zijn serious games ?
40
Hoofdstuk 10 : Onderzoek : Is XNA geschikt voor serious games ? XNA zou dankzij zijn mogelijkheid om samen te werken met het .NET Framework kunnen gebruikt worden voor het maken van complexe serious games. Indien men het .NET Framework bij de ontwikkeling betrekt, is de mogelijkheid om te ontwikkelen op Xbx 360 afhankelijk van de gebruikte libraries. Om te werken moeten deze libraries ook tot het .NET Compact Framework behoren. Maar gelukkig is XNA alleen ook krachtig genoeg om serious games te ontwikkelen. De definitie van serious games zou zelfs beter passen bij een pure XNA game gezien het .NET Framework geen gametechnologie is. Uit het onderzoek is gebleken hoe nieuw de termen XNA en serious games zijn. Een aantal van de geïnteresseerden hoorde het donderen in Keulen bij het vermelden van XNA. De game-industrie daarentegen is er wel van op de hoogte. Serious games werd door sommige mensen begrepen als commerciële spellen. Ze hadden serious letterlijk als serieus opgevat en dachten dat het over Triple-A games ging. Dit is zeker een bewijs dat de term nog geen wereldwijde erkenning heeft. Er zijn nog niet veel bedrijven die XNA momenteel als ontwikkeltechnologie gebruiken. De meesten gebruiken C++ om te coderen en men denkt dus dat er weinig bedrijven zijn die zullen overschakelen naar XNA omdat deze meestal al heel wat werk gestoken hebben in de zelfontwikkelde engines. Deze omzetten naar C# zou veel werk en tijd vergen. Indie (independent) game developers zien wel het nut van XNA en enkelen van hen gebruiken het reeds. De ontwikkeling gaat vlugger en XNA kan het misschien terug mogelijk maken om één persoon een complete game te laten maken, zoals men vroeger dus nog heeft gedaan. Het grootste deel van de mensen die XNA al geprobeerd hebben, is zeer positief over wat het ontwikkelen zelf betreft. Velen denken dat studenten en hobbyisten de groep zal blijven die met XNA zal werken, aangezien het speciaal voor die doelgroep ontwikkeld is. Sommigen denken zelfs dat je met XNA geen complexe games kan maken. Één persoon heeft de opmerking gemaakt dat als XNA ook geschikt zou zijn om webgames te ontwikkelen, er misschien meer mensen interesse in XNA zouden hebben. In Europa zijn niet veel mensen opgetogen over het feit dat je voor de Zune zal kunnen ontwikkelen, omdat het apparaat hier nog niet verschenen is. De enkelingen die het nuttig vinden, zijn mensen die het ontwikkelen voor meerdere platformen interessant vinden. In Noord-Amerika is men wel positief tegenover de Zune ondersteuning. Opvallend is dat men de mogelijkheid om te ontwikkelen voor de Xbox 360 als een overtuigend argument ziet om XNA te gebruiken. Maar indien XNA de mogelijkheid zou bieden om te ontwikkelen voor handheld systemen, vindt de meerderheid dit geen overtuigend argument.
H10 : Onderzoek : Is XNA geschikt voor serious games ?
41
Ben Sawyer, co-directeur van het Serious Games Initiative, heeft persoonlijk op mijn e-mail geantwoord. Hij denkt dat de mogelijkheid om naar Xbox 360 te ontwikkelen interessante ideeën kan opleveren. De mogelijkheid om in de toekomst naar de Zune te kunnen ontwikkelen, ziet hij als een manier om draagbare serious games te testen. Over het algemeen is elke game ontwikkelomgeving en elke game technologie van gelijke waarde om serious games te maken. Hij vermeldt ook nog even de Delta 3D engine. Dit is een open source 3D engine die speciaal gebruikt wordt om games en serious games te maken. Deze zou volgens hem goed zijn voor bepaalde types serious games. Deze engine is echter enkel geschikt om 3D games te maken en niet geschikt om te ontwikkelen naar consoles toe. Enkele bedrijven die serious games maken en gereageerd hebben op mijn enquête, blijken gebruik te maken van Quest3D. Het schijnt eenvoudig in gebruik te zijn maar toch gelimiteerder in technologie en functies dan XNA. De software is ook betalend. Om het spel op de Xbox 360 te krijgen moet je lid zijn van de Creators Club. Dit vormt niet zo direct een probleem voor bedrijven, maar het downloaden van de games wel. Hiervoor moet je namelijk ook een Creators Club memberschip hebben en aangezien serious games een ruimere doelgroep willen bereiken dan enkel hardcore gamers, is dit toch een groot struikelblok wat Xbox 360 ontwikkeling betreft. Indien men echter XNA in combinatie met de XDK (Xbox Development Kit) gebruikt, kan men wel games op de normale wijze aan de mensen aanbieden. De meeste mensen vinden Nintendo’s Wii geschikter om serious games voor te ontwikkelen dan Microsoft’s Xbox 360. Dit komt natuurlijk omdat de Wii controller bewegingen registreert en men zo bepaalde handelingen realistischer kan laten uitvoeren dan via het indrukken van een paar knoppen. Veel mensen zeiden dat het eigenlijk niet uitmaakt voor serious games met welk ontwikkelplatform je werkt. Voor serious games zijn alle game ontwikkel omgevingen van gelijke waarde. Het draait om de inhoud, niet om de technische achtergrond.
Besluit Kan XNA gebruikt worden voor serious games ? Volgens de pure definitie van serious games kan het zeker. De serious games bedrijven zelf zijn nog niet helemaal overtuigd om XNA te gebruiken, maar geïnteresseerden in de game wereld zien zeker de mogelijkheid om XNA te gebruiken bij de ontwikkeling van serious games. Sommige indie developers maken er al gebruik van. De handheld markt zou waarschijnlijk wel een gat in de markt kunnen zijn voor serious games. Aangezien er niet veel mensen XNA voor handheld game development zouden willen gebruiken en Ben Sawyer de Zune zou gebruiken om handheld serious games te testen, zou je je hierop kunnen toeleggen om er je specialisatie van te maken. Omdat XNA nog betrekkelijk nieuw is, zijn er reeds concurrenten op de markt die al langer actief zijn. XNA zal zich dus via zijn grote community moeten bewijzen vooraleer serious games bedrijven en zelfs game bedrijven in het algemeen, er gebruik van zullen maken.
H10 : Onderzoek : Is XNA geschikt voor serious games ?
42
Deel III : Algemeen besluit
Algemeen besluit XNA is een ideale omgeving om te leren hoe je games maakt. Iedereen die interesse heeft in games moet het zeker eens uitproberen. Tracht dit te doen terwijl je nog student bent om gebruik te kunnen maken van Microsoft Dreamspark en je zo van alle programma’s de professionele versie gratis ter beschikking hebt. Om met XNA aan de slag te gaan, is het wel aangeraden om kennis te hebben over programmeren en bij voorkeur over de programmeertaal C#. XNA is ook de eerste technologie die de springplank naar een console toe vormt. Nu kun je eindelijk zelf ontdekken hoe het is om een game voor console te maken. De bestandstypes die compatibel zijn, zijn momenteel beperkt, maar daar zal hopelijk in de toekomst verandering in komen. Dit eindwerk heeft de basis gelegd om te starten met XNA, maar het echte werk begint nu pas. Ga op verkenning en tracht zelf een game te maken. Is XNA geschikt voor serious games ? Ja. Gezien de wijde definitie van serious games, kan XNA zeker aangewend worden om iets te maken dat een ander doel heeft dan entertainment. XNA moet zich op sommige vlakken nog bewijzen voor de professionele wereld, maar de grote community zal hier zeker aan meehelpen.
Algemeen besluit
44
Bronnen Boeken C. Carter, Microsoft® XNA™ Unleashed : Graphics and Game Programming for Xbox 360 and Windows. Sams, Indianapolis, 2007. S. Cawood, P. McGee, Microsoft® XNA™ Game Studio Creator’s Guide : An Introduction to XNA Game Programming. McGraw-Hill, United States, 2007. H. M. Deitel, P. J. Deitel, C# for Programmers, Prentice Hall PTR, New Jersey, 20062. M. Nachtegael, J. Buysse, Wiskundig Vademecum : Een synthese van de leerstof wiskunde. Uitgeverij Pelckmans, Kapellen, 20017. B. Nitschke, Professional XNA™ Game Programming : For Xbox 360™ and Windows®. Wiley Publishing, Inc., Indianapolis, 2007. J. Thompson, B. Berbank-Green, N. Cusworth, The Computer Game Design Course : principles, practices and techniques for the aspiring game designer. Thames & Hudson Ltd, London, 2007. P. Vandaele, Wiskunde in het Grafische - Algebra, Cursus. Arteveldehogeschool, Gent, 2005. Artikels R. Fransen, R. Moscou, .net magazine, Codeglue ontwikkelt game in XNA Game Studio 2.0, 7 april 2008, nr. 20, pag. 18-19, Diegem 3D World, 3D World , Games get serious, 7 december 2007, nr. 98, Bath Web GROOTJANS, R., Riemer’s XNA Tutorials, on line, http://www.riemers.net, 12 februari - 14 april 2008 MICROSOFT CORPORATION, verschillende delen van website, on line, http://creators.xna.com , 12 februari - 27 april 2008 MICROSOFT CORPORATION, verschillende delen van website, on line, http://msdn2.microsoft.com/ en-us/xna/default.aspx, 12 februari - 27 april 2008 THE SERIOUS GAMES INITIATIVE, verschillende delen van website, on line, http://www.seriousgames. org/index2.html, 17 april 2008 THE SERIOUS GAMES INSTITUTE, verschillende delen van website , on line, http://www.seriousgamesinstitute.co.uk, 16 februari 2008
Bronnen
45
SAWYER, B., The History and Future of Serious Games, http://content.digitalwell.washington.edu/msr/ external_release_talks_12_05_2005/14739/lecture.htm, 17 april 2008 Andere Media COX, C., Beginner’s guide to XNA Game Studio Express, DVD-rom, Windows, Microsoft, Redmond, 2007 KETS, B., XNA and Beyond : Overview, PowerPoint Presentatie, Brecht Kets, Kortrijk, 2008 MICROSOFT, XNA 2.0 Launch Event, evenement, Microsoft, Mechelen, 29 november 2007 VANDEN ABEELE, V., Serious Games : Paradox of Paradigma, PowerPoint Presentatie, Groep T, 2007
Bronnen
46
Bijlagen
A. Systeemvereisten & installatie Processor : 600 MHz, 1GHz aangeraden RAM : 192 MB, 256 MB aangeraden Hard Disk ruimte : • Visual Studio 2005 Zonder MSDN : 1GB op de systeemschijf, 2GB op de installatieschijf Met MSDN : 1 GB op systeemschijf, 3,8 GB op installatieschijf indien MSDN volledig geïnstalleerd moet worden, 2,8 GB indien MSDN default geïnstalleerd wordt • Visual C# 2005 Express Edition Basis capaciteit : 40 MB op de systeemschijf, 160 MB op de installatieschijf .NET Framework : bijkomende 24 MB op de systeemschijf, bijkomende 300 MB op de installatieschijf Optioneel : met MSDN Express : bijkomende 166 MB op systeemschijf, bijkomende 600 MB op installatieschijf Operating System : Windows XP Service Pack 2, Windows Vista Visual Studio : Visual Studio 2005 Service Pack 1 of Visual C# Express Edition Sevice Pack 1 Grafische kaart : moet DirectX 9.0c en Shader Model 1.1 ondersteunen (Shader Model 2.0 is aangeraden en sommige Starter Kits maken er ook gebruik van) .Net Framework 1.1 is nodig om te kunnen werken met XACT Visual Studio 2005 biedt de meeste mogelijkheden, maar als je hier niet over beschikt kun je Visual C# 2005 Express Edition installeren. Vergeet niet dat je deze versie moet reserveren vooraleer XNA zal werken. Microsoft Visual C# 2005 Express Edition, Service Pack 1 voor Microsoft Visual Studio 2005 en Service Pack 1 voor Microsoft Visual Studio C# 2005 Express Edition kun je terug vinden op de bijgeleverde cd-rom. Hier volgt geen uitgebreide uitleg over hoe je de zaken installeert. De wizards zijn er net om het de gebruiker duidelijk te maken wat hij aan het installeren is. Volgorde van installeren : • Visual Studio 2005 of Visual C# 2005 Express Edition • Service Pack 1 voor Visual Studio 2005 of Service Pack 1 voor Visual C# Express Edition • XNA Game Studio 2.0
Bijlagen
B. XNA Basic File Game1.cs using using using using using using using using using using
System; System.Collections.Generic; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Audio; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.GamerServices; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Net; Microsoft.Xna.Framework.Storage;
namespace MijnEersteXNAGame { /// <summary> /// This is the main type for your game /// public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = “Content”; } /// <summary> /// Allows the game to perform any initialization it needs to before starting /// to run. /// This is where it can query for any required services and load any non/// graphic related content. Calling base.Initialize will enumerate through /// any components and initialize them as well. /// protected override void Initialize() { // TODO: Add your initialization logic here }
base.Initialize();
/// <summary> /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); }
// TODO: use this.Content to load your game content here
Bijlagen
/// <summary> /// UnloadContent will be called once per game and is the place to unload /// all content. /// protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } /// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// <param name=”gameTime”>Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); // TODO: Add your update logic here }
base.Update(gameTime);
/// <summary> /// This is called when the game should draw itself. /// /// <param name=”gameTime”>Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); // TODO: Add your drawing code here
}
}
}
base.Draw(gameTime);
Bijlagen
C. Afbeeldingen & Tekst using using using using using using using using using using
System; System.Collections.Generic; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Audio; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.GamerServices; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Net; Microsoft.Xna.Framework.Storage;
namespace MijnEersteXNAGame { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D achtergrond; SpriteFont spriteFont; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = “Content”; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); achtergrond = Content.Load(“XnaLogo3D”); }
spriteFont = Content.Load<SpriteFont>(“SpriteFont1”);
protected override void UnloadContent(){ } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); }
base.Update(gameTime);
Bijlagen
protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); spriteBatch.Draw(achtergrond, new Vector2(0, 0), Color.White); // Alternatief: // spriteBatch.Draw(achtergrond, Vector2.Zero, Color.White); spriteBatch.DrawString(spriteFont, Tekst_opslitsen.DoeHet(“Tekst in een blok van 20 karakters per lijn.”,20), new Vector2(20, 20), Color.White); spriteBatch.End();
}
}
}
base.Draw(gameTime);
Bijlagen
D. Transformatiematrices Schalen
Draaiing ten opzichte van de X-as over een hoek α 1
Draaiing ten opzichte van de Y-as over een hoek α
Draaiing ten opzichte van de Z-as over een hoek α
Translatie of verschuiven
Tx = het getal die bij de X-waarde van een coördinaat opgeteld wordt Ty = het getal die bij de Y-waarde van een coördinaat opgeteld wordt Tz = het getal die bij deZ-waarde van een coördinaat opgeteld wordt
Bijlagen
E. 3D in XNA using using using using using using using using using using
System; System.Collections.Generic; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Audio; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.GamerServices; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Net; Microsoft.Xna.Framework.Storage;
namespace MijnEersteXNAGame { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Model mijnModel; float rotYas; Vector3 cameraPositie; Vector3 cameraKijkrichting; Vector3 cameraOmhoog; float gezichtsVeld; float aspectRatio; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = “Content”; } protected override void Initialize() { cameraPositie = new Vector3(0.0f, 1000.0f, 1000.0f); cameraKijkrichting = new Vector3(0.0f, 0.0f, 0.0f); // Alternatief: cameraKijkrichting = Vector3.Zero; cameraOmhoog = new Vector3(0.0f, 1.0f, 0.0f); // Alternatief: cameraOmhoog = Vector3.Up; gezichtsVeld = MathHelper.ToRadians(45.0f); aspectRatio = graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height; base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); }
mijnModel = Content.Load<Model>(“p1_saucer”);
Bijlagen
protected override void UnloadContent(){ } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); rotYas += MathHelper.ToRadians(1.5f); if (rotYas >= MathHelper.ToRadians(360.0f)) { rotYas = 0.0f; } }
base.Update(gameTime);
protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); foreach (ModelMesh mesh in mijnModel.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.World = Matrix.CreateScale(0.2f) * Matrix.CreateRotationY(rotYas); effect.View = Matrix.CreateLookAt(cameraPositie, cameraKijkrichting, cameraOmhoog); effect.Projection = Matrix.CreatePerspectiveFieldOfView( gezichtsVeld, aspectRatio, 1.0f, 10000.0f); } }
}
}
}
mesh.Draw();
base.Draw(gameTime);
Bijlagen
F. Geluid in XNA using using using using using using using using using using
System; System.Collections.Generic; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Audio; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.GamerServices; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Net; Microsoft.Xna.Framework.Storage;
namespace MijnEersteXNAGame { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Model mijnModel; float rotYas; Vector3 cameraPositie; Vector3 cameraKijkrichting; Vector3 cameraOmhoog; float gezichtsVeld; float aspectRatio; AudioEngine audioEngine; WaveBank waveBank; SoundBank soundBank; Cue cue; float cueVar; bool afzwakken; float draaiSnelheid; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = “Content”; } protected override void Initialize() { cameraPositie = new Vector3(0.0f, 1000.0f, 1000.0f); cameraKijkrichting = new Vector3(0.0f, 0.0f, 0.0f); // Alternatief: cameraKijkrichting = Vector3.Zero; cameraOmhoog = new Vector3(0.0f, 1.0f, 0.0f); // Alternatief: cameraOmhoog = Vector3.Up; gezichtsVeld = MathHelper.ToRadians(45.0f); aspectRatio = graphics.GraphicsDevice.Viewport.Width / graphics.GraphicsDevice.Viewport.Height;
Bijlagen
cueVar = 12.0f; afzwakken = true; draaiSnelheid = 12.0f; }
base.Initialize();
protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); mijnModel = Content.Load<Model>(“p1_saucer”);
}
audioEngine = new AudioEngine(“Content\\Audio.xgs”); waveBank = new WaveBank(audioEngine, “Content\\Wave Bank.xwb”); soundBank = new SoundBank(audioEngine, “Content\\Sound Bank.xsb”);
protected override void UnloadContent(){ } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit(); rotYas += MathHelper.ToRadians(draaiSnelheid); if (rotYas >= MathHelper.ToRadians(360.0f)) { rotYas = 0.0f; cue = soundBank.GetCue(“menu_select2”); cue.Play(); if (afzwakken) { cueVar -= 1.0f; draaiSnelheid -= 0.4f; if (cueVar <= -12.0f) { afzwakken = false; } cue.SetVariable(“PitchVar”, cueVar); } else { cueVar += 1.0f; draaiSnelheid += 0.4f; if (cueVar >= 12.0f) { afzwakken = true; }
Bijlagen
} }
}
cue.SetVariable(“PitchVar”, cueVar);
base.Update(gameTime);
protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); foreach (ModelMesh mesh in mijnModel.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.World = Matrix.CreateScale(0.2f) * Matrix.CreateRotationY(rotYas); effect.View = Matrix.CreateLookAt(cameraPositie, cameraKijkrichting, cameraOmhoog); effect.Projection = Matrix.CreatePerspectiveFieldOfView( gezichtsVeld, aspectRatio, 1.0f, 10000.0f); } }
}
}
}
mesh.Draw();
base.Draw(gameTime);
Bijlagen
Academiejaar 2007-2008
Jens Wouters