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:

  1. XNAGameStudioGameBasics
  2. XNAFrameworkResources
  3. XNAGameStudioGameLoop
  4. XNAGameStudioGameInput
  5. 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.

  1. OpenMicrosoftVisualStudio2010ExpressforWindowsPhonefromStart|AllPrograms|MicrosoftVisualStudio2010Express|MicrosoftVisualStudio2010ExpressforWindowsPhone.

Note:VisualStudio2010:OpenVisualStudio2010fromStart|AllPrograms|MicrosoftVisualStudio2010.

  1. IntheFilemenu,chooseNewProject.
  2. IntheNewProjectdialog,selecttheXNAGameStudio4.0forWindowsPhonecategoryand,fromthelistofinstalledtemplates,selectWindowsPhoneGame(4.0).CallthenewprojectAlienGame.Whenthetargetphoneframeworkdialogappears,choose“WindowsPhone7.1”.
  3. InSolutionExplorer,reviewthestructureofthesolutiongeneratedbytheWindowsPhoneApplicationtemplate.AnyVisualStudiosolutionisacontainerforrelatedprojects;inthiscase,itcontainsanXNAGameStudiogameforWindowsPhoneprojectnamedAlienGameandarelatedgamesresourcesprojectnamedAlienGameContent.
  4. AddareferencetothenewlycreatedAlienGameprojectfortheMicrosoft.Phoneassembly.
  5. ThegeneratedprojectincludesadefaultgameimplementationthatcontainsthebasicXNAGameStudiogameloop.ItislocatedintheGame1.csfile.
  6. OpentheGame1.csfile.Werecommendthatyouchangethedefaultnametothenamethatreflectsyourgame.
  7. Renamethemaingameclass(defaultnameGame1)toAlienGame.
  8. Renamethefilenametomatchthenewclassname.Right-clickonGame1.csinSolutionExplorerandchooseRename.GivetheclassthenewnameAlienGame.cs.
  9. PressF5tolaunchtheapplicationintheWindowsPhoneEmulator.NoticethatadeviceemulatorwindowappearsandthereisapausewhileVisualStudiosetsuptheemulatorenvironmentanddeploystheimage.Onceitisready,theemulatorshowstheStartpageandshortlythereafter,yourapplicationappearsintheemulatorwindow.

Theapplicationwilldisplayasimplebluescreenwithnothingelseshown.Thisisnormaltoanapplicationinsuchearlystages.

Figure2

RunningtheapplicationintheWindowsPhoneEmulator

Untilyoucreatetheuserinterfaceandprogramtheapplicationlogic,thereisverylittlethatyoucandowiththeapplication.

  1. 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

  1. ClosetheprojectinVisualStudio.SwitchtoWindowsExplorer,navigatetotheprojectlocationandcopytwofilesfromtheSource\Assets\Media\Images\Iconsfolderofthislabintothe“AlienGame”directory,replacingtheexistingfiles:

◦Game.ico

◦PhoneGameThumb.png

  1. 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)

  1. Thislabprovidesanumberofmediaresourcessuchasfonts,sounds,andimages.AddthefollowingitemstotheAlienGameContentproject:

◦AllthefontsfromtheSource\Assets\Media\Fontsfolder

◦AlltheimagesfromtheSource\Assets\Media\Images\Contentfolder

◦AllthesoundsfromtheSource\Assets\Media\Soundsfolder

  1. AddanewprojectfolderundertheAlienGameprojectandnameitScreenManager.

Thisfolderwillholdsourcefilesprovidebythelab,whichwillhelptomanagethecomplexityofcreatinggamescreens,menus,andofnavigatingbetweenthem.

Note:ThiscodeimplementsthestandardapproachforcreatingXNAGameStudiomenusandscreensandmaybefoundatthefollowinglink:

  1. AddallexistingScreenManagerfilesfromtheSource\Assets\Code\ScreenManagerfolderofthislabtotheprojectfoldercreatedinthepreviousstep.
  2. AddtheParticleSystem.csfilefromtheSource\Assets\CodefoldertotherootoftheAlienGameproject.
  3. AddareferencetotheSystem.Windowsassembly.
  4. AddanewclasstotheAlienGameprojectandnameitBackgroundScreen:
  5. Openthenewclassandaddthefollowingusingdirectives:

C#

usingAlienGameSample;

usingMicrosoft.Xna.Framework.Graphics;

usingMicrosoft.Xna.Framework;

  1. DerivethenewclassfromtheGameScreenclass(theGameScreenclassisdefinedinclassesaddedpreviouslytotheScreenManagerfolder):

C#

classBackgroundScreen:GameScreen

{

}

  1. Addthefollowingclassvariablestobeusedlaterforloadingresources:

C#

Texture2Dtitle;

Texture2Dbackground;

  1. Defineaclassconstructorasfollows:

C#

publicBackgroundScreen()

{

TransitionOnTime=TimeSpan.FromSeconds(0.0);

TransitionOffTime=TimeSpan.FromSeconds(0.5);

}

  1. 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.

  1. NowcreateaLoadingScreenclass.Thescreensupportedbytheclasswillbepresentedwhilegameresourcesarebeingloaded.
  2. AddfollowingusingdirectivestotheLoadingScreenclass:

C#

usingAlienGameSample;

usingSystem.Threading;

usingMicrosoft.Xna.Framework;

usingMicrosoft.Phone.Shell;

  1. DerivethisnewclassfromtheGameScreenbaseclass(likeyoudidforthepreviousclass),andaddaconstructorasfollows:

C#

classLoadingScreen:GameScreen

{

publicLoadingScreen(boolshowPauseScreen)

{

TransitionOnTime=TimeSpan.FromSeconds(0.0);

TransitionOffTime=TimeSpan.FromSeconds(0.0);

this.showPauseScreen=showPauseScreen;

}

}

  1. Addacoupleoffields.Onetoholdthethreadwhichwillbeusedtoloadthecomponentsandanothertodeterminethetypeofscreenthatshowsupafterallcontenthasbeenloaded:

C#

privateThreadbackgroundThread;

privateboolshowPauseScreen;

  1. 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:

  1. Overridethebaseclass’sLoadContentmethodandinitiatethecontentloadingasanewthreadtoachieveasynchronousloadingoftheresources:

Note:Inoursimplegame,theresourceswillbeloadedalmostinstantaneously,butwhendealingwithgamesthataremorecomplex,thisapproachallowsshowingaprogressindicatororasplashscreen.

C#

publicoverridevoidLoadContent()

{

if(backgroundThread==null)

{

backgroundThread=newThread(BackgroundLoadContent);

backgroundThread.Start();

}

base.LoadContent();

}

  1. 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.

  1. LetusintroducetheGameplayHelperclassusedintheloadingscreen.CreateanewclasswiththatnameintheAlienGameproject. Be sure to mark the class as public.
  2. AddareferencetotheAlienGameprojectfortheSystem.Xml.Serializationassembly.
  3. 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;

  1. 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.

  1. ChangetheGameplayHelperclasstoimplementIXmlSerializable.Addthefollowingplaceholdermethodswhichwewillfleshoutlaterinthelab:

C#

publicXmlSchemaGetSchema()

{

returnnull;

}

publicvoidReadXml(XmlReaderreader)

{

//TODO:Deserializethegameplaystate

}

publicvoidWriteXml(XmlWriterwriter)

{

//TODO:Serializethegameplaystate

}

  1. Addthefollowingconstructorfortheclass:

C#

publicGameplayHelper(ContentManagercontentManager,SpriteBatchspriteBatch,

GraphicsDevicegraphicsDevice)

{

//TODO:Performadditionalinitializations

InitializeAssets(contentManager,spriteBatch,graphicsDevice);

}

  1. AddtheInitializeAssetshelpermethodseenabove:

C#

publicvoidInitializeAssets(ContentManagercontentManager,SpriteBatchspriteBatch,GraphicsDevicegraphicsDevice)

{

this.contentManager=contentManager;

this.spriteBatch=spriteBatch;

this.graphicsDevice=graphicsDevice;

//TODO:Performadditionalinitializations

}

  1. 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

}

  1. AddanadditionalclassdefinitiontotheGameplayHelper.csfileasfollows:

C#

publicclassPlayer

{

publicVector2Position;

publicVector2Velocity;

publicfloatWidth;

publicfloatHeight;

publicboolIsAlive;

publicfloatFireTimer;

publicfloatRespawnTimer;

publicstringName;

publicintScore;

publicintLives;

}

  1. Nowaddanewclass(inanewfile)andnameitMainMenuScreen.Whencreated,addthefollowingusingstatementstoit:

C#

usingAlienGameSample;

usingMicrosoft.Phone.Shell;

  1. DerivethenewclassfromtheMenuScreenbaseclass.ThisclassisalsodefinedinclassesaddedtotheScreenManagerfolderandfacilitatesalltypicalfunctionalityneededtoshow/interactwithmenusandmenuitems.
  2. 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);

}

}

  1. Intheaboveconstructor,wesubscribedtotwoevents,whichwillfirewhentheuserselectsmenuitems.Createtheeventhandlermethodstohandlethoseevents:

C#

voidStartGameMenuEntrySelected(objectsender,EventArgse)

{

//TODO:Movetotheactualgame

}

protectedoverridevoidOnCancel()

{

ScreenManager.Game.Exit();

}

  1. OpentheAlienGame.csfileandaddthefollowingusingstatement:

C#

usingAlienGameSample;

usingMicrosoft.Phone.Shell;

usingSystem.IO.IsolatedStorage;

  1. DeletetheentirecontentsoftheAlienGameclass.
  2. AddthefollowingclassmemberstotheAlienGameclass:

C#

publicconststringGameStateKey="GameState";

publicconststringInGameKey="InGame";

///<summary>

///Whetherornotassetreloadingisrequired.

///</summary>

publicboolReloadRequired{get;set;}

///<summary>

///Allowsstoringthegame'sgameplayhelper.

///</summary>

publicGameplayHelperGameplayHelper{get;set;}

GraphicsDeviceManagergraphics;

ScreenManagerscreenManager;

  1. 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.

  1. 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));

}

  1. Compileandruntheapplication.Aftertheapplicationloads,themainmenuscreenshouldappear:

Figure3

Thegame’smainmenu

  1. Stopthedebuggingandreturntoeditingtheapplication.

Duringthistask,youaddedresourcestothegameandcreatedanumberofscreenstopresenttheuserwithsomebasicuserinterfaceswhileloadingthegame.Youalsocreatedthemainmenu.

Task3–XNAGameStudioGameLoop

Inthistask,youwillfocusonhavingthevariousgamescreensupdateanddrawproperly.

  1. OpenBackgroundScreen.cs.
  2. OverridethebaseclassUpdatemethodasfollows:

C#

publicoverridevoidUpdate(GameTimegameTime,boolotherScreenHasFocus,

boolcoveredByOtherScreen)

{

base.Update(gameTime,otherScreenHasFocus,false);

}

  1. 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();

}

  1. PressF5tocompileandruntheapplication.

Figure4

Thegameafterhavingthebackgroundscreenupdateanddraw

  1. Stopthedebugging(SHIFT+F5)andreturntoeditingtheapplication.Ournextgoalistocreatethegameplayscreenandenhancethegameplayhelpertosupportthefullyfunctionalgame.
  2. AddareferenceintheAlienGameprojecttotheMicrosoft.Devices.Sensorsassembly.
  3. AddanadditionalclasstotheapplicationandsetitsnametoGameplayScreen.
  4. 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;

  1. DerivetheclassfromGameScreen.

C#

classGameplayScreen:GameScreen

{

}

  1. AddfollowingFielddefinitions:

C#

SensorReadingEventArgsAccelerometerReadingaccelState;

AccelerometerAccelerometer;

GameplayHelpergameplayHelper;

  1. 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();

}

}

  1. Overridethebaseclass’sLoadContentmethodtogetthecurrentgameplayhelperandstatethatwearenowinanactivegame:

C#

publicoverridevoidLoadContent()

{

gameplayHelper=PhoneApplicationService.Current.State[AlienGame.GameStateKey]asGameplayHelper;

PhoneApplicationService.Current.State[AlienGame.InGameKey]=true;

base.LoadContent();

}

  1. 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);

}

  1. 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

}

}

  1. 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;

  1. Attheverybottomoftheabovecodeblock,youcanseethatwedefinesomevariablesforstoringtheplayer,thealiensandtheirrespectivebullets.StillintheGameplayHelper.csfile,addthefollowingclassdefinitions:

C#

publicclassBullet

{

publicVector2Position;

publicVector2Velocity;

publicboolIsAlive;

}

publicclassAlien

{

publicVector2Position;

[XmlIgnore]

publicTexture2DTexture;

publicstringTextureName;

publicVector2Velocity;

publicfloatWidth;

publicfloatHeight;

publicintScore;

publicboolIsAlive;

publicfloatFireTimer;

publicfloatAccuracy;

publicintFireCount;

}

  1. 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.

  1. 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.

  1. OpenMainMenuScreen.cs,locatetheStartGameMenuEntrySelectedmethod,andreplacethe“TODO”commentinsideitwiththefollowingcode.ThiswilladdtheGameplayScreenscreentotheScreenManagerwhenuserclicks“STARTGAME”button:

C#

(PhoneApplicationService.Current.State[AlienGame.GameStateKey]asGameplayHelper).Start();

ScreenManager.AddScreen(newGameplayScreen());

  1. Compileandruntheapplication.Clickthe“STARTGAME”menuentryandobservethemainmenuitemsscrollingdownfromthescreen.

Figure5

Tryingtostartthegame

Note:TheGameplayscreenisstillempty,andsoallyouwillseeisthemenudisappearingonceyouselect“StartGame”.

  1. Stopdebuggingandreturntoeditingtheapplication.

Duringthistask,youcreatedthefoundationsofthegameplayscreenandthegameplayhelper,whichwillhandlethegame’slogic.

Task4–GameSpecificLogic

Inthistask,youwillcreategame-specificlogic.Therefore,thefocusofthistaskwillbethegameplayhelper.

  1. Wewillbeginbyaddingsomeadditionalinitializationstothegameplayhelper.OpenGameplayHelper.cs andmovetotheGameplayHelperclass’sInitializeAssetsmethod.Replacethe“TODO”commentinthemethodwiththefollowingcode:

C#

SetAlienTextures();

particles.InitializeAssets(contentManager,spriteBatch);

particles.SetParticleTextures();

Theabovecodeassignsthevariousalienobjectstheirassociatedtextureandperformssomeadditionalinitializationsfortheparticlesystem.

  1. AddtheSetAlienTexturesmethodseenaboveusingthefollowingcode:

C#

privatevoidSetAlienTextures()

{

foreach(Alienalieninaliens)

{

alien.Texture=contentManager.Load<Texture2D>(alien.TextureName);

}

}

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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;

}

}

  1. 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;

}

}

}

}

  1. 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;

}

}

  1. 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;

}

  1. 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.

  1. AddtheDrawPlayermethod:

C#

voidDrawPlayer()

{

if(!gameOverplayer.IsAlive)

{

spriteBatch.Draw(tankTexture,player.Position,Color.White);

}

}

  1. 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);

}

}

  1. 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);

}

}

  1. 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);

}

  1. 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)));

}

  1. 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);

}

}

  1. 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);