Hands-OnLab
GameDevelopmentwithXNAFramework
Labversion:2.0.0
Lastupdated:11/14/2018
Contents
Overview
Exercise 1: XNA Game Studio Games on the Windows Phone 7
Task 1 – XNA Game Studio Game Basics
Task 2 – XNA Framework Game Resources
Task 3 – XNA Game Studio Game Loop
Task 4 – Game Specific Logic
Task 5 – Lifetime event handling
Summary
Overview
ThislabintroducesyoutoXNAGameStudiogamedevelopmentonWindowsPhones,aswellastothebasicsofXNAGameStudiogamedevelopment.DuringthelabyouwillbuildasimpleXNAGameStudiogameapplicationthatintroduceskeyconceptsinXNAGameStudiogamedevelopmentandlearnhowtouseMicrosoftVisual2010ExpressforWindowsPhonetobuildanddesignyourXNAGameStudiogamesforWindowsPhones.
Objectives
Attheendofthelabyouwillhave:
- Ahigh-levelunderstandingoftheXNAGameStudiogameenginemodelwithinaWindowsPhone7application
- Learnedhowtouseresources(images,fonts,etc.)inyourXNAGameStudiogame
- Learnedhowtoaddgamelogic
- LearnedaboutthedrawingmechanismforWindowsPhoneXNAGameStudiogames
Prerequisites
Thefollowingisrequiredinordertocompletethishands-onlab:
- MicrosoftVisualStudio2010ExpressforWindowsPhoneorMicrosoftVisualStudio2010
- WindowsPhoneDeveloperTools
Note:Todownloadthetools,goto
Tasks
Thishands-onlabincludesthefollowingtasks:
- XNAGameStudioGameBasics
- XNAFrameworkResources
- XNAGameStudioGameLoop
- XNAGameStudioGameInput
- AlienGameSpecificLogic
Estimatedtimetocompletethislab:60minutes.
Exercise1:XNAGameStudioGamesontheWindowsPhone7
Ifyouhaveeverwantedtomakeyourowngames,Microsoft®XNA™GameStudio4.0isforyou.Student,hobbyist,independentgamedeveloper—anybodycancreateandsharegreatgamesusingXNAGameStudio.
XNAGameStudio4.0isagamedevelopmentproductfromMicrosoftthatisbuiltontopoftheMicrosoftVisualStudio2010ExpressforWindowsPhone,givinggamedevelopersthepowerandsimplicityofC#asaprogramminglanguage.XNAGameStudio4.0includestheXNAFrameworkandtheXNAFrameworkContentPipeline,whichprovideaneasyandflexiblewaytoimportthree-dimensional(3D)models,textures,sounds,andotherassetsintoyourgame,andagame-focusedapplication-programminginterface(API)thatsimplifiesdevelopmentforXbox360®,Windows®andnowWindowsPhone7®.
TheXNAFrameworkisanapplication-programminginterface(API).WhatthatmeansisthatitisaframeworkdevelopedbyMicrosofttohelpyoumakegamesfaster.However,it'snotadraganddropgamemakerandyouwillneedtolearnhowtoprogrambeforeyoucanuseit.Itiseasytouse,butyouwillhavetobesomewhattechnicaltodevelopgameswithit.
TheXNAFrameworkisnotagameengine.Itdoesnotincludephysics,collisiondetection,orotherthingsoftenfoundingameengines.Itisagamedevelopmentframework,buthowthegameworksisprogrammedentirelybyyou.
DuringthislabyouwillbuildafullXNAGameStudiogamefortheWindowsPhone–“AlienGame”–asimpleshootergame.ThegoalinAlienGameissimple:Protectearthagainsttheinvadingaliensforaslongaspossible.Thelongeryoulast,themoredifficultthegamebecomes.Watchoutforthesmalleraliensthatcomeoutatnight!
GeneralArchitecture
AlienGameusesthegamescreenmanagementarchitecturefromtheGameStateManagementsample(foundat
- Mainmenu(MainMenuScreenclass)
- Playingthegame(GameplayScreenclass)
- Paused(PauseScreenclass)
AlienGameperformsallcontentloadingatstartup.ThefirstthingitdoesistoloadanddisplaytheBackgroundScreen(whichservestodisplayabackgroundforboththemainmenuandpausescreens,butdoesnotrepresentastate).Next,itloadsanddisplaystheLoadingScreen,whichloadstherestofthegame’scontentasynchronously.TheLoadingScreencoulddisplaysomeformofprogress,buttheloadingtimesaresoshortinthissamplethatdisplayingprogresswouldbepointless.Onceallofthecontentisloaded,theMainMenuScreenisloadedanddisplayed,andthemenusanimateontothescreen.Thisallowsforquickertransitionsbetweenthemainmenuandgameplay,withoutalengthypauseasthecontentisloaded.Thishelpsthehard-drive-baseddevicesespecially,astheharddrivemayspindown.
GameplayScreenandGameClasses
ThedesignandimplementationofAlienGameisrelativelysimple.AlllogicanddrawingiscontainedintheGameplayHelperclass.Thefilecontainingthegameplayhelper,GameplayHelper.cs,containssomeadditionalclassesthatrepresentthegame’sentities,namelyBullet,Alien,andPlayer.Thegameplayscreenemploysthegameplayhelpertopresenttheuserwiththeactualgame.
ParticleSystem
AlienGameincludesasimplesprite-basedparticlesystemthatdisplaysexplosionanddusteffects.
Thecompletedgamewilllookasfollows:
Figure1
AlienGamerunningonWindowsPhone
XNAGameStudioGameBasics
Agametypicallyconsistsofmultiplelevels.Levelsareconnectedtoeachotherbythegameplot,gameplayer,enemies,etc.Eachlevelcanbetreatedasacomplete,small,game.
Alevelusuallyhasthreestates:
- Loading–Inthisstate,thesystemloadsresources,setsuplevel-relatedvariables,calculatesthegame-world(game-worldistheplacewhereallthegameprocessoccurs),andperformsanyothertasksthatmustbeperformedbeforethegameactuallybegins.Thisstateoccursonlyonceinthelevel/gamelifecycle.
- Update–Inthisstate,thesystemneedstoupdatethegame-worldstate.Usuallythismeanscalculatingnewpositionsforthevariousgameentities(player(s)andenemies),updatinghealth,ammo,andotherstatesandrecalculatingthescoreandothergamelogicaccordingtothegame.Thisstateoccursthroughoutthetimethatthegameengineisactive.
- Draw–Inthisstate,thesystemdrawsthechangescalculatedintheupdatestatetotheoutputgraphicsdevice.Thisstateoccursthroughoutthetimethatthegameengineisactive.
IntheXNAFramework,thelasttwostagescanoccurupto60timespersecondonaPCorXbox360andupto30timespersecondonaZune,ZuneHDorWindowsPhone7device.
Task1–XNAGameStudioGameBasics
Inthissection,youwillcreateyourfirstXNAGameStudiogamefortheWindowsPhone.Thegamewillbeasimpleone,butyouwilladdfunctionalitythroughoutthelab.
Note:Thestepsinthishands-onlabillustrateproceduresusingMicrosoftVisualStudio2010ExpressforWindowsPhone,buttheyareequallyapplicabletoMicrosoftVisualStudio2010withtheWindowsPhoneDeveloperTools.InstructionsthatrefergenericallytoVisualStudioapplytobothproducts.
- OpenMicrosoftVisualStudio2010ExpressforWindowsPhonefromStart|AllPrograms|MicrosoftVisualStudio2010Express|MicrosoftVisualStudio2010ExpressforWindowsPhone.
Note:VisualStudio2010:OpenVisualStudio2010fromStart|AllPrograms|MicrosoftVisualStudio2010.
- IntheFilemenu,chooseNewProject.
- IntheNewProjectdialog,selecttheXNAGameStudio4.0forWindowsPhonecategoryand,fromthelistofinstalledtemplates,selectWindowsPhoneGame(4.0).CallthenewprojectAlienGame.Whenthetargetphoneframeworkdialogappears,choose“WindowsPhone7.1”.
- InSolutionExplorer,reviewthestructureofthesolutiongeneratedbytheWindowsPhoneApplicationtemplate.AnyVisualStudiosolutionisacontainerforrelatedprojects;inthiscase,itcontainsanXNAGameStudiogameforWindowsPhoneprojectnamedAlienGameandarelatedgamesresourcesprojectnamedAlienGameContent.
- AddareferencetothenewlycreatedAlienGameprojectfortheMicrosoft.Phoneassembly.
- ThegeneratedprojectincludesadefaultgameimplementationthatcontainsthebasicXNAGameStudiogameloop.ItislocatedintheGame1.csfile.
- OpentheGame1.csfile.Werecommendthatyouchangethedefaultnametothenamethatreflectsyourgame.
- Renamethemaingameclass(defaultnameGame1)toAlienGame.
- Renamethefilenametomatchthenewclassname.Right-clickonGame1.csinSolutionExplorerandchooseRename.GivetheclassthenewnameAlienGame.cs.
- PressF5tolaunchtheapplicationintheWindowsPhoneEmulator.NoticethatadeviceemulatorwindowappearsandthereisapausewhileVisualStudiosetsuptheemulatorenvironmentanddeploystheimage.Onceitisready,theemulatorshowstheStartpageandshortlythereafter,yourapplicationappearsintheemulatorwindow.
Theapplicationwilldisplayasimplebluescreenwithnothingelseshown.Thisisnormaltoanapplicationinsuchearlystages.
Figure2
RunningtheapplicationintheWindowsPhoneEmulator
Untilyoucreatetheuserinterfaceandprogramtheapplicationlogic,thereisverylittlethatyoucandowiththeapplication.
- PressSHIFT+F5orclicktheStopbuttoninthetoolbartodetachthedebuggerandendthedebuggingsession.Donotclosetheemulatorwindow.
Task2–XNAFrameworkGameResources
Manygamesusingpre-definedimagestopresentthegame,processsound,etc.Thislabprovidesyouwithanumberofsuchresourcestomakethegamedevelopmentprocesseasier.Duringthistask,youwilladdthoseresourcestothegame.Thislabalsoprovidesanumberofcodefilestohandlethecomplexityofmenuandscreenchangesduringthegame.Youwilladdthosefilestothegamealso.
Note:Allthegameresourcesareprovidedinthelabinstallfolderunderthefollowinglocations:
Source\Assets\Code–allCSharpcodefiles
Source\Assets\Media–allgraphics,fontsandsounds
- ClosetheprojectinVisualStudio.SwitchtoWindowsExplorer,navigatetotheprojectlocationandcopytwofilesfromtheSource\Assets\Media\Images\Iconsfolderofthislabintothe“AlienGame”directory,replacingtheexistingfiles:
◦Game.ico
◦PhoneGameThumb.png
- Re-openVisualStudio2010andopentheAlienGameproject.
Mostgamesuseartintheformofmodels,meshes,sprites,textures,effects,terrains,animations,andsoon.Suchartassetscanbecreatedinmanydifferentwaysandstoredinmanydifferentfileformats.Theytendtochangefrequentlyinthecourseofgamedevelopment.TheContentPipelineisdesignedtohelpyouincludesuchartassetsinyourgameeasilyandautomatically.AnartistworkingonacarmodelcanaddtheresultingfiletotheXNAGameStudiogameproject,assignthemodelaname,andchooseanimporterandcontentprocessorforit.Then,adeveloperwhowantstomakethecardrivecanloaditbynameusingacalltoContentManager.Load.Thissimpleflowletstheartistfocusoncreatingassetsandthedeveloperfocusonusingthem,withouteitherhavingtospendtimeworryingaboutcontenttransformation.
TheXNAContentPipelineisreadilyintegratedintoyourXNAGameStudioproject.Youjustaddtheresourcetoyourprojectandwhenyoucompileit,thedataisimportedandconvertedinaXNB(XNABinaryFile)usingaContentImporter.ThisXNBfileisgeneratedfortherightplatform.ContentImportersareimplementedasassemblies.InadditiontothestandardonesprovidedbyXNAGameStudio,youcanalsousecustomimportersandprocessorsthatyouorotherthirdpartiesdevelop.SomeofstandardContentImportersincludethefollowingfiletypes(partiallist):
◦AutodeskFBXformat(.fbx)
◦DirectXEffectfileformat(.fx)
◦Fontdescriptionspecifiedina.spritefontfile
◦Texturefile.Thefollowingtypesaresupported:.bmp,.dds,.dib,.hdr,.jpg,.pfm,.png,.ppm,and.tga
◦GameaudiospecifiedintheMicrosoftCross-PlatformAudioCreationTool(XACT)format(.xap)
- Thislabprovidesanumberofmediaresourcessuchasfonts,sounds,andimages.AddthefollowingitemstotheAlienGameContentproject:
◦AllthefontsfromtheSource\Assets\Media\Fontsfolder
◦AlltheimagesfromtheSource\Assets\Media\Images\Contentfolder
◦AllthesoundsfromtheSource\Assets\Media\Soundsfolder
- AddanewprojectfolderundertheAlienGameprojectandnameitScreenManager.
Thisfolderwillholdsourcefilesprovidebythelab,whichwillhelptomanagethecomplexityofcreatinggamescreens,menus,andofnavigatingbetweenthem.
Note:ThiscodeimplementsthestandardapproachforcreatingXNAGameStudiomenusandscreensandmaybefoundatthefollowinglink:
- AddallexistingScreenManagerfilesfromtheSource\Assets\Code\ScreenManagerfolderofthislabtotheprojectfoldercreatedinthepreviousstep.
- AddtheParticleSystem.csfilefromtheSource\Assets\CodefoldertotherootoftheAlienGameproject.
- AddareferencetotheSystem.Windowsassembly.
- AddanewclasstotheAlienGameprojectandnameitBackgroundScreen:
- Openthenewclassandaddthefollowingusingdirectives:
C#
usingAlienGameSample;
usingMicrosoft.Xna.Framework.Graphics;
usingMicrosoft.Xna.Framework;
- DerivethenewclassfromtheGameScreenclass(theGameScreenclassisdefinedinclassesaddedpreviouslytotheScreenManagerfolder):
C#
classBackgroundScreen:GameScreen
{
}
- Addthefollowingclassvariablestobeusedlaterforloadingresources:
C#
Texture2Dtitle;
Texture2Dbackground;
- Defineaclassconstructorasfollows:
C#
publicBackgroundScreen()
{
TransitionOnTime=TimeSpan.FromSeconds(0.0);
TransitionOffTime=TimeSpan.FromSeconds(0.5);
}
- TheGameScreenClassdefinessomecoregamefunctionalityaccordingtowhatwasdescribedintheexercisepreface:LoadContent,Update,andDraw.Overridethebaseclass’sLoadContentfunctionality:
C#
publicoverridevoidLoadContent()
{
title=ScreenManager.Game.Content.Load<Texture2D>("title");
background=ScreenManager.Game.Content.Load<Texture2D>("background");
}
Thiscodeloadscontentfromthegame’sresources.Youcanseethatweloadtwopiecesofcontentbyname.
- NowcreateaLoadingScreenclass.Thescreensupportedbytheclasswillbepresentedwhilegameresourcesarebeingloaded.
- AddfollowingusingdirectivestotheLoadingScreenclass:
C#
usingAlienGameSample;
usingSystem.Threading;
usingMicrosoft.Xna.Framework;
usingMicrosoft.Phone.Shell;
- DerivethisnewclassfromtheGameScreenbaseclass(likeyoudidforthepreviousclass),andaddaconstructorasfollows:
C#
classLoadingScreen:GameScreen
{
publicLoadingScreen(boolshowPauseScreen)
{
TransitionOnTime=TimeSpan.FromSeconds(0.0);
TransitionOffTime=TimeSpan.FromSeconds(0.0);
this.showPauseScreen=showPauseScreen;
}
}
- Addacoupleoffields.Onetoholdthethreadwhichwillbeusedtoloadthecomponentsandanothertodeterminethetypeofscreenthatshowsupafterallcontenthasbeenloaded:
C#
privateThreadbackgroundThread;
privateboolshowPauseScreen;
- Createamethodtoloadthecontent.ThisapproachispartofthestandardloadingproceduresinXNAprogramming.Wewilldefertheactualloadingtothegameplayhelperclass,whichwewillcreatelaterinthistask:
C#
voidBackgroundLoadContent()
{
GameplayHelpergameplayHelper;
//Ifwehavenevercreatedagameplayhelperyet,nowisthetime.Otherwise,wesimplyneedtoreloaditscontent,possibly.
if(PhoneApplicationService.Current.State.ContainsKey(AlienGame.GameStateKey))
{
gameplayHelper=PhoneApplicationService.Current.State[AlienGame.GameStateKey]asGameplayHelper;
}
else
{
gameplayHelper=newGameplayHelper(ScreenManager.Game.Content,ScreenManager.SpriteBatch,
ScreenManager.Game.GraphicsDevice);
PhoneApplicationService.Current.State[AlienGame.GameStateKey]=gameplayHelper;
}
if((ScreenManager.GameasAlienGame).ReloadRequired)
{
gameplayHelper.InitializeAssets(ScreenManager.Game.Content,ScreenManager.SpriteBatch,ScreenManager.Game.GraphicsDevice);
gameplayHelper.LoadContent();
(ScreenManager.GameasAlienGame).GameplayHelper=gameplayHelper;
}
}
Theabovecodeisnotasstraightforwardassimplycreatingagameplayhelperandusingittoloadcontent,soletusreviewit.Oureventualgoalistohavethegameplayhelperclassnotonlyfacilitatethegame’slogicanddrawing,butalsoserveasarepresentationofthegame’scurrentstate.Tothatend,thegameplayhelperwillbeserializable,allowingustopreserveitsstateonceourgameisdeactivated.Thecodeaboveexaminesthegame’spersistablestatedictionaryforanexistinggameplayhelperorcreatesanewoneandplacesitinsaiddictionary.Then,ifanassetrealoadisnecessary,thegameplayhelperisinstructedtoloadthegame’sassetsandisthenstoredinanadditionallocationforeasyaccess.
Note:Toproperlyunderstandthecodeabove,youshouldbefamiliarwithapplicationlifecyclemanagementontheWindowsPhone7.Seethefollowinglink:
- Overridethebaseclass’sLoadContentmethodandinitiatethecontentloadingasanewthreadtoachieveasynchronousloadingoftheresources:
Note:Inoursimplegame,theresourceswillbeloadedalmostinstantaneously,butwhendealingwithgamesthataremorecomplex,thisapproachallowsshowingaprogressindicatororasplashscreen.
C#
publicoverridevoidLoadContent()
{
if(backgroundThread==null)
{
backgroundThread=newThread(BackgroundLoadContent);
backgroundThread.Start();
}
base.LoadContent();
}
- Overridethebaseclass’sUpdatemethodtowaitforcontentloadingtofinishandthenproceedtoeitherthemainmenuorpausescreens(addedinthenextsteps):
C#
publicoverridevoidUpdate(GameTimegameTime,boolotherScreenHasFocus,boolcoveredByOtherScreen)
{
if(backgroundThread!=nullbackgroundThread.Join(10))
{
backgroundThread=null;
this.ExitScreen();
if(showPauseScreen)
{
//TODO:Addpausescreen
}
else
{
ScreenManager.AddScreen(newMainMenuScreen());
}
ScreenManager.Game.ResetElapsedTime();
}
base.Update(gameTime,otherScreenHasFocus,coveredByOtherScreen);
}
Youcanseethatwe’veplacedaplaceholderwherewilllateraddthepausescreen.
- LetusintroducetheGameplayHelperclassusedintheloadingscreen.CreateanewclasswiththatnameintheAlienGameproject. Be sure to mark the class as public.
- AddareferencetotheAlienGameprojectfortheSystem.Xml.Serializationassembly.
- Addthefollowingusingdirectivestothenewclass’sfile,whichshouldbecalledGameplayHelper.cs(wewillrequireallofthemeventually):
C#
usingMicrosoft.Xna.Framework.Graphics;
usingMicrosoft.Xna.Framework;
usingMicrosoft.Xna.Framework.Audio;
usingMicrosoft.Xna.Framework.Content;
usingSystem.IO.IsolatedStorage;
usingSystem.IO;
usingMicrosoft.Xna.Framework.Input.Touch;
usingSystem.Xml.Serialization;
usingSystem.Xml;
usingSystem.Xml.Schema;
- AddthefollowingfieldstotheGameplayHelperclass:
C#
SoundEffectalienFired;
SoundEffectalienDied;
SoundEffectplayerFired;
SoundEffectplayerDied;
Texture2Dcloud1Texture;
Texture2Dcloud2Texture;
Texture2DsunTexture;
Texture2DmoonTexture;
Texture2DgroundTexture;
Texture2DtankTexture;
Texture2DalienTexture;
Texture2Dbadguy_blue;
Texture2Dbadguy_red;
Texture2Dbadguy_green;
Texture2Dbadguy_orange;
Texture2DmountainsTexture;
Texture2DhillsTexture;
Texture2DbulletTexture;
Texture2DlaserTexture;
SpriteFontscoreFont;
SpriteFontmenuFont;
ContentManagercontentManager;
SpriteBatchspriteBatch;
GraphicsDevicegraphicsDevice;
Thegameplayhelperwillusethebottomthreefieldsinthecodeaboveforcontentloadingandrendering.Theotherfieldswillbeusedtostoreloadedcontent.
- ChangetheGameplayHelperclasstoimplementIXmlSerializable.Addthefollowingplaceholdermethodswhichwewillfleshoutlaterinthelab:
C#
publicXmlSchemaGetSchema()
{
returnnull;
}
publicvoidReadXml(XmlReaderreader)
{
//TODO:Deserializethegameplaystate
}
publicvoidWriteXml(XmlWriterwriter)
{
//TODO:Serializethegameplaystate
}
- Addthefollowingconstructorfortheclass:
C#
publicGameplayHelper(ContentManagercontentManager,SpriteBatchspriteBatch,
GraphicsDevicegraphicsDevice)
{
//TODO:Performadditionalinitializations
InitializeAssets(contentManager,spriteBatch,graphicsDevice);
}
- AddtheInitializeAssetshelpermethodseenabove:
C#
publicvoidInitializeAssets(ContentManagercontentManager,SpriteBatchspriteBatch,GraphicsDevicegraphicsDevice)
{
this.contentManager=contentManager;
this.spriteBatch=spriteBatch;
this.graphicsDevice=graphicsDevice;
//TODO:Performadditionalinitializations
}
- AddtheLoadContentmethodusedinpreviousstepsforloadingcontent:
C#
publicvoidLoadContent()
{
cloud1Texture=contentManager.Load<Texture2D>("cloud1");
cloud2Texture=contentManager.Load<Texture2D>("cloud2");
sunTexture=contentManager.Load<Texture2D>("sun");
moonTexture=contentManager.Load<Texture2D>("moon");
groundTexture=contentManager.Load<Texture2D>("ground");
tankTexture=contentManager.Load<Texture2D>("tank");
mountainsTexture=contentManager.Load<Texture2D>("mountains_blurred");
hillsTexture=contentManager.Load<Texture2D>("hills");
alienTexture=contentManager.Load<Texture2D>("alien1");
badguy_blue=contentManager.Load<Texture2D>("badguy_blue");
badguy_red=contentManager.Load<Texture2D>("badguy_red");
badguy_green=contentManager.Load<Texture2D>("badguy_green");
badguy_orange=contentManager.Load<Texture2D>("badguy_orange");
bulletTexture=contentManager.Load<Texture2D>("bullet");
laserTexture=contentManager.Load<Texture2D>("laser");
alienFired=contentManager.Load<SoundEffect>("Tank_Fire");
alienDied=contentManager.Load<SoundEffect>("Alien_Hit");
playerFired=contentManager.Load<SoundEffect>("Tank_Fire");
playerDied=contentManager.Load<SoundEffect>("Player_Hit");
scoreFont=contentManager.Load<SpriteFont>("ScoreFont");
menuFont=contentManager.Load<SpriteFont>("MenuFont");
//TODO:Loadthehighscores
}
- AddanadditionalclassdefinitiontotheGameplayHelper.csfileasfollows:
C#
publicclassPlayer
{
publicVector2Position;
publicVector2Velocity;
publicfloatWidth;
publicfloatHeight;
publicboolIsAlive;
publicfloatFireTimer;
publicfloatRespawnTimer;
publicstringName;
publicintScore;
publicintLives;
}
- Nowaddanewclass(inanewfile)andnameitMainMenuScreen.Whencreated,addthefollowingusingstatementstoit:
C#
usingAlienGameSample;
usingMicrosoft.Phone.Shell;
- DerivethenewclassfromtheMenuScreenbaseclass.ThisclassisalsodefinedinclassesaddedtotheScreenManagerfolderandfacilitatesalltypicalfunctionalityneededtoshow/interactwithmenusandmenuitems.
- CreatetheMainMenuScreenclassconstructor:
C#
classMainMenuScreen:MenuScreen
{
publicMainMenuScreen()
:base("Main")
{
//Createourmenuentries.
MenuEntrystartGameMenuEntry=newMenuEntry("STARTGAME");
MenuEntryexitMenuEntry=newMenuEntry("QUIT");
//Hookupmenueventhandlers.
startGameMenuEntry.Selected+=StartGameMenuEntrySelected;
exitMenuEntry.Selected+=OnCancel;
//Addentriestothemenu.
MenuEntries.Add(startGameMenuEntry);
MenuEntries.Add(exitMenuEntry);
}
}
- Intheaboveconstructor,wesubscribedtotwoevents,whichwillfirewhentheuserselectsmenuitems.Createtheeventhandlermethodstohandlethoseevents:
C#
voidStartGameMenuEntrySelected(objectsender,EventArgse)
{
//TODO:Movetotheactualgame
}
protectedoverridevoidOnCancel()
{
ScreenManager.Game.Exit();
}
- OpentheAlienGame.csfileandaddthefollowingusingstatement:
C#
usingAlienGameSample;
usingMicrosoft.Phone.Shell;
usingSystem.IO.IsolatedStorage;
- DeletetheentirecontentsoftheAlienGameclass.
- AddthefollowingclassmemberstotheAlienGameclass:
C#
publicconststringGameStateKey="GameState";
publicconststringInGameKey="InGame";
///<summary>
///Whetherornotassetreloadingisrequired.
///</summary>
publicboolReloadRequired{get;set;}
///<summary>
///Allowsstoringthegame'sgameplayhelper.
///</summary>
publicGameplayHelperGameplayHelper{get;set;}
GraphicsDeviceManagergraphics;
ScreenManagerscreenManager;
- AddthefollowingconstructortotheAlienGameclass:
C#
publicAlienGame()
{
graphics=newGraphicsDeviceManager(this);
//SettheWindowsPhonescreenresolution
graphics.PreferredBackBufferWidth=480;
graphics.PreferredBackBufferHeight=800;
Content.RootDirectory="Content";
//Hookuplifecycleevents
PhoneApplicationService.Current.Launching+=Game_Launching;
PhoneApplicationService.Current.Activated+=Game_Activated;
PhoneApplicationService.Current.Closing+=Game_Closing;
//Framerateis30fpsbydefaultforWindowsPhone.
TargetElapsedTime=TimeSpan.FromSeconds(1/30.0);
//CreateanewinstanceoftheScreenManager
screenManager=newScreenManager(this);
Components.Add(screenManager);
}
Theaboveconstructorinitializesthescreenmanagerandregisterstosomeofthegame’slifecycleevents.
- Addthehandlersforthevariouslifecycleevents:
C#
privatevoidGame_Closing(objectsender,ClosingEventArgse)
{
//TODO:Performcleanup
}
privatevoidGame_Activated(objectsender,ActivatedEventArgse)
{
//TODO:Handlereactivation
}
privatevoidGame_Launching(objectsender,LaunchingEventArgse)
{
ReloadRequired=true;
PhoneApplicationService.Current.State[InGameKey]=false;
//Displaythemainscreen
screenManager.AddScreen(newBackgroundScreen());
screenManager.AddScreen(newLoadingScreen(false));
}
- Compileandruntheapplication.Aftertheapplicationloads,themainmenuscreenshouldappear:
Figure3
Thegame’smainmenu
- Stopthedebuggingandreturntoeditingtheapplication.
Duringthistask,youaddedresourcestothegameandcreatedanumberofscreenstopresenttheuserwithsomebasicuserinterfaceswhileloadingthegame.Youalsocreatedthemainmenu.
Task3–XNAGameStudioGameLoop
Inthistask,youwillfocusonhavingthevariousgamescreensupdateanddrawproperly.
- OpenBackgroundScreen.cs.
- OverridethebaseclassUpdatemethodasfollows:
C#
publicoverridevoidUpdate(GameTimegameTime,boolotherScreenHasFocus,
boolcoveredByOtherScreen)
{
base.Update(gameTime,otherScreenHasFocus,false);
}
- OverridethebaseclassDrawmethod.TheDrawmethodwillusetheSpriteBatchclassfromtheMicrosoft.Xna.Framewok.Graphicsnamespacetodrawonthegraphicsdevice.Itenablesagroupofspritestobedrawnusingthesamesettings.DefineaDrawmethodmatchingthefollowingcode:
C#
publicoverridevoidDraw(GameTimegameTime)
{
SpriteBatchspriteBatch=ScreenManager.SpriteBatch;
spriteBatch.Begin();
//DrawBackground
spriteBatch.Draw(background,newVector2(0,0),
newColor(255,255,255,TransitionAlpha));
//DrawTitle
spriteBatch.Draw(title,newVector2(60,55),
newColor(255,255,255,TransitionAlpha));
spriteBatch.End();
}
- PressF5tocompileandruntheapplication.
Figure4
Thegameafterhavingthebackgroundscreenupdateanddraw
- Stopthedebugging(SHIFT+F5)andreturntoeditingtheapplication.Ournextgoalistocreatethegameplayscreenandenhancethegameplayhelpertosupportthefullyfunctionalgame.
- AddareferenceintheAlienGameprojecttotheMicrosoft.Devices.Sensorsassembly.
- AddanadditionalclasstotheapplicationandsetitsnametoGameplayScreen.
- Addthefollowingusingstatementstothenewclass:
C#
usingAlienGameSample;
usingMicrosoft.Xna.Framework;
usingMicrosoft.Xna.Framework.Graphics;
usingMicrosoft.Xna.Framework.Audio;
usingMicrosoft.Xna.Framework.Input;
usingMicrosoft.Xna.Framework.Input.Touch;
usingMicrosoft.Devices.Sensors;
usingMicrosoft.Phone.Shell;
- DerivetheclassfromGameScreen.
C#
classGameplayScreen:GameScreen
{
}
- AddfollowingFielddefinitions:
C#
SensorReadingEventArgsAccelerometerReadingaccelState;
AccelerometerAccelerometer;
GameplayHelpergameplayHelper;
- Addthefollowingconstructor,whichwillinitializethehandlingofaccelerometerinput:
C#
publicGameplayScreen()
{
TransitionOnTime=TimeSpan.FromSeconds(0.0);
TransitionOffTime=TimeSpan.FromSeconds(0.0);
Accelerometer=newAccelerometer();
if(Accelerometer.State==SensorState.Ready)
{
Accelerometer.CurrentValueChanged+=(s,e)=>
{
accelState=e;
};
Accelerometer.Start();
}
}
- Overridethebaseclass’sLoadContentmethodtogetthecurrentgameplayhelperandstatethatwearenowinanactivegame:
C#
publicoverridevoidLoadContent()
{
gameplayHelper=PhoneApplicationService.Current.State[AlienGame.GameStateKey]asGameplayHelper;
PhoneApplicationService.Current.State[AlienGame.InGameKey]=true;
base.LoadContent();
}
- Overridethebaseclass’sDrawandUpdatemethods.Thesewillsimplycallthecorrespondingmethodsonthegameplayhelper(wewilladdtheselateron):
C#
publicoverridevoidUpdate(GameTimegameTime,boolotherScreenHasFocus,boolcoveredByOtherScreen)
{
floatelapsedSeconds=(float)gameTime.ElapsedGameTime.TotalSeconds;
if(IsActive)
{
gameplayHelper.Update(elapsedSeconds);
}
base.Update(gameTime,otherScreenHasFocus,coveredByOtherScreen);
}
publicoverridevoidDraw(GameTimegameTime)
{
floatelapsedSeconds=(float)gameTime.ElapsedGameTime.TotalSeconds;
gameplayHelper.Draw(elapsedSeconds);
}
- Finally,addanoverridetoHandleInputforfeedingtheaccelerometerinputtothegameplayhelper:
C#
publicoverridevoidHandleInput(InputStateinput)
{
Vector3accelerationInfo=accelState==null?Vector3.Zero:
newVector3((float)accelState.SensorReading.Acceleration.X,
(float)accelState.SensorReading.Acceleration.Y,(float)accelState.SensorReading.Acceleration.Z);
if(gameplayHelper.HandleInput(input.PauseGame,accelerationInfo,TouchPanel.GetState()))
{
//TODO:Finishthecurrentgame
}
elseif(input.PauseGame)
{
//TODO:Pausethecurrentgame
}
}
- Wearefinishedwiththegameplayscreenforthemoment.Letusmoveontoupdatingthegameplayhelperwithsomemethodsweusedwhileconstructingthegameplayscreen.Butfirst,letusaddsomeadditionalfieldstothegameplayhelperforfutureuse.OpenGameplayHelper.csandaddthefollowingfielddefinitionstotheGameplayHelperclass:
C#
privateconststringSerializationNamespace=@"
RectangleworldBounds;
boolgameOver;
intbaseLevelKillCount;
intlevelKillCount;
floatalienSpawnTimer;
floatalienSpawnRate;
floatalienMaxAccuracy;
floatalienSpeedMin;
floatalienSpeedMax;
intalienScore;
intnextLife;
inthitStreak;
inthighScore;
Randomrandom;
Vector2cloud1Position;
Vector2cloud2Position;
Vector2sunPosition;
//Levelchanges,nighttimetransitions,etc
floattransitionFactor;//0.0f==day,1.0f==night
floattransitionRate;//0.0f==daytonight
ParticleSystemparticles;
//Screendimensionconstants
constfloatscreenHeight=800.0f;
constfloatscreenWidth=480.0f;
constintleftOffset=25;
constinttopOffset=50;
constintbottomOffset=20;
//Actorvariables
Playerplayer;
List<Alien>aliens;
List<Bullet>alienBullets;
List<Bullet>playerBullets;
- Attheverybottomoftheabovecodeblock,youcanseethatwedefinesomevariablesforstoringtheplayer,thealiensandtheirrespectivebullets.StillintheGameplayHelper.csfile,addthefollowingclassdefinitions:
C#
publicclassBullet
{
publicVector2Position;
publicVector2Velocity;
publicboolIsAlive;
}
publicclassAlien
{
publicVector2Position;
[XmlIgnore]
publicTexture2DTexture;
publicstringTextureName;
publicVector2Velocity;
publicfloatWidth;
publicfloatHeight;
publicintScore;
publicboolIsAlive;
publicfloatFireTimer;
publicfloatAccuracy;
publicintFireCount;
}
- BackintheGameplayHelperclass,changetheclass’sconstructorbyreplacingthe“TODO”commentlocatedinsideitwiththefollowingcode:
C#
random=newRandom();
worldBounds=newRectangle(0,0,(int)screenWidth,(int)screenHeight);
gameOver=true;
player=newPlayer();
playerBullets=newListBullet>();
aliens=newListAlien>();
alienBullets=newListBullet>();
particles=newParticleSystem(contentManager,spriteBatch);
Inthecodeabovesimplyinitializesomeofthegame’sentitiesanddefinethesizeofthe“gameworld”,whereallthegame’sactionstakeplace.
- Addthefollowingplaceholdermethodsthatwewillchangelaterintheexercise.
C#
publicvoidUpdate(floattotalElapsedSeconds)
{
//TODO:Addgameupdatelogic
}
publicvoidDraw(floattotalElapsedSeconds)
{
//TODO:Addgamedrawinglogic
}
publicboolHandleInput(boolpauseGame,Vector3acceleration,TouchCollectiontouchInfo)
{
//TODO:Addinputhandlinglogic
returnfalse;
}
publicvoidStart()
{
//TODO:Initializethegame'sstateandstartit
}
Youwillnoticethatthelastmethod,Start,wasnotusedwhenwecreatedthegameplayscreen.Youwillseeitbeingusedinthenextstep.
- OpenMainMenuScreen.cs,locatetheStartGameMenuEntrySelectedmethod,andreplacethe“TODO”commentinsideitwiththefollowingcode.ThiswilladdtheGameplayScreenscreentotheScreenManagerwhenuserclicks“STARTGAME”button:
C#
(PhoneApplicationService.Current.State[AlienGame.GameStateKey]asGameplayHelper).Start();
ScreenManager.AddScreen(newGameplayScreen());
- Compileandruntheapplication.Clickthe“STARTGAME”menuentryandobservethemainmenuitemsscrollingdownfromthescreen.
Figure5
Tryingtostartthegame
Note:TheGameplayscreenisstillempty,andsoallyouwillseeisthemenudisappearingonceyouselect“StartGame”.
- Stopdebuggingandreturntoeditingtheapplication.
Duringthistask,youcreatedthefoundationsofthegameplayscreenandthegameplayhelper,whichwillhandlethegame’slogic.
Task4–GameSpecificLogic
Inthistask,youwillcreategame-specificlogic.Therefore,thefocusofthistaskwillbethegameplayhelper.
- Wewillbeginbyaddingsomeadditionalinitializationstothegameplayhelper.OpenGameplayHelper.cs andmovetotheGameplayHelperclass’sInitializeAssetsmethod.Replacethe“TODO”commentinthemethodwiththefollowingcode:
C#
SetAlienTextures();
particles.InitializeAssets(contentManager,spriteBatch);
particles.SetParticleTextures();
Theabovecodeassignsthevariousalienobjectstheirassociatedtextureandperformssomeadditionalinitializationsfortheparticlesystem.
- AddtheSetAlienTexturesmethodseenaboveusingthefollowingcode:
C#
privatevoidSetAlienTextures()
{
foreach(Alienalieninaliens)
{
alien.Texture=contentManager.Load<Texture2D>(alien.TextureName);
}
}
- MovetotheUpdatemethodandreplacethe“TODO”commentinsidethemethodwiththefollowingcode:
C#
//Movetheplayer
if(player.IsAlive==true)
{
player.Position+=player.Velocity*128.0f*totalElapsedSeconds;
player.FireTimer-=totalElapsedSeconds;
if(player.Position.X<=0.0f)
player.Position=newVector2(0.0f,player.Position.Y);
if(player.Position.X+player.Width>=worldBounds.Right)
player.Position=newVector2(worldBounds.Right-player.Width,player.Position.Y);
}
Respawn(totalElapsedSeconds);
UpdateAliens(totalElapsedSeconds);
UpdateBullets(totalElapsedSeconds);
CheckHits();
if(player.IsAliveplayer.Velocity.LengthSquared()0.0f)
particles.CreatePlayerDust(player);
particles.Update(totalElapsedSeconds);
Youcanseethatthegame’supdatecycleiscomposedofmovingtheplayeraccordingtothevelocityderivedfromtheaccelerometerinput(whichwewillhandlelater),handlingtheplayer’srespawningincaseofdeath,updatingthevariousbullets,checkingforhits(theplayerhittingaliensandviceversa)andupdatingthegame’sparticles.
- Wewillbeginaddingthevariousmethodsusedduringtheupdatecycle.UsethefollowingcodetoaddtheRespawnmethod:
C#
voidRespawn(floatelapsed)
{
if(gameOver)
return;
if(!player.IsAlive)
{
player.RespawnTimer-=elapsed;
if(player.RespawnTimer<=0.0f)
{
//Seeifthereareanybulletsclose...
intleft=worldBounds.Width/2-tankTexture.Width/2-8;
intright=worldBounds.Width/2+tankTexture.Width/2+8;
for(inti=0;ialienBullets.Count;++i)
{
if(alienBullets[i].IsAlive==false)
continue;
if(alienBullets[i].Position.X>=left||alienBullets[i].Position.X<=right)
return;
}
player.IsAlive=true;
player.Position=newVector2(worldBounds.Width/2-player.Width/2,worldBounds.Bottom-groundTexture.Height+2-player.Height);
player.Velocity=Vector2.Zero;
player.Lives--;
}
}
}
Thismethoddeductsfromtheplayer’slifecountafterdyingandresetshispositiononcetherearenoenemybulletsnearby.
- AddtheUpdateAliensmethodusingthefollowingcode:
C#
privatevoidUpdateAliens(floatelapsed)
{
//Seeifit'stimetospawnanalien;
alienSpawnTimer-=elapsed;
if(alienSpawnTimer<=0.0f)
{
SpawnAlien();
alienSpawnTimer+=alienSpawnRate;
}
for(inti=0;ialiens.Count;++i)
{
if(aliens[i].IsAlive==false)
continue;
aliens[i].Position+=aliens[i].Velocity*elapsed;
if((aliens[i].Position.X-aliens[i].Width-64aliens[i].Velocity.X0.0f)||
(aliens[i].Position.XworldBounds.Width+64aliens[i].Velocity.X0.0f))
{
aliens[i].IsAlive=false;
continue;
}
aliens[i].FireTimer-=elapsed;
if(aliens[i].FireTimer<=0.0faliens[i].FireCount0)
{
if(player.IsAlive)
{
Bulletbullet=CreateAlienBullet();
bullet.Position.X=aliens[i].Position.X+aliens[i].Width/2-laserTexture.Width/2;
bullet.Position.Y=aliens[i].Position.Y+aliens[i].Height;
if((float)random.NextDouble()<=aliens[i].Accuracy)
{
bullet.Velocity=Vector2.Normalize(player.Position-aliens[i].Position)*64.0f;
}
else
{
bullet.Velocity=newVector2(-8.0f+16.0f*(float)random.NextDouble(),64.0f);
}
alienFired.Play();
}
aliens[i].FireCount--;
}
}
}
Theabovecodespawnsaliens,movesthemacrossthetopofthescreen,markingthemasdeadiftheyexitthegameworld.Thecodeisalsoresponsibleforcausingthealienstofirebulletstowardstheplayer.
- AddtheCreateAlienBulletandSpawnAlienandmethodsusedinthepreviousstep’scode.WewillalsointroduceanadditionalmethodcalledCreateAlienthatwewillusetoinitializeanalienobjectandplaceitintheproperlist:
C#
BulletCreateAlienBullet()
{
Bulletb=null;
for(inti=0;ialienBullets.Count;++i)
{
if(alienBullets[i].IsAlive==false)
{
b=alienBullets[i];
break;
}
}
if(b==null)
{
b=newBullet();
alienBullets.Add(b);
}
b.IsAlive=true;
returnb;
}
privatevoidSpawnAlien()
{
AliennewAlien=CreateAlien();
if(random.Next(2)==1)
{
newAlien.Position.X=-64.0f;
newAlien.Velocity.X=random.Next((int)alienSpeedMin,(int)alienSpeedMax);
}
else
{
newAlien.Position.X=worldBounds.Width+32;
newAlien.Velocity.X=-random.Next((int)alienSpeedMin,(int)alienSpeedMax);
}
newAlien.Position.Y=24.0f+80.0f*(float)random.NextDouble();
//Aliens
if(transitionFactor0.0f)
{
switch(random.Next(4))
{
case0:
newAlien.Texture=badguy_blue;
newAlien.TextureName="badguy_blue";
break;
case1:
newAlien.Texture=badguy_red;
newAlien.TextureName="badguy_red";
break;
case2:
newAlien.Texture=badguy_green;
newAlien.TextureName="badguy_green";
break;
case3:
newAlien.Texture=badguy_orange;
newAlien.TextureName="badguy_orange";
break;
}
}
else
{
newAlien.Texture=alienTexture;
newAlien.TextureName="alien1";
}
newAlien.Width=newAlien.Texture.Width;
newAlien.Height=newAlien.Texture.Height;
newAlien.IsAlive=true;
newAlien.Score=alienScore;
floatduration=screenHeight/newAlien.Velocity.Length();
newAlien.FireTimer=duration*(float)random.NextDouble();
newAlien.FireCount=1;
newAlien.Accuracy=alienMaxAccuracy;
}
AlienCreateAlien()
{
Alienb=null;
for(inti=0;ialiens.Count;++i)
{
if(aliens[i].IsAlive==false)
{
b=aliens[i];
break;
}
}
if(b==null)
{
b=newAlien();
aliens.Add(b);
}
b.IsAlive=true;
returnb;
}
TheSpawnAlienmethodappearslengthy,yetitisarathersimplemethodwhichdealsprimarilywithdecidingwhattypeofalientocreate.
- AddtheUpdateBulletsmethod,whichtakescareofmovingbulletsaroundbasedontheirvelocityanddestroyingthemoncetheyexitthegameworld:
C#
voidUpdateBullets(floatelapsed)
{
for(inti=0;iplayerBullets.Count;++i)
{
if(playerBullets[i].IsAlive==false)
continue;
playerBullets[i].Position+=playerBullets[i].Velocity*elapsed;
if(playerBullets[i].Position.Y-32)
{
playerBullets[i].IsAlive=false;
hitStreak=0;
}
}
for(inti=0;ialienBullets.Count;++i)
{
if(alienBullets[i].IsAlive==false)
continue;
alienBullets[i].Position+=alienBullets[i].Velocity*elapsed;
if(alienBullets[i].Position.YworldBounds.Height-groundTexture.Height-laserTexture.Height)
alienBullets[i].IsAlive=false;
}
}
- AddtheCheckHitsmethod.Thisisafairlylengthmethodthathandlesallcollisionrelatedlogicsuchasincreasingtheplayer’sscoreonceanalienishit,advancingtothenextlevelafteracertainamountofalienshavebeenkilled,causingtheplayertoexplodeifhitbyoneofthealiens’bulletsandsoon:
C#
voidCheckHits()
{
if(gameOver)
return;
for(inti=0;iplayerBullets.Count;++i)
{
if(playerBullets[i].IsAlive==false)
continue;
for(inta=0;aaliens.Count;++a)
{
if(aliens[a].IsAlive==false)
continue;
if((playerBullets[i].Position.X>=aliens[a].Position.XplayerBullets[i].Position.X<=aliens[a].Position.X+aliens[a].Width)
(playerBullets[i].Position.Y>=aliens[a].Position.YplayerBullets[i].Position.Y<=aliens[a].Position.Y+aliens[a].Height))
{
playerBullets[i].IsAlive=false;
aliens[a].IsAlive=false;
hitStreak++;
player.Score+=aliens[a].Score*(hitStreak/5+1);
if(player.ScorehighScore)
highScore=player.Score;
if(player.ScorenextLife)
{
player.Lives++;
nextLife+=nextLife;
}
levelKillCount--;
if(levelKillCount<=0)
AdvanceLevel();
particles.CreateAlienExplosion(newVector2(aliens[a].Position.X+aliens[a].Width/2,aliens[a].Position.Y+aliens[a].Height/2));
alienDied.Play();
}
}
}
if(player.IsAlive==false)
return;
for(inti=0;ialienBullets.Count;++i)
{
if(alienBullets[i].IsAlive==false)
continue;
if((alienBullets[i].Position.X>=player.Position.X+2alienBullets[i].Position.X<=player.Position.X+player.Width-2)
(alienBullets[i].Position.Y>=player.Position.Y+2alienBullets[i].Position.Y<=player.Position.Y+player.Height))
{
alienBullets[i].IsAlive=false;
player.IsAlive=false;
hitStreak=0;
player.RespawnTimer=3.0f;
particles.CreatePlayerExplosion(newVector2(player.Position.X+player.Width/2,player.Position.Y+player.Height/2));
playerDied.Play();
if(player.Lives<=0)
{
gameOver=true;
}
}
}
}
- AddanadditionalhelpermethodcalledAdvanceLevelforadvancingtothenextgamelevel:
C#
voidAdvanceLevel()
{
baseLevelKillCount+=5;
levelKillCount=baseLevelKillCount;
alienScore+=25;
alienSpawnRate-=0.3f;
alienMaxAccuracy+=0.1f;
if(alienMaxAccuracy0.75f)
alienMaxAccuracy=0.75f;
alienSpeedMin*=1.35f;
alienSpeedMax*=1.35f;
if(alienSpawnRate0.33f)
alienSpawnRate=0.33f;
if(transitionFactor==1.0f)
{
transitionRate=-0.5f;
}
else
{
transitionRate=0.5f;
}
}
- Finally,addanadditionalmethodnamedCreatePlayerBullet,whichwewilluselater,forcreatingbulletsfiredbytheplayer:
C#
BulletCreatePlayerBullet()
{
Bulletb=null;
for(inti=0;iplayerBullets.Count;++i)
{
if(playerBullets[i].IsAlive==false)
{
b=playerBullets[i];
break;
}
}
if(b==null)
{
b=newBullet();
playerBullets.Add(b);
}
b.IsAlive=true;
returnb;
}
- Wehaveimplementedthegame’slogicandcanmoveontothegame’spresentation.Replacethe“TODO”commentintheGameplayHelper’sDrawmethodwiththefollowingcode:
C#
spriteBatch.Begin();
DrawBackground(totalElapsedSeconds);
DrawAliens();
DrawPlayer();
DrawBullets();
particles.Draw();
DrawForeground(totalElapsedSeconds);
DrawHud();
spriteBatch.End();
Theabovecodeisessentiallyalistofallthevariouselementsthatwemustdrawtodisplaythegame.
Note: Followingisashortdescriptionofeachofthedrawinghelpermethodswewillsoonimplement:
DrawPlayer:Drawstheplayer’stank.
DrawAliens:Drawsallaliens.
DrawBullets:Drawsallbullets(boththeplayer’sandthealiens’).
DrawForeground:Drawsthecloudsthatappearatthetopofthescreen.
DrawBackground:Drawsthegrass,hills,mountains,andsun/moon.Alsohandlestransitioningbetweendayandnight.
DrawHud:Drawsthescoreelements,livesremaining,andthe“GAMEOVER”promptwhenneeded.
DrawString:Agenericmethodfordrawingshadowedtext.Itdoesnotappearabove,butwewillrequireit.
- AddtheDrawPlayermethod:
C#
voidDrawPlayer()
{
if(!gameOverplayer.IsAlive)
{
spriteBatch.Draw(tankTexture,player.Position,Color.White);
}
}
- AddtheDrawAliensmethod:
C#
voidDrawAliens()
{
for(inti=0;ialiens.Count;++i)
{
if(aliens[i].IsAlive)spriteBatch.Draw(aliens[i].Texture,
newRectangle((int)aliens[i].Position.X,(int)aliens[i].Position.Y,(int)aliens[i].Width,(int)aliens[i].Height),Color.White);
}
}
- AddtheDrawBulletsmethod:
C#
privatevoidDrawBullets()
{
for(inti=0;iplayerBullets.Count;++i)
{
if(playerBullets[i].IsAlive)
spriteBatch.Draw(bulletTexture,playerBullets[i].Position,Color.White);
}
for(inti=0;ialienBullets.Count;++i)
{
if(alienBullets[i].IsAlive)
spriteBatch.Draw(laserTexture,alienBullets[i].Position,Color.White);
}
}
- AddtheDrawForegroundmethod:
C#
privatevoidDrawForeground(floatelapsedTime)
{
//Movetheclouds.Movementistypicallyupdate-related,buttheseanimations
//havenoimpactovergameplay.
cloud1Position+=newVector2(24.0f,0.0f)*elapsedTime;
if(cloud1Position.XscreenWidth)
cloud1Position.X=-cloud1Texture.Width*2.0f;
cloud2Position+=newVector2(16.0f,0.0f)*elapsedTime;
if(cloud2Position.XscreenWidth)
cloud2Position.X=-cloud1Texture.Width*2.0f;
spriteBatch.Draw(cloud1Texture,cloud1Position,Color.White);
spriteBatch.Draw(cloud2Texture,cloud2Position,Color.White);
}
- AddtheDrawBackgroundmethod:
C#
privatevoidDrawBackground(floatelapsedTime)
{
transitionFactor+=transitionRate*elapsedTime;
if(transitionFactor0.0f)
{
transitionFactor=0.0f;
transitionRate=0.0f;
}
if(transitionFactor1.0f)
{
transitionFactor=1.0f;
transitionRate=0.0f;
}
Vector3day=Color.White.ToVector3();
Vector3night=newColor(80,80,180).ToVector3();
Vector3dayClear=Color.CornflowerBlue.ToVector3();
Vector3nightClear=night;
Colorclear=newColor(Vector3.Lerp(dayClear,nightClear,transitionFactor));
Colortint=newColor(Vector3.Lerp(day,night,transitionFactor));
//Clearthebackground,usingtheday/nightcolor
graphicsDevice.Clear(clear);
//Drawthemountains
spriteBatch.Draw(mountainsTexture,newVector2(0,screenHeight-mountainsTexture.Height),tint);
//Drawthehills
spriteBatch.Draw(hillsTexture,newVector2(0,screenHeight-hillsTexture.Height),tint);
//Drawtheground
spriteBatch.Draw(groundTexture,newVector2(0,screenHeight-groundTexture.Height),tint);
//Drawthesunormoon(basedontime)
spriteBatch.Draw(sunTexture,sunPosition,newColor(255,255,255,(byte)(255.0f*(1.0f-transitionFactor))));
spriteBatch.Draw(moonTexture,sunPosition,newColor(255,255,255,(byte)(255.0f*transitionFactor)));
}
- AddtheDrawHudmethod:
C#
voidDrawHud()
{
floatscale=2.0f;
if(gameOver)
{
Vector2size=menuFont.MeasureString("GAMEOVER");
DrawString(menuFont,"GAMEOVER",newVector2(graphicsDevice.Viewport.Width/2-size.X,graphicsDevice.Viewport.Height/2-size.Y/2),newColor(255,64,64),scale);
}
else
{
intbonus=100*(hitStreak/5);
stringbonusString=(bonus0?"("+bonus.ToString(System.Globalization.CultureInfo.CurrentCulture)+"%)":"");
//Score
DrawString(scoreFont,"SCORE:"+player.Score.ToString(System.Globalization.CultureInfo.CurrentCulture)+bonusString,newVector2(leftOffset,topOffset),Color.Yellow,scale);
stringtext="LIVES:"+player.Lives.ToString(System.Globalization.CultureInfo.CurrentCulture);
Vector2size=scoreFont.MeasureString(text);
size*=scale;
//Lives
DrawString(scoreFont,text,newVector2(screenWidth-leftOffset-(int)size.X,topOffset),Color.Yellow,scale);
DrawString(scoreFont,"LEVEL:"+(((baseLevelKillCount-5)/5)+1).ToString(System.Globalization.CultureInfo.CurrentCulture),newVector2(leftOffset,screenHeight-bottomOffset),Color.Yellow,scale);
text="HIGHSCORE:"+highScore.ToString(System.Globalization.CultureInfo.CurrentCulture);
size=scoreFont.MeasureString(text);
DrawString(scoreFont,text,newVector2(screenWidth-leftOffset-(int)size.X*2,screenHeight-bottomOffset),Color.Yellow,scale);
}
}
- AddtheDrawStringmethod:
C#
voidDrawString(SpriteFontfont,stringtext,Vector2position,Colorcolor,floatfontScale)
{
spriteBatch.DrawString(font,text,newVector2(position.X+1,position.Y+1),Color.Black,0,newVector2(0,font.LineSpacing/2),fontScale,SpriteEffects.None,0);
spriteBatch.DrawString(font,text,position,color,0,newVector2(0,font.LineSpacing/2),fontScale,SpriteEffects.None,0);