3D Platform Tutorial

122
3D Platformer Tutorial Building a 3D Platform Game in Unity 2.0

description

 

Transcript of 3D Platform Tutorial

Page 1: 3D Platform Tutorial

3DPlatformerTutorialBuildinga3DPlatformGameinUnity2.0

Page 2: 3D Platform Tutorial

Contents

1. IntroductionWhatyouwilllearn 5Whatyoushouldalreadyknow 6ProjectOrganization 6Files 7TypographicalConventions 7UnityConventions 8Projects 8GameObjects,Components,Assets&Prefabs 8GameObjects 8Components 9Assets 9Prefabs 9Acknowledgments 10

2. FirstStepsAnimatingLerpz 11ThePlot 11IntroducingLerpz 12TheCharacterController&theThirdPersonControllerscript 19AnimatingLerpz 19CharacterAnimation 20Animationblending 20TheThirdPersonPlayerAnimationscript 20Gizmos 21TheJet­Pack 22AddingtheParticleSystems 23AddingtheLight 26BlobShadows 29AddingaBlobShadow 30CreatinganewLayer 31ScriptingConcepts 33Organization&Structure 34Death&Rebirth 36

Page 3: 3D Platform Tutorial

TheFalloutDeathscript 37RespawnPoints 38Howitworks 40

3. SettingtheSceneFirstSteps 42PlacingProps 43HealthPickups 43TheForceField 44ScriptingtheCollectableItems 45JumpPads 48

4. TheGUITheUserInterface 50Unity2'snewGUIsystem 50FurtherInformation 51TheIn­gameHUD 51TheGUISkinobject 52TheStartMenu 56SettingtheScene 57TheBackdrop. 58TheButtons. 60GameOver 64

5. AdversariesAntagonists&Conflict 69TheLaserTraps 69ImplementingtheLaserTraps 70TheLaserTrapScript 73Overview 73TheRobotGuards 75Divide&Conquer 77Spawning&Optimization 79Howitworks. 80

6. Audio&FinishingTouchesIntroduction 83Audio 83SampleNotes 84AddingSoundtoLerpzEscapes! 84AmbientSounds 86TheJumpPads 87Collectables 88TheImpoundFence 90

Page 4: 3D Platform Tutorial

ThePlayer 90TheRobotGuards 94CutScenes 96Unlockingtheimpoundfence 96

7. OptimizingWhyOptimize? 110OptimizingRendering:MonitoringFramesPerSecond 110MakingsenseoftheStatsdisplay 111OptimizingRendering:TheTwo­CameraSystem 112

8. Endoftheroad.TheRoadLessTravelled 114SuggestedImprovements 114Fixingthedeliberatemistakes 114Morelevels 115Moreenemies 115Addscoring 115Addanetworkedhigh­scoresystem 115Addmultiplayersupport 115FurtherReading 115

9. ScriptAppendixStartMenuGUIscript 116GameOverGUI 117GameOverScript 118ThirdPersonStatus 118LevelStatus 120HandleSpaceshipCollision 122

Page 5: 3D Platform Tutorial

Introduction

Withitsmyriadfeatures,includingheight­mappedterrains,nativenetworkingsup­port,completephysicsintegrationandscripting,Unitycanbedauntingfornewcom­ers,butmasteringitsmanytoolsisinfinitelyrewarding.

Thistutorialwillwalkyouthroughtheprocessofbuildingacomplete3Dplatformgamelevelwithathird­personperspectiveview.Thisincludeseverythingfromplayercontrols,collisiondetection,someadvancedscripting,blobshadows,basicAI,addingagameHUD,cut­scenesandaudiospoteffects.

WhatyouwilllearnThistutorialfocusesonthetechnicalsideofbuildingagameinUnity,coveringthefollowing:

• CharacterControllers

• Projectors

• AudioListeners,AudioSources&AudioClips

• MultipleCameras(andhowtoswitchbetweenthem)

• UnityGUIscriptingsystem

• Colliders

• Messages&events

Unityisapowerfultoolforgamedevelopment,suitableformanygamegenres,fromfirst­personshooterstopuzzlegames.

Page 6: 3D Platform Tutorial

• Lighting

• Particlesystems

• Blobshadows

• Scripting(AI,statemachines,playercontrols)

Thistutorialwillshowhowthesefeaturescanbeusedtogethertocreateagame.

WhatyoushouldalreadyknowThistutorialmakesextensiveuseofscriptingsoyoushouldbefamiliarwithatleastoneofthesupportedscriptinglanguages:JavaScript,C#orBoo.(JavaScriptisusedforthescriptsinthistutorial.)

ItisalsoassumedthatyouarefamiliarwithUnity’sinterfaceandknowhowtoper­formbasicoperations,suchaspositioninganassetinascene,addingComponentstoaGameObject,andeditingpropertiesintheInspector.

ProjectOrganizationUnitydoesnotattempttoforceaparticularwayoforganizingyourproject'sassets.Youmaypreferorganizingyourassetsbyassettype,withseparatefoldersfor,say,"Textures","Models","Soundeffects"andsoon.AtUnityTechnologies,wehavefoundthisworkswellforsmallerprojects.Formorecomplexprojects,ourusersgen­erallyrecommendorganizingassetsbyfunction,perhapsgroupingthemunderfolders suchas"Player","Enemies","Props","Scenery",andsoon.

Thistutorial’sprojectwasworkedonbyanumberofteammembersandgreworgani­callytoreflecttheirdifferentconventionsandstyles.Intheinterestsofauthenticity,wehavedecidedtoleavetheproject'sorganizationasitwasasthisismorerepresen­tativeofa'smaller'project'sorganizationandstructure.

AbstractGameObjects&ComponentsUnity'sdesignplaceseachscene'sassetsatthecenterofthedevelopmentprocess.This makesforaveryvisualapproachtogamedevelopment,withmostoftheworkinvolv­ingdragginganddropping.Thisisidealforthebulkofleveldesignwork,butnotallassetscanbedisplayedinthisway.Someassetsareabstractratherthanvisualobjects,sotheyareeitherrepresentedbyabstracticonsandwireframegizmos­­e.g.AudioSourcesandLights­­orarenotdisplayedatallwithintheSceneView.Scriptsfallintothislattercategory.

ScriptsdefinehowassetsandGameObjectsinaUnitySceneinteractwitheachotherandthisinteractivityisatthecoreofallgames.Forthisreason,itisusuallyagoodplantokeepinformativenotesinsideyourscripts.

Thistutorialwillassumeyoucanreadtheprovidedscriptsandunderstandthemanycommentsliberallysprinkledthroughoutthem.However,whenaparticularscriptingtechniqueorconceptisimportant,wewillcoveritindetail.

6

Page 7: 3D Platform Tutorial

Thescriptsaredocumentedthroughtheuseofextensivecommentsandhavealsobeendesignedtobeasself­explanatoryaspossibleintheirdesign.Weencourageyoutoreadthroughthescriptsasweintroducetheminthistutorial,studyingtheirwork­ings.Feelfreetoexperiment!

FilesThemostup­to­datefilesforthisprojectcanbedownloadedfrom:http://unity3d.com/support/resources/tutorials/3DPlatformProject.zip

The“Scenes”foldercontainsthefinalresult:amainmenuScene,agameoverSceneandaScenecontainingthecompletedgamelevel.

ThistutorialassumesyoualreadyknowbasicUnitycontrols,suchaspositioningob­jectsinascene,sothefirststartingpointScenealreadyhasthebasicsceneryandsomepropsinplace.

TypographicalConventionsThisisalongtutorialcontainingalotofinformation.Tomakeiteasiertofollow,somesimpleconventionsareused:

Background&TangentsTextinboxeslikethesecontainsadditionalinformationthatmayhelpclarifythemaintext.

Scriptingcodewillappearasshownbelow:

// This is some script code.Function Update(){ DoSomething();}

NOTE Thescriptsincludedinthetutorialincludeplentyofcommentsandarede­signedtobeeasytofollow.Thesecommentsareusuallyomittedinthecodefragmentsinthetutorialtexttosavespace.

ActionsyouneedtoperformwithinUnityareshownlikethis:

Clickonthis;

Thenthis;

ThenclickPlay.

7

Page 8: 3D Platform Tutorial

Scriptnames,assets,menuitemsorInspectorPropertiesareshowninboldfacetext.Conversely,amonospace fontisusedforscriptfunctionsandeventnames,suchas

theUpdate()functioninthescriptexampleabove.

UnityConventionsUnityisauniquedevelopmentsystem.Mostdeveloperswillbeusedtoworkinginacodeeditor,spending90%oftheirtimeeditingcodeandevenwritingcodetoloadupanduseassets.Unityisdifferent:Itisasset­centricratherthancode­centric,placingthefocusontheassetsinmuchthesamewayasa3Dmodelingapplication.Forthisreason,itisworthunderstandingthekeyconventionsandterminologyuniquetoUnitydevelopment:

ProjectsAgamebuiltinUnitywillconsistofaProject.Thiscontainsallyourproject’selements,suchasmodels,scripts,levels,menus,etc.Usually,asingleProjectfilewillcontainalltheelementsforyourgame.WhenyoustartUnity2,thefirstthingitdoesisopenaProjectfile.(Ifyouhaveonlyjustinstalledit,thiswillbetheProjectfilecontainingtheIslandDemo.)

ScenesEachProjectcontainsoneormoredocumentscalledScenes.AsingleScenewillcon­tainasinglegamelevel,butmajoruser­interfaceelements,suchasgamemenus,game­oversequencesormajorcut­scenesmayalsoliveintheirownScenefiles.Com­plexgamesmayevenuseentireScenesjustforinitializationpurposes.ThusalllevelsinagamewillmostlikelybeScenes,butnoteveryScenewillnecessarilybeagamelevel.

GameObjects,Components,Assets&PrefabsKeytounderstandingUnityistherelationshipbetweenaGameObjectandaCompo­nent.

GameObjectsAGameObjectisthefundamentalbuildingblockinUnity.AGameObjectisacon­tainerfordifferentpiecesoffunctionalitycalledComponents.AGameObjectalmostalwayscontainsmorethanoneComponent.AllGameObjectscontainaTransformComponent,whichdefinesitspositionandorientation.

GameObjectHierarchiesTherealpoweroftheGameObjectisitsabilitytocontainotherGameObjects,actingmuchlikeafolderinOSX’sFinder.ThisallowshierarchicalorganizationofGameOb­jects,soacomplexmodeloracompletelightingrigcanbedefinedunderasingleparentGameObject.(Infact,mostmodelswillappearinUnityasahierarchyofGameObjectsbecausethisreflectshowtheyaredefinedinthemodelingpackage.)AGameObjectdefinedinsideanotherGameObjectisconsideredachildGameObject.

8

Page 9: 3D Platform Tutorial

ComponentsComponentsarethebuildingblocksofGameObjects.Withoutthem,theGameObjectwon’tdoanythinginteresting.

AComponentmayrepresentvisibleentities,suchasmeshes,materials,terraindataoraparticlesystem.OtherComponenttypesaremoreabstract,suchasCamerasandLights,whichdonothaveaphysicalmodelrepresentingthem;instead,youwillseeaniconandsomewire­frameguidelinesillustratingtheirkeysettings.

AComponentisalwaysattachedtoaGameObject;itcannotlivealone.MultipleCom­ponentscanbeattachedtothesameGameObject.GameObjectscansupportmultipleComponentsofcertaintypes―aGameObjectcancontainanynumberofscripts,forexample.Butothers,suchasthoseusedtodefineparticlesystems,areexclusiveandcanonlyappearonceinanysingleGameObject.Forexample,ifyouwanttodefinemultipleparticlesystems,youwouldnormallyuseahierarchyofGameObjects,eachcontainingitsownsetofparticlesystemComponents.

AssetsAllyourimportedAssetsappearintheProjectPaneandtheycanbealmostanything:asimplematerialortexture,audiofiles,orevenacomplete,prefabricatedGameOb­ject(knownasa“Prefab”).

Forexample,aplayercharacterPrefabcouldbedefinedasasingleAsset,containingthemodelanditsassociatedanimations.ItcouldalsocontainscriptComponents,audioclipsandanyotherComponentsitneedstofunction,soyoucouldsimplydragitintoaSceneandinstantlyhaveafullyoperationalavatar.

CustomIcons&GizmosYoucantellUnitytodisplaycustomiconsandothervisualinformationforyourAssetsifyouwish.Wewillseeanexampleofthisinthenextchapter.

Yourproject’sAssetsareshownintheProjectPane.WhenyoudroponeintoyourScene,itappearsintheHierarchyPane,whichdefinesthecontentoftheScene.(ASceneistheequivalentofthestageinatheater.Itcanbealevel,amenu,amulti­playergamelobby­­whateveryouwish.)TheProjectPaneisretainedacrossallScenes inyourProject.

PrefabsAPrefabisanAssetwhichhasbeendefinedasatemplate.ItistoUnitywhatatem­platedocumentistoawordprocessingapplication.WhenyouplaceaPrefabintoyourScene,UnityplacesalinktothePrefabintotheHierarchyPane,notacompletecopy.Thisiscalledinstantiation.EachlinkyoumakeisreferredtoasaninstanceofthePrefab.

IfyouclickonaPrefabinyourProjectPaneandtweakitssettings,youwillfindthatthosechangesareinstantlyreflectedinalltheinstancesinyourScene.ThismakesPre­

9

Page 10: 3D Platform Tutorial

fabsidealformanyre­usableelements,suchasbullets,enemiesandsoon.Ifyoufindyourenemyisn’tbehavingcorrectly,youonlyneedtoadjustthescriptorsettingsintheoriginalPrefabinsteadofeditingeachoneintheSceneindividually.

However,ifyouneedtoadjustacoupleofsettingsinaspecificinstanceofaPrefab,youmaydothistoo:thesechangeswillonlyaffectthatparticularinstance.

PrefabsaredisplayedinbluetextinboththeProjectandHierarchyPane.

NOTE APrefabinstancecannothaveadditionalComponentsaddedtoitasdoingsowillbreakthelinktotheoriginalPrefab.Unitywillwarnyouifyoutryanddothis.Unitywill,however,allowyoutoupdatetheoriginalPrefabwithsuchchangesafterthelinkisbroken.

AcknowledgmentsThistutorialcouldnothavebeenproducedwithoutthefollowingpeople:

DavidHelgason,JoachimAnte,TomHiggins,SamKalman,KeliHlodversson,NicholasFrancis,ArasPranckevičius,ForestJohnsonand,ofcourse,EthanVosburghwhopro­ducedthebeautifulassetsforthistutorial.

10

Page 11: 3D Platform Tutorial

FirstSteps

AnimatingLerpzInthischapterwewilllookat:

• Implementingthird­personplayerandcameracontrols

• Controllingandblendinganimations

• Usingparticlesystemstoimplementthejet­pack’sthrusters

• Addingablob­shadowtotheplayer

• Maintainingtheplayer’sstate

• Handlingplayerhealth,deathandre­birth.

Beforewecanbegin,weneedtoknowwhatthisgameisallabout.Inshort,weneed...

ThePlotOurheroisLerpz:analienvisitingRobotWorldVersion2.ThisreplacedRobotWorldVersion1,whichsufferedaparticularlybrutalsegmentationfaultandabruptlycrashedintoitssunmanyyearsago.

Unfortunately,Lerpzhashadsomebadluck:hisspaceshiphasbeenimpoundedbythecorruptlocalpolice.Afterlookinghighandlow,Lerpzhasfoundhisspaceship,but

Everyplatformgamehasitsstarcharacterwhotheplayercontrols.OurstarisLerpz.

Page 12: 3D Platform Tutorial

howcanhegetitbackfromMr.Big’snastier,obsessive­compulsivecousin,Mr.EvenBigger?

Mr.Biggerlovesnothingmorethanartisticallyarrangingfuelcanistersonhisfloatingpatio.Heparticularlyadmireshowtheyglowwhenheplacesthemonhoverpads.(And,ofcourse,they’recheaperthanfittingnormalgardenlights.)

Butthere’ssomethingMr.Biggerhasn’trealized!Thankstohispenny­pinchingways,Lerpzknowsthatifhecollectsallthefuelcanisters,thepowerusedtokeepthemhoveringwilloverloadthesecuritysystem.Thiswillshutdowntheimpoundlot’sfenceandfreeLerpz’sspaceship.Lerpzcanthenenterhisspaceship,addthefuelfromthecansandflyawaytofreedom.

Allourherohastodoiscollectenoughfuelcanistersandtheimpound’sforcefieldwillautomaticallyshutdown.Lerpzcanthengetbackintohisspacecaranddriveitaway.Mr.Bigger’shiredrobotguardswilltrytostopLerpz,butluckily,they’renotparticularlybright.

Nowthatthat’soutoftheway,wecanstartfleshingoutourhero.

IntroducingLerpzOpentheprojectupandviewtheScenes­>TheGameScene.

OurfirststepistoaddLerpztoourScene:

OpentheObjectsfolderintheProjectPane;

DragtheLerpzPrefabintoeithertheSceneViewortheHierarchyView;

ClickonthenewLerpzentryintheHierarchyandrenameittoPlayer;

KeepthePlayerobjectselected,movethemouseovertheSceneViewandtaptheF(focus)keytocentertheviewontheLerpzmodel.

MoveLerpzontotheraisedplatformwiththeJumpPad(thenichewiththeyel­lowchevrons),neartheJail.(Seethescreenshotonthenextpage.)

IfyouclickPlaynow,youshouldseeLerpzstandinginthecourtyardoutsidethejail.Atthisstage,Lerpzcannotbemovedandthecameraalsoneedstobelinkedtoourplayer'scharacter.

ClickthePlaybuttonagaintostopthegame.

WeneedtogetLerpzmoving,butfirst,weneedtostepbackamomentandtakealookatourcamera.

12

Page 13: 3D Platform Tutorial

PositioningLerpzintheScene.

ThirdPersonCameras.Inafirst­personshooter,thecameraistheplayer'spointofview,sothereisnoneedtoworryaboutmakingitfollowanotherobjectaroundthescene.Theplayercontrolsthecameraobjectdirectly.First­personcamerasarethereforerelativelyeasytoim­plement.

However,athird­personviewpointcamerarequiresacamerathatcanfollowtheplayeraround.Thisseemssimpleenoughuntilyourealizethecameraalsoneedstoavoidgettingscenerybetweentheplayer'scharacterandthecamera'sviewpoint.Thiscanbeachievedusingraycastingtocheckforunwantedobjectsbetweenthecameraandplayeravatar,buttherearesomespecialcasestoconsider.Forexample:

• WhathappensifLerpzisbackedupagainstasolidwall?Shouldthecameramoveaboveandlookdownontheplayer?Shoulditmovetotheside?

• Whatifanenemygetsbetweenthecameraandourplayeravatar?

• Howshouldtheplayercontrolswork?Shouldtheyberelativetothecamera'sview?Ifso,thiscouldgetveryconfusingifthecameramovesinanunexpecteddirectiontoavoidanobstacle.

Anumberofsolutionsforthird­personcamerashavebeentriedovertheyears.It'sarguablethatnonehaveeverbeen100%perfect.Somesolutionsfadeoutanythingbetweenthemandtheirfocus,makingwallsorenemiessemi­transparent.Otherop­tionsincludecameraswhichfollowtheplayeraround,butwhichwill,ifnecessary,movethroughwallsandbuildingstokeeptheplayer'sviewconsistent.

13

Page 14: 3D Platform Tutorial

Theprojectsuppliedwiththistutorialincludesafewdifferentcamerascripts,butforthepurposesofthistutorial,we'lluseSpringFollowCamera.You'llfinditinthePro­jectPaneinsidetheCamerasub­folderoftheScriptsfolder.

DragtheSpringFollowCamerascriptfromtheProjectPaneontotheNearCam­eraobjectintheHierarchyPane.

ClickPlay.

Youwillgetanerrormessage.ThisappearsjusttotherightofthePlay,PauseandStepbuttonsatthebottomofUnity’swindow.

BringuptheDebugConsole(Shift+Cmd+C/Shift+Ctrl+ConPC),ifitisnotal­readyvisible.

Thisdisplaysanywarnings,errorsandotherdebugginginformationfromyourgame.Youwillprobablyseealotofcopiesoftheerrormessagerepeatedinthelog.High­lightoneandthepanebelowthelogwillshowabitmoreinformationaboutthiser­rormessage,asshowninimage3.1.

“Notarget”errormessage.

14

Page 15: 3D Platform Tutorial

TIP Wheneverpossible,theDebugLogwindowwillshowalinelinkingtotheof­fendingGameObjectintheHierarchy,(ortotheProjectPaneifthefaultisinaPrefaborScript).Youcanseethislineinthescreenshotabove.

TheUnassignedReferenceExceptionerrortypeisoneyouwilllikelyseeveryfrequentlyifyou’renewtoUnity.Itsoundsscary,butallitmeansisthatascriptvari­ablehasnotbeenset.TheDebugLogexplainsthistoo,solet’sdoasitsuggests:

ClickontheNearCameraobjectintheHierarchyPaneandlookattheSpringFollowCamera(Script)Component’sproperties.

TheTargetpropertyissettoNone(Transform).Thisdefinesthetargetobjectwewantthecameratopointat,solet’ssetit:

Stopthegameifyouhaven’tdonesoalready.

Ifit’snotalreadyselected,clickontheNearCameraobjectintheHierarchyPane.

DragourPlayerGameObjectfromtheHierarchyPaneontotheTargetsettingtosetit.

MakingChangesWhilePlayingWhenyouareplayingthegame,Unitywillletyoutweakthepropertiesofthevarious gameobjectsandcomponentsinthegame.However,itwillnotsavethem!Themo­mentyoustopthegame,anychangeswillbediscarded!

Ifyouwantyourchangestostick,alwaysstopthegamefirst!

IfyouclickPlaynow,thecamerastillwon'twork.YouwillseeaerrorsrelatingtotheSpringFollowCamerascript.ItneedsthetargettohaveaThirdPersonControllerscriptattachedtoit.Thisisbecauseathird­personcameraiscloselytiedtotheplayercon­trols:itneedstoknowwhattheplayerisdoingsothatitcanreactaccordingly.

Thefinalsettingsshouldlookasshownintheimagebelow:

15

Page 16: 3D Platform Tutorial

SpringFollowCamerascriptsettings.

Experimentwiththenumbersifyoudon'tlikethewaythecameraworks;thisisasub­jectivejudgementandthereisnosinglecorrectsettingforsomethinglikethis.

Thisisthefirstinaseriesofdependenciesthatweneedtodealwith.

CompletetheconnectionbetweenthecameraandtheplayerbydraggingtheThirdPersonControllerscriptfromtheScripts­>PlayerfolderintheProjectPaneontoourPlayerGameObject(intheHierarchyPane).(ThiswillbreakthePrefabconnection.)

TheThirdPersonControllerscriptalsohasitsownrequirementsanddependencies.ThemostimportantoftheseistheCharacterControllercomponent.Luckily,thescriptalreadytellsUnityaboutthis,soUnitywilladdthiscomponentforus.

Connections&Dependencies.Unityexcelsatshowingvisualassets,butthesealsohavetobeconnectedtoeachothertoprovidetheinteractivityweexpectfromagame.Theseconnectionsarediffi­culttoshowvisually.

Theseconnectionsareknownasdependencies,andit'swhatyougetwhenoneobjectrequiresasecondobjecttofunction.Thatsecondobjectmay,inturn,requireyetmore

16

Page 17: 3D Platform Tutorial

objectstowork.Theresultisthatyourassetsaretiedtoeachotherwithmyriadvir­tualbitsofstring­­scripts­­tyingthemalltogethertomakeagame.

Definingallthesedependenciesisakeyelementofgamedesign.

WenowneedtoaddatagtoourPlayerGameObject.ThisissothatscriptscanfindourPlayerintheScenebysimplytellingUnitytofindtheGameObjectwithsaidtag.

WithourPlayerobjectdisplayedintheInspector,opentheTagdrop­downmenuandchoosethe“Player”tag,asshownbelow.

SelectingthePlayertag.

NOTE TheTagslistedinthemenushownaboveareprovidedbyUnitybydefault.We’lllearnhowtocreateourownTagsandLayerslater.

TheTagwillbeusedlater,solet’sreturntotheCharacterControllerandourscript.

SelectthePlayerobjectandlookintheInspector.Itshouldlooksimilartothis:

17

Page 18: 3D Platform Tutorial

CharacterControllerandThirdPersonControllerScriptComponentsinplace.

OurnextstepistoadjusttheCharacterController.Atthemoment,theCapsuleCol­lideritusesislocatedtoofardownintheYaxis,soLerpzstandsonthinair.(YoucanseetheCollider'spositionintheSceneView:it'sthelongbluecylindricalwireframeshape.)WeneedtochangetheCenterYvalue.

AdjustingtheCharacterController’sCapsuleCollider­­displayedasabluewireframe.

18

Page 19: 3D Platform Tutorial

PositiontheCapsuleColliderasshowninthescreenshotabove.(Alittleexperi­mentationsuggestssettingtheCharacterController’sCenterYto1.03willalignitslowerendperfectlywithLerpz'sfeet.)

IfyouclickPlaynow,Lerpzshouldnowmovearoundwhenyouusethecontrolkeyswithhisfeetfirmlyontheground.

TheCharacterController&theThirdPersonControllerscriptInmostgames,theplayer’savatariscapableofimpossiblephysicalfeatssuchasturn­ingandstoppinginstantaneously,leapingimprobabledistancesandotheractionswhichwouldbedifficulttomodelusingtraditionalphysics.TheCharacterControllerthereforedecouplesourplayeravatarfromthephysicsengine,providingbasicmove­mentcontrols.

TheCharacterControllersimplifiesmovementforaplayer(andmanynon­player)charactertypes.Itconsistsofacapsulecollidertiedtoabasicmovementsystem,al­lowingourcharactertomovearound,climbstepsandslideupordownslopes.YoucanchangethemaximumstepandslopesizesintheInspector.

TheCharacterControllerisnormallyusedwithascript.ThistalkstotheCharacterControllerandextendsitscapabilitiestomeettheneedsofthegame.Inourproject,theThirdPersonControllerscriptperformsthisfunctionandaddsthenecessarysup­portforourplatformgame.Itreadsthejoystick,keyboard,mouseorotherinputde­viceandactsuponittocontroltheplayer’savatar.

TheUnityInputManager(Edit­>ProjectSettings­>InputManager)allowsyoutodefinehowtheinputdevicescontroltheplayer.

NOTE Thereisnothingspecialaboutthescriptsweareusingfortheplayer.TheyareperfectlyordinaryUnityscriptswhichhavebeenbuiltforthisproject.ThereisnodefaultCharacterControllerscript.

TheThirdPersonControllerscriptisalreadypartofthePrefab,sothereisnoneedtoaddit.

ThenextstepistomakeLerpzanimatecorrectlyandaddtheadditionalmovements,suchasjumpingandpunching...

AnimatingLerpzAtthispoint,Lerpzisjustglidingacrossthescenery.ThisisbecausetheCharacterCon­trollerdoesn'thandleanimation.Itdoesn'tknowanythingaboutourplayer'smodelorwhichanimationsequencesapplytoeachmovement.WeneedtoconnectLerpztohisanimationsequencesandthisisdonewiththeThirdPersonPlayerAnimationscript.

UsetheComponentmenutoaddtheThirdPersonPlayerAnimationscripttothePlayergameobject.

IfyouclickPlaynow,you'llseeLerpzanimatingcorrectly.

19

Page 20: 3D Platform Tutorial

Sowhat’sgoingonhere?Whatdoesthisscriptdo?TheanswerliesinhowUnityhan­dlescharacteranimationdata.

CharacterAnimationCharacteranimationsequencesarecreatedwithinamodelingpackage,like3DStudioMax,Maya,BlenderorCheetah3D,.OnimportingintoUnity,thesesequencesareautomaticallyextractedandstoredinanAnimationcomponent.

Theseanimationsequencesaredefinedonavirtualskeleton,whichisusedtoanimatethebasicmodel.Theseskeletonsdefinehowthemodel'smesh­­theimportantdatadefiningthevisiblesurfacesofthemodelitself­­ismodifiedandtransformedbytheenginetoproducetherequiredanimation.

Skeletons&ArmaturesIfyouarefamiliarwithstop­motionor“claymation”animationtechniques,youmaybeawareoftheiruseofmetalarmatures.Theanimatedmodelsarebuiltaroundthesearmatures.Thevirtualskeletonsusedin3Dmodelsaredirectlyequivalenttotheseandarerarelyascomplexasrealskeletons.

Themeshcomponentofsuchmodelsiscommonlyreferredtoasaskinnedmesh.Thevirtualskeletonprovidesthebonesbeneaththemeshanddefinehowitanimates.

AnimationblendingCharacteranimationsareusuallyblendedtogethertoprovidethenecessaryflexibilityforagame.Forinstance,ananimatedwalkcyclecouldbeblendedwithaseriesofspeechanimations,theresultbeingacharacterthatiswalkingandtalkingatthesametime.

Blendingisalsousedtoproducesmoothtransitionsbetweenanimations,suchasthetransitionbetweenawalkcycleandapunchsequence.

WeneedtouseascripttotellUnitywhenweneedtoswitchanimations,whenani­mationblendingisneededandhowitshouldbedone.Thisiswherescriptingcomesin.

TheThirdPersonPlayerAnimationscriptTheLerpzmodelwe'reusingwascreatedformultipleprojectsandcontainsfifteenanimationsequences.Onlyelevenareusedinthistutorial.IfyouselectthePlayerob­jectintheHierarchypaneandlookattheInspector,youwillseeallfifteenanimationsequenceslistedwithintheAnimationcomponent,ofwhichonlythefollowingareactuallyusedinthistutorial:

• Walk­­Thenormalwalkcycle.

20

Page 21: 3D Platform Tutorial

• Run­­Arunninganimation.(HoldtheShiftkeywhileplayingtorun.)

• Punch­­Playedwhenattackinganenemyrobotguard.

• Jump­­PlayedwhenLerpzleapsintotheair.

• Jumpfall­­PlayedwhenLerpz'sleapreachesitsapexandhestartstofall.

• Idle­­AloopplayedwhenLerpzisidle.

• Walljump­­AbackflipanimationplayedwhenLerpzjumpsoffawall.

• Jet­packJump­­PlayedwhenLerpz'sjet­packisslowinghisfall.

• Ledgefall­­PlayedwhenLerpzstepsofftheedgeofaplatform.

• Buttstomp­­PlayedwhenLerpzhasbeenstruckbyarobotguard.

• Jumpland­­PlayedwhenLerpzlandsafterajumporfall.

ThemodelandanimationsforLerpzwerecreatedusingMayaandimportedintoUnity.Formoreinformationaboutimportingmeshesandanimations,pleaserefertotheUnityManual.

MostoftheseanimationsaredealtwithbytheThirdPersonPlayerAnimationscript,whichchecksthecontrolstheplayerisusingandreactsaccordingly.Someanimationsarelayeredoverotherswhileothersaresimplyqueueduponeafteranother.Thescriptismostlyasetofmessageresponderfunctions.TherelevantmessagesarefiredoffbytheThirdPersonControllerscript,whichreadstheinputdevicesandupdatesthecharacter’sstateaccordingly.

Lerpz'sattackingmove­­hispunch­­isdealtwithinaseparatescript,ThirdPerson­CharacterAttack.(We’lladdthisscriptlater.)Thismayseemanarbitrarysplit,butitisnot:mostofthebasicmovements­­walking,running,jumping,etc.­­areprettysimi­larnomatterwhatyourplayer'scharacterlookslike.However,attackinganddefen­sivemovestendtobemuchmorevaried.Insomeplatformgames,theplayercharac­termighthaveagun;inanother,hemightperformamartialartsmove.

Inthistutorial,LerpzisamasterofthepopularWesternmartialartknownasFisti­cuffs,anamewhichtranslatesto"hittingyouropponentveryhardwithyourfist".Theanimationisasimplepunchinganimation.

GizmosThirdPersonCharacterAttackalsoincludesausefultestingfeature:agizmowhichdrawsaspheretorepresenttheareaaffectedbyLerpz'spunchingaction.GizmosaredrawninsideoneoftwoGizmo­drawingmessagehandlingfunctions.Inthisexample,thegizmo­­wireframeyellowspheredrawnatthepunchpositionanddisplayingitsareaofeffect­­isdrawninresponsetotheOnDrawGizmosSelected()function.

21

Page 22: 3D Platform Tutorial

ThisfunctionmustbestaticandwillbecalledbytheUnityEditoritself.AnalternativeisOnDrawGizmos(),whichiscalledbytheUnityEditoreveryupdatecycle,regard­lessofwhethertheparentGameObjecthasbeenselected.

TheJet­Pack

Lerpz’sJet­packinaction.

Atthispoint,ourcharacterisrunningandjumpingaround,buthisjet­packisnotyetworking.Lerpzusesthejet­packtoslowhisrateofdescent.Themovementisalreadyinplace,butthejet­pack'sjetsdon'tanimateatall.Tomakethejetswork,wewillneedtoaddtwoParticleSystemsandaPointLightcomponent.Theparticlesystemswillproduceaflame­likeeffect,whilethepointlightwillgivetheillusionoftheflamesactingasasourceofillumination.

TIP Ideallywewouldhaveapointlightsourceforeachjet,butthejetexhaustsarecloseenoughtoeachotherthatwecangetawaywithjusttheone.Aslightsarecomputationallyexpensive,thisisahandyoptimization.

WhatisaParticleSystem?ParticleSystemsemitdozensofparticles­­usuallyflat2Dbillboardsorsprites­­intothe3Dworld.Eachparticleisemittedatasetspeedandvelocity,andlivesforacer­taintime.Dependingonthesettingsandbillboardmaterialsused,theseparticlesys­temscanbeusedtosimulateanythingfromfire,smokeandexplosionstostar­fields.

22

Page 23: 3D Platform Tutorial

AddingtheParticleSystemsUsetheGameObjectMenutocreateanemptyGameObjectintheHierarchyPane.

RenamethisGameObject"Jet".

WiththenewGameObjectselected,add:

AnEllipsoidParticleEmitter

AParticleAnimator

AWorldParticleCollider

AParticleRenderer

Uncheckthe“Enabled”checkboxintheInspectorfortheParticleRendererComponent.Thiswilldisableittemporarily.

PositiontheJetdirectlybelowLerpz'sright­handjetexhaust.

Re­enabletheParticleRenderer.

AdjustthesettingsfortheEllipsoidParticleEmitterasshownbelow:

EllipsoidParticleEmittersettings.

23

Page 24: 3D Platform Tutorial

Thesesettingsresultinanarrowstreamofparticleswhichwewillusetosimulateajetofflame.

TIP Iftheparticlesarenotmovingdirectlydownwards,useUnity'srotationtoolstorotateourobjectuntilthejetismovinginlinewiththejet­pack'sexhaust.

Whenwe'redone,theparticlesystemwillbeattachedtothePlayer's"torso"childobjectinitshierarchy.Thiswillcausethejettofollowtheplayer'smovements.Atthispointhowever,we'reprimarilyinterestedingettingittolookright,sodon'tworrytoomuchaboutaccurateplacement.

TheMinSizeandMaxSizesettingsdefinethesizerangeoftheparticles.TheMinEn­ergyandMaxEnergysettingsdefinetheminimumandmaximumlifespanofthepar­ticles.Ourparticleswillliveforonlyashorttime­­0.2secondsinthiscase­­beforefadingaway.

Wesetthequantityofparticlestoemitto50.TheMinEmissionandMaxEmissionrangedefineshowmanyparticleswewantonscreenatanyonetime,andhowvari­ablethisemissionshouldbe.Wehavechosentosettheminimumandmaximumrangetosimilarvaluesorit'lllooklikethejetissputteringratherthanblastingawaysmoothly.Theresultshouldbeasmoothflowofparticles.

NOTE We'vedisabled"SimulateinWorldspace"here.Thishelpsgivetheimpressionthatwehaveahot,fastjetofgasratherthanamuchslowerflame,eventhoughtheparticlesaren'tmovingallthatquickly.BymakingthejetignoreLerpz'stwistsandturns,itlookssimilartothehot,steadyflamefromablow­torch.

Now,settheParticleAnimatorComponent’ssettingsasshown:

ParticleAnimatorsettings.ThevaluesinthechartdefinetheColorAnimationentries.Don’tforgettoadjusttheothersettingstoo!

24

Color Red Green Blue Opacity

Animation(0)

Animation(1)

Animation(2)

Animation(3)

Animation(4)

254 255 219 100

254 255 116 29

255 92 0 13

105 63 50 10

13 15 17 4

Page 25: 3D Platform Tutorial

TheParticleAnimatorwillanimatetheparticlecolorsastheyage.Theparticleswillstartoffwhite,darkeningthroughyellowandorangeasourvirtualjetcools.Sincewe'regoingtoberenderingatextureintoeachparticle,theParticleAnimatorwillbeusedtotintthisparticle,sothecoloranimationwillbesubtle,buthopefullyeffective.

Thecolorpickerdialogwhichappearswhenyouclickonacoloralsooffersan“Opac­ity”slider.Thetableshownwiththecolorsettingsalsoincludethisas,byreducingopacitythroughtheanimationcycle,theparticle'flames'willappeartofadeawayastheycool.

NOTE TheOpacitysettingmodifiestheAlphachanneloftheparticle,defininghowtransparentitis.Theparticlematerialweusealreadyincludesalphachannelinformation,whichisthenmodifiedfurtherbytheOpacitysetting.

NextistheParticleRenderer.ThisComponentdrawseachparticle,soitneedstobetoldhowtheparticleswillappear.Italsodefinesthematerialtousetorendereachparticle.Wewantaflame­likejeteffect,soweshallusethe“fireadd”material,whichcanbefoundin:Particles­>Sources­>Materials­>fireadd

TIP ThisassetisalsoincludedintheStandardAssetsfolder.

Setthiscomponent'svaluesasfollows:

25

Page 26: 3D Platform Tutorial

ParticleRenderersettings.

TheStretchParticlessettingtellsUnitywhethertheparticlesshouldberenderedstretchediftheyaremovingathighspeed.Wewanttheparticlestostretchalittleaccordingtotheirvelocity.Thisaddsasubtlevisualcueandmakesthesmall,roundshapeswe'reusingforthisjetblendmoreintoeachother.

NOTE TheCastShadowsandReceiveShadowssettingshavenoeffectunlessyouuseacustomshader.Thisisanadvancedtopicbeyondthescopeofthistutorial.

AddingtheLightOurjetlookscool,butit’sreallyanillusion:ParticleSystemsjustspitoutlotsoftinyimages,buttheresultingflame­likeeffectdoesnotemitlight.Tocompletetheillu­sion,wewillcreateaseparatePointLightGameObject.We’llswitchitonandoffatthesametimeasthejets.Theresultwillbeajetofflamewhichappearstolightupitsimmediatesurroundings.(Wewillonlyuseasinglelight,ratherthanonelightperjet;thissavesprocessingpowerwhilemaintainingourillusion.)

CreateanewPointLightGameObject.

26

Page 27: 3D Platform Tutorial

Namethis"JetLight"andpositionitbetweenthetwomodeledjetsonLerpz'sjetpack.(We’llcomebacktoourJetparticlesystemshortly.)Thislightwillcre­ateanillusionthatthejetsareemittinglight.

Forthiseffecttowork,weneedabrightpointlightwithahighintensity.

SelecttheLightandadjustsettingsasshown:

Jet­packLightsettings.

Whynoshadows?Shadowsarecomputationallyexpensiveformosthardware.Itmakessensetoavoidcalculatingthemifwecanavoidit,andthisisoneareawherewecangetawaywithit.Thejetsaren'tverybig,sotheyonlyneedtolightupLerpz'sback.Thepointlightwillalsobereflectedinnearbyscenery,butitwon'tbebrightenoughtomakethelackofshadowsnoticeable.

ThenextstepistoupdatethePlayerGameObjecttoincludeourjetandlightobjects.Todothis,we’llfirstaddourJettotheProjectPaneasaPrefab:

IntheProjectPane,selectthe(empty)Playerfolder,thenclickCreate...

Fromthedrop­downmenu,choosePrefab.ThiswillcreateanemptyPrefabob­jectinside.

RenametheemptyPrefabtoJet.

DragourshinynewJetobjectfromtheHierarchyPaneontoournewPrefab.

TheJet’snameintheHierarchyPaneshouldturnbluetoshowthatit’snowlinkedtoaPrefab.We’regoingtousetwoinstancesofourJetPrefabforLerpz’sjetpack.UsingaPrefabmeanswecantweakboththejetsbysimplyeditingtheoriginalPrefab.

27

Color Red Green Blue Opacity

Color 255 206 155 100

Page 28: 3D Platform Tutorial

DeleteouroriginalJetobjectfromtheHierarchyPane.(ButleaveourJetLightwhereitis!)

Nowweaddthejet(twice)andthelight(once)toourPlayer:

GotothePlayerobjectintheHierarchy,

Openitupuntilyoufindthetorsochildobject.

DragtheJetPrefabobjectontothisobjecttwice.ThiswillcreatetwoJetin­stances.

RenamethetwoJetinstancesJetLandJetR,

DroptheJetLightontothesametorsoobject.

Youshouldnowhaveanobjecthierarchythatlookssomethinglikethis:

Jet­packHierarchy.

UseUnity'smanipulationtoolstopositioneachJetPrefabunderitsrespectivejetoutletinLerpz'smodel.Youmayneedtorotatethem,sothattheparticlesaregoingintherightdirection.

MovetheJetLighttoapointbetweenthetwoJetPrefabs.

Whenyou'veachievedthis,Lerpzshouldnowhavetwoflamingjetsgushingfromhisjet­packashemovesaround.We'realmostdone!

ThefinalstepistomakeJetPrefabsandJetLightobjectsactivateonlywhenhe'sjumping.Thisisachievedthroughscripting.

LookfortheJetPackParticleControllerscriptinScripts­>Playeranddragthisontothetop­most"Player"objectintheHierarchyPane.Thisaddsthescripttoourplayercharacter.

28

Page 29: 3D Platform Tutorial

Youshouldnowfindthatthejet­packworksasexpected.Thescriptcontrolsthetwoparticlesystemsandthelight,synchronizingthemwithLerpz’smovementsandtrig­geringallthreeelementstogetherwhenevertheplayerpressesthejumpbuttontojumportoslowhisdescent.

Lerpz’sJet­packinaction.

BlobShadowsLerpzmustbeeasytoidentifyatalltimes,sothatplayerswon’tlosetrackoftheiravatarswhenthegamegetsvisuallybusy.Mostofthisworkisuptotheartistsandthegame’sdesigner,buttherearesomeelementswhichhavetobehandledbyUnityitself.

Oneofthemostimportantoftheseisshadowingandlighting.Toaidperformance,theeffectsoflightingareoftenpre­renderedintothetexturesbytheartistusingamodelingpackage­­atechniqueknownas“baking”.Thistechniqueonlyworkswellonstaticobjects,suchassetsandfixedprops.(Wehavedeliberatelyavoidedthistechniqueforthistutorial’sassets.)Acharacterwalkingunderastreetlightneedstoreacttothatlightinrealtime.Theroadbeneaththecharactercanhavethelightingbakedin,butthecharactercannotusethistrickandalsoneedstoreacttothelights.

Thesolutionistopositiondynamiclightswheretheyneedtobe­­ifyouuse”baked”textures,remembertoaddalightwhereveroneisimpliedbythebakedlighting­­butmakethelightsonlyaffectmovingobjects.Lightshavealreadybeenplacedinthesceneforyou.

Thisleavesonefinalelement:shadows.

29

Page 30: 3D Platform Tutorial

Ina3Dplatformgame,theshadowplaysakeyroleintellinguswherethecharacterwilllandifheisjumpingorfalling.ThismeansLerpzshouldhaveagood,visibleshadow,whichisnotthecaseatthemoment.

Shadowscanbeproducedusinglights,withtheshadowcomputedandrenderedinrealtimebythegraphicsengine.However,suchshadowsareexpensiveintermsofprocessingpower.Inaddition,notallgraphicscardscancomputeshadowsquicklyoreffectively;oldercardsmaynotbeabletodosoatall.

Forthisreason,wewilluseaBlobShadowforLerpz.

AddingaBlobShadowABlobShadowisacheat.Insteadofcastingraysoflightandcheckingiftheyhitany­thing,wesimplyprojectadarkimage­­inthiscasejustacircularblackblob­­ontoanythingbelowourcharacter.Thisisquickerandeasierforthegraphicscardtodo,soitshouldworkwellonallrangesofhardware.

UnityincludesaBlob­ShadowprefabinitsStandardAssetscollection,soweshallusethisratherthancreatingourown.ThisassethasalreadybeenimportedandaddedtotheprojectintheBlob­Shadowfolder.OpenthisfolderandclickontheblobshadowprojectorPrefabanddragitontoourtop­levelcharacterobject­­Player­­intheHier­archyPane.ThisshouldaddtheProjectorjustbelowthetoplevelinourPlayerob­ject'shierarchy:

TheBlobShadowProjectorPrefabinthePlayer’shierarchy.

Next,youwillneedtomodifytheblobshadowprojector’sPositionandRotationdatasothatitisdirectlyaboveourcharacterandpointingdirectlydownattheground.

Selectthe4­Splitlayout.

30

Page 31: 3D Platform Tutorial

Settheblobshadowprojector’sRotationvaluesto90,180and0respectively.

NowusethesideandtopviewstomovetheprojectordirectlyoverLerpz’head.Youmightwanttomoveitupordownalittleuntilyou’rehappywiththeshadow’ssize.

CreatinganewLayerAtthispointyouwillhavenoticedthattheblobisalsobeingprojectedontoLerpz.Wedon’twantthistohappen.Therearetwooptionstogetaroundthis:movetheNearClipPlanesettingfurtherawayfromtheprojector,orsimplytellitnottoprojectontoobjectsinspecificLayers.Weshallusethelatteroption.

WhynotadjusttheNearClipPlane?Thistechniquemightseemeasiestatfirstglance,buttheplanewouldneedtobead­justedbyscriptingtotakeintoaccountLerpz’sanimations.Hisfeetmovefurtheroutwhenhejumps,thenbrieflymovealittlecloserwhenhelandsagain.SincetheshadowmustalwaysbeprojectedontothegroundonwhichLerpzstands,thismeanstheNearClipPlanecannotremainthesamethroughoutthesesequences.

OpenupthePlayerGameObject.

OpentheLayerdrop­downintheInspector.

ChooseAddnewlayer…

ClickonthefirstemptyUserLayerentryandnameitnoShadow.

YoushouldnowseesomethinglikethisinyourInspector:

AddinganewLayerusingtheTagManager.

31

Page 32: 3D Platform Tutorial

NowclickbackonthePlayerobjectintheHierarchyPanetobringuptheusualInspectorsettings.

Clickonthe“Layer”drop­downandsetittothenewlayername,noShadow.UnityasksifyouwishtoapplythistoallchildGameObjects:click“ChangeChil­drenLayers”.

NextweneedtotelltheBlobShadowProjectornottoprojectontoobjectsinthisLayer.

Bringuptheblobshadow’spropertiesintheInspectorandlookattheIgnoreLayersentryintheProjectorComponent.

Usethedrop­downmenutotherighttoselectthenoShadowLayer(whichshouldapplytochildrenaswell),asshown:

TheBlobShadowProjectorsettings.

Ifyounowplaythegameandmovearoundyoushouldseetheshadowbehavingprettymuchasexpected....exceptifyoujumparoundnearthecollectablefuelcells.Ifyoutrythis,youwillseetheitemshowingtheblobshadowtoo.

Wewantcollectableitemstostandoutatalltimes,itmakessensetotelltheBlobShadowProjectortoavoidthesetoo.

We’llbelookingatthesecollectablesinmuchmoredetailinthenextchapter,butlet’sfixthisproblemnowwhilewe’rehere.

First,stopthegame.

NowgototheProjectPaneandlocatetheFuelCellPrefabandHealthLifePick­UpPrefabobjects.You’llfindtheminsidethePropsfolder.

SelecttherootobjectofeachPrefabandsetitsLayertonoShadow,asshownbelow:

32

Page 33: 3D Platform Tutorial

Layerchangedto“noShadow”

NOTE Whenmakingachangetoaparentobject,Unitywilloftenaskifthechangeshouldalsobeappliedtothatobject'schildren.Doingsocanbedangerousifyouhaven'tthoughtthroughalltheramifications.

Inthiscase,wewantallthechildobjectsofthe"FuelCellPrefab”and“Health­LifePickUpPrefab”GameObjectstobeinthesame"noShadow"layer,sowhenUnityasks,agreetopropagatethechanges.

ScriptingConceptsHistoryislitteredwithsurprisinglycomplexmachines­­knownasautomata­­builtbyourancestorsforthepurposesofentertainment.Somewereveryelaborateandcouldevenperformsimpleplaysusingpuppets.Otherswereinteractiveandchangedtheirbehavioraccordingtouserinput.Thesemachineswerefundamentallythesame:thedesignercreatedassets­­puppets,props,paintedbackdrops,etc.­­andthendesignedmachinerytomakethoseassetsbehaveastheydesired.

Thebasicprinciplehasremainedunchangedovertheyears.Computershavemerelyturnedphysicalmachinery,builtofsteelandsprings,intovirtualmachinerycontrolledbylistsofinstructions.Unityreferstosuchlistsofinstructionsasscripts.

Mostscriptsarecenteredonaconceptpopularingamedevelopment:theFiniteStateMachine.AFiniteStateMachineessentiallydefinesasystemofinteractingconditions,knownasstates.

Astatecanbealmostanything,suchaswhetheranobjectshouldberenderedatall,whetheritshouldbesubjecttothelawsofphysics,belitorcastashadow,whetheritcanbounce,itspositiononadisplay,andsoon.TheInspectorPaneletsuschangemanysuchstatesdirectlybecausethesestatesarecommontoalmostallgames.

However,thereisanothertypeofstatewhichisspecifictothegameitself.Unitydoesnotknowthattheplayer'savatarisanalien,howmuchdamageLerpzcantakeorthatLerpzhasajet­pack.HowcanUnitybeawareoftherobotguards'requiredbe­haviororhowtheyshouldinteractwithLerpz?

Thisiswherescriptscomein.Weusescriptstoaddtheinteractionandstatemanage­mentspecifictoourgame.

Ourgamewillneedtokeeptrackofanumberofstates.Theseinclude:

• Theplayer’shealth;

33

Page 34: 3D Platform Tutorial

• Thenumberoffuelcanisterstheplayerhascollected;

• Whethertheplayerhascollectedenoughfueltounlocktheforcefield;

• Whethertheplayerhassteppedonajumppad;

• Whethertheplayerhastouchedacollectableitem;

• Whethertheplayerhastouchedthespaceship;

• Whethertheplayerhastouchedarespawnpoint;

• WhethertheGameOverorGameStartscreensshouldbeshown;

• ...andmore.

Manyofthesestatesrequireteststobemadeagainstotherobjects’statestoensurethey’reuptodate.Sometimesweevenneedintermediatestates,toaidatransition.Forexample,collectingafuelcanisterwillforceachecktobemadetoseeiftheplayerhasenoughtoshutdowntheforcefield.

Organization&StructureInthistutorialthestatemachinesfortheplayer,thelevelandtheenemiesarehan­dledbyabunchofscriptslinkedtovariousGameObjects.Thesescriptstalktoeachother,sendingeachothermessagesandcallingeachother'sfunctions.

Thereareanumberofwayswecansetuptheselinks:

• ByaddingalinkexposedintheInspector,ontowhichyoudroptherelevantobject.Thisisidealforgeneral­purposescriptswhichyouintendtore­useinotherprojects.

Thisisthemostefficientasthescriptmerelyplucksthedatafromtherelevantvari­ableanddoesn'tneedtodoanysearching.However,itdoesassumeyouknowinad­vanceexactlywhichobjectorcomponentyou'llbelinkingto.

Weusethisoptionforthecut­scenecamerasinLevelStatus.(Thisscript,currentlyjustashortstub,isalreadyattachedtotheLevelGameObject.)Thisgivesustheflexibilitytosetupmultiplecameras,oneforthe"levelexitunlocked"cut­sceneandanotherforthe"levelcomplete"sequence.Inpractice,we'reonlyusingtwocamerasinthegame;onefortheplayerandthe"levelcomplete"sequence,theotherforthe"un­locked"cut­scene.Buttheoptionistheretochangethis.

• Settingupalinkwithinthescript'sAwake()function.TheAwake()functioniscalled

oneveryscriptyouwritebeforethefirstUpdate()eventisfiredontheGameObjectitisattachedto.SettingupthelinkhereallowsyoutocachetheresultforlateruseinanUpdate()function.Typically,youwouldsetupaprivatevariablewithalinkto

anotherGameObjectorcomponentyouneedtoaccesswithinyourscript.Ifyouneed

34

Page 35: 3D Platform Tutorial

todoaGameObject.Find()calltolocatetherelevantobject,itismuchbetterto

dosoonceonly,insideAwake()asGameObject.Find()isquiteslow.

Thisoptionismoresuitedtothosesituationswhereyoudon'tneedtheflexibilityofthefirstoption,butdon'twanttohavetoperformaconvolutedsearchfortheobjecteverygamecycle.Thesolutionisthereforetosearchfortheobjectwhenthescriptis'wokenup',storingtheresultsofthesearchforuseintheupdatesection.

Forexample,theLevelStatusscript,whichhandleslevelstate,cacheslinkstoanum­berofotherobjects,includingthePlayerobject.Weknowthesewon'tchange,sowemayaswellmakethecomputerdothisworkforus.

• SettingupalinkduringtheUpdate()function.Thisfunctioniscalledatleastonce

pergamecycle,soitisbesttoavoidusingslowfunctioncallshere.However,theGameObject.Find()andGetComponent()functionscanbequiteslow.

Thisoptionisusedforthosesituationswheretheobjectyouneedcouldchangeatanytimeduringthegameplay.

Forexample,whichofthemultipleRespawnpointsinthistutorial'sSceneshouldtheplayerberespawnedat?Thisclearlychangeswhilethegameisrunning,soweneedtohandlethisaccordingly.

Theproblemwiththisisthatit’sslow,soitisbesttodesignyourgamesuchthatyoudon’tneedtodothisoften.

ScriptsinaVisualDevelopmentEnvironmentUnityisanunusualtoolinthatitsfocusisonthevisualassetsratherthanthelinksandconnectionsbetweenthem.AlargeUnityprojectcanhavedozensofscriptsofvaryingcomplexitydottedaroundtheHierarchy,sothedesignusedforthistutorialusessomeobject­orientationtechniquestoalleviatethis.

Thescriptwhichdealswithaparticularpartofthestatemachine­­e.g.playeranima­tion­­shouldalsobetheonewhichkeepstrackoftherelevantstatevariables.Thiscanmakethingsalittlecomplicatedwhenascriptneedstoaccessastatevariablestoredinanotherscript,whichiswhysomescriptscachesomevalueslocallytomakeaccesstotheinformationquicker.Thistechniquealsooccasionallyresultsinchainsofcommands,whereafunctioninonescriptmerelycallsasimilarfunctioninanotherscript.Thehandlingoftheplayer'sdeathandhealthisanexampleofthis.

NOTE AsyougetmoreexperiencewithUnity,youwillfindotherwaystohandlestatesthatmaybebettersuitedtoyourowngames.Thedesignpatternusedinthistutorialshouldnotbeconsidereda“onesizefitsall”solution!

35

Page 36: 3D Platform Tutorial

Ifyouwish,youcanturnourPlayerGameObjectintoaPrefabcontainingallourchanges,soyoucanreuseitinotherprojectsasastartingpoint:

ClickonthePlayerfolderintheProjectPane.

CreateanewPrefab.(It’llappearinsidethePlayerfolder.)

GivethenewPrefabanappropriatename­­IsuggestLerpzPrefab.

DragourPlayerGameObjectontoLerpzPrefabtocompetetheprocess.

Death&RebirthPlatformgamecharacterstendtoleadriskylivesandLerpzisnoexception.Weneedtoensurehelosesalifeifhefallsoffthelevel.Wealsoneedtomakehimre­appearatasafespotonthelevel­­oftencalleda“respawnpoint”­­sohecancontinuehisquest.

AnotherpointisthatifLerpzcanfalloffthelandscape,itisalsopossiblethatthelevel'sotherresidentscoulddosotoo,sothesemustalsobedealtwithappropriately.

Thebestsolutionforthisistouseaboxcollidertocatchanythingfallingoffthelevel.We'llmakeitverylongandbroad,sothatifaplayershouldtryusingthejet­packwhilejumpingoff,we'llstillcatchhim.However,Lerpzwillneedsomewheretores­pawn.We’llcometotherespawnpointsshortly.First,let’sbuildtheboxcollider:

CreateanemptyGameObject.

RenamethenewobjectFalloutCatcher.

AddaBoxColliderobjecttoit.

AddtheFalloutDeathscriptfromComponent­>ThirdPersonProps.

UsetheInspectortosetthevaluesasshowninthescreenshotbelow:

36

Page 37: 3D Platform Tutorial

FalloutCatchersettings.

TheFalloutDeathscriptThisscriptisshortbecauseitsimplydelegatesalltheworktotheThirdPersonStatusscript.(ThisneedstobeattachedtoLerpz,butwewon’tdosojustyet.)

Thecodetohandlethecollider’striggerisinOnTriggerEnter().Thisfunctionis

calledbyUnitywhentheBoxColliderisstruckbyanotherGameObjectcontainingaCollidercomponent,suchasLerpzoranenemy.

Therearethreetests:onefortheplayer,oneforasimpleRigidBodyobject,andathirdtesttocheckiftheobjecthasaCharacterControllerComponent.Thesecondtestlooksforanypropssuchasboxesorcratesfallingoffthelevel.(Wedon’thavesuchitemsinthislevel,butyoucanaddoneifyouwouldliketoexperiment.)Thethirdtestisusedforenemiesasthesewon’thaveordinaryphysicsattached.

Iftheplayerhitstheboxcollider,thecodesimplycallstheFalloutDeath()func­tioninLerpz’sThirdPersonStatusscript.

IfanotherobjectwithacolliderobjecthitsourGameObject,wesimplydestroyit,re­movingitfromtheScene,otherwiseit’llfallforever.

Inaddition,wehave:

• Theutilityfunction­­ Reset()­­whichensuresanyrequiredComponentsarealso

present.ThisfunctioniscalledbyUnityautomaticallywhenaddingthecomponentfor

37

Page 38: 3D Platform Tutorial

thefirsttime.ItcanalsobecalledintheEditorbyclickingonthecog­wheelicontotherightofanyComponent’snameintheInspector:

TheResetmenucommand.

• The@Scriptdirective,whichalsoaddsthescriptdirectlytoUnity’sComponentmenu.ThisisaconvenienceandsaveshavingtohuntforitinsidetheProjectPane­­usefulifyouhaveacomplexprojectwithlotsofassets.

Ifwetrytoplaythegameatthispoint,Unitywillcomplainbecauseitdoesn’tknowwheretomakeLerpzreappear.Thisiswhererespawnpointscomein...

RespawnPointsWhentheplayerdies,weneedsomewheresafeforhimtore­appear.Inthistutorial,Lerpzwillreappearatoneofthreerespawnpoints.WhenLerpztouchesoneofthesepoints,itwillbecomeactiveandthiswillbewherehereappearsifhedies.

LerpzstandingonanactiveRespawnpoint.

TherespawnpointsareinstancesoftheRespawnPrefabprefabobject.(You'llfinditintheProjectPane'sPropsfolder.)

Thisprefabisamodelofateleportbase,coupledwiththreecompleteparticlesys­tems,aspotlightandsomeotheroddsandends.Here'sthebasicstructure:

38

Page 39: 3D Platform Tutorial

• RSBasecontainsthemodelitself:ashortcylindricalbasewithaglowingbluediscinthecenter.

• RSSpotlightisaspotlightobjectwhichshinesasubtlebluelightupfromthesurfaceofthemodel,givingtheillusionthatthebluetextureisglowing.

• Theremaininggameobjectsareparticlesystems.TheRespawnscriptattachedtotheparentRespawnPrefabobjectswitchesbetweentheseparticlesystemsdependingontheprefab'sstate:

‣ Iftherespawnpointisinactive,asmall,subtleparticleeffectisshownlookinglikeabrightbluemist.ThisiscontainedinRsParticlesInactive.

‣ Iftherespawnpointisactive,alarger,moreostentatiouseffectisshown.ThisiscontainedinRsParticlesActive.

Onlyonerespawnpointcanbeactiveonthelevelatanyonetime.Whentheplayertouchestherespawnpoint,acolliderobject(setasatrigger)detectsthisandtrig­gersactivationoftherespawnpoint.

‣ Theremainingthreeparticlesystems­­RSParticlesRepawn1,RSParticlesRepawn2andRSParticlesRepawn3­­areenabledtogetherwhentheplayerisrespawnedattherespawnpoint.Theseareone­shotparticlesystems.Thescriptletstheseplay,thenrestorestheRsParticlesActiveparticlesystemoncethisone­shotsequenceiscompleted.

Theprefabcontainsascript,Respawn,whichcontrolsthestateoftherespawnpoint.However,inorderforthegametoknowwhichspecificrespawnpointtheplayerneedstobereturnedtowhenhedies,weneedtoarrangetherespawnpointsinahierarchyunderamastercontrollerscript.Let’sdothisnow:

DragtheRespawnPrefabintotheSceneView.

Positionitasshownintheimageonthenextpage.

RenamethisinstanceRespawn1.

Repeattheabovestepstwicemore.Youcanplacethesewhereveryoulike­­addmoreifyouwish!Isuggestputtingonenearthearenaatthefarendofthelevel,andanothernearthetreesinthegardenabovetheplatforms.

ThenextstepistocreateacontainerGameObject.

RenamethisRespawnPoints

MakealltherespawnprefabinstanceschildrenofRespawnPoints.

39

Page 40: 3D Platform Tutorial

Positioningthefirstrespawnpoint.(Lerpzhasbeenmovedoutoftheshotforclarity.)

HowitworksWhentheSceneisloaded,UnitycallstheStart()functionineachinstanceoftheRespawnscript,wheresomeusefulvariablesareinitializedandpointerstootherele­mentsarecached.

Thekeymechanismiscenteredaroundthisstaticvariable:

static var currentRespawn : Respawn;

ThisdefinesaglobalvariablenamedcurrentRespawn.

Thestatickeywordmeansitissharedacrossallinstancesofthescript.Thisletsus

keeptrackofwhichrespawnpointisthecurrent,activeone.However,whentheScenebegins,noneofthepointsisactivated,soweneedtosetadefaultoneforourScene.TheUnityInspectorwillnotdisplaystaticvariabletypesatall,sothescriptde­finesanInitialRespawnproperty,whichneedstobesetforeachinstance.

DragthedefaultRespawnpointontothis.

You’llneedtorepeatthisforallrespawnpointsinthescene.(Inthetutorialproject’scase,thedefaultissettoRespawn1,whichislocatedneartheJailanddirectlybelowtheplayer’sstartingpoint.)

NOTE Itisn’tpossibletosetthesepropertiesdirectlyintheoriginalPrefab.

40

Page 41: 3D Platform Tutorial

Whenarespawnpointisactivatedbytheplayertriggeringitscollider,thatpoint'sRespawnscriptfirstdeactivatestheoldRespawnpointandthensetscurrentRespawntopointtoitself.TheSetActive()functiontakescareoffiringofftherelevantparticlesystemsandsoundeffects.

TherespawningoftheplayercharacterishandledbytheThirdPersonStatusscript,whichmanagesmostoftheplayer’sgamestate.

AddtheThirdPersonStatusscripttothePlayerGameObject.Thescriptcanbefoundin:Scripts­>Player­>ThirdPersonStatus.

Therespawnpointscriptsalsohandlesoundeffects.Theseareplayedasone­shotsamples,exceptforanAudioSourceattachedtoeachRespawnPrefab.ThisCompo­nentcontainsthe"active"sound,whichisaloop.Thescriptsimplyenablesordisablesthissoundasappropriate,eitherwhileplayingaone­shoteffect­­suchaswhentheplayerisactuallyrespawningoractivatingtherespawnpointitself­­orwhentheres­pawnhasbeendeactivated.

NOTE Unitymakesitalmosttooeasytoaddsoundeffects.Wheneveryouplantoaddsuchanasset,considercarefullyhowitwillbeused.Forexample,wehaven'tincludeda"respawndeactivated"soundbecauseyou'dneverhearthesoundbeingplayed;you’reunlikelytopositiontworespawnpointswithinear­shotofeachother.Ifyouweretoconverttheprojectintoamultiplayergame,youmightwanttoaddsuchasoundandthenecessaryscriptcodetohandleit.

Thescriptisnotcomplexandyoushouldfindthescriptcodeeasyenoughtofollow.Wewillreturntotherespawnpointsinlaterchapters.

41

Page 42: 3D Platform Tutorial

SettingtheScene

FirstStepsInthissectionwewilllookatbuildingthegameworldwheretheactiontakesplace.Inmovieterminology,thismeansbuildingtheset,placingthepropsandwritingthescriptingthatletsourherointeractwiththem.

Ourfirststepistopreparethestage.Thetutorialfilealreadyhasthebasiclevelmeshsetupandpopulatedwithanumberofcollectableitems.Wewillplaceafewmorepropsandelements,butmosthavebeendoneforyouasplacingallofthesepropswouldmakeforaverylong,veryboringtutorial.

LightingThelevelisalreadyprovidedwithanambientlightaswellasmyriadpointlights.Theselightupthescenery,theplayer,theenemiesand,toalimitedextent,thepick­ups.

Inthisproject’scase,thelightswereplacedbytheartistwhomodeledthelevel.Light­ingplayssuchanimportantroleincreatingtheambienceandsenseofplacethatitisusuallybesttoleavethistothemodelerwhobuiltthelevel.

42

Withourheronowmobile,thenextstepistogivehimsomethingtodo...

Page 43: 3D Platform Tutorial

PlacingPropsThebasicsetisprovidedforyou,alongwithanumberofpropsalreadyinplace.

BuildingYourOwnLevelsThetutoriallevelwasbuiltbyarrangingscenerycomponentsinMayaandthenim­portingthelevelintoUnity.Ifyouwouldliketoexperiment,orevencreateadditionallevels,theindividualsceneryelementscanbefoundintheBuildYourOwn!folderintheProjectPane.

Therearealargenumberofthefuelcellpropsandplacingallofthesewouldmakeforaverydulltutorial.Ifyouhavereadtheprevioustutorials,youwillalreadyknowhowtodothisanyway,sowewilllimittheplacementtothe"Health"pickups,thejump­padsandrespawnpoints,amongothers.

HealthPickupsWebeginwithasimpletask:addingsomecollectablehealthpickups.Thesearespin­ning,glowingheartsthataddhealthtoourplayer.ThepickupsarealreadydefinedasPrefabsintheProjectPane.Lookinsidethe"Props"folderandyouwillfindtheHealthLifePickUpPrefabobjectreadytouse.

Placingahealthpickup.

DragoneontotheSceneViewanduseUnity'spositioningtoolstopositionitsomewhereonthelevel.

Repeatthisprocessuntilyou'veplacedabouthalfadozenofthesearoundthemap.

43

Page 44: 3D Platform Tutorial

Wherethey'relocatedisuptoyou,thoughtheyshouldn'tbetooeasytofindorgetto.Considerhowtheplayermightplaythelevelanddecidewherethebestplacesareforsuchpickups.Itisbestnottobetoogenerousorthegamewillbetooeasy.

Finally,weshouldgroupthesepickupsintoafolderofsomesorttoavoidhavingthemclutteruptheHierarchyPane.WecandothisbycreatinganemptyGameObjectusingtheGameObject­>CreateEmptymenuitem.RenamethisnewobjecttoHealthPickups anduseittogroupyourhealthpickups,asshown:

Healthpickupshierarchy.

TheForceField

Theforcefield.

Atthemoment,theforcefieldtrappingourhero’sspaceshipdoesn’tanimate:it’sjustastaticmeshtexture.Theresultisvisuallydisappointing.

44

Page 45: 3D Platform Tutorial

Thereareanumberofwaystoachieveadecentvisualeffect,butwhichtochoose?Sometimesasimplesolutionisthebest:wewilljustanimatethetexture’sUVcoordi­natestogiveittheeffectofaripplingforcefield.

TheanimationwillbedoneusingashortscriptwhichcanbefoundinsidetheScripts­>MiscfolderintheProjectPane.Itisnamed,FenceTextureOffsetandlookslikethis:

var scrollSpeed = 0.25;

function FixedUpdate() { var offset = Time.time * scrollSpeed; renderer.material.mainTextureOffset = Vector2 (offset,offset);}

ThefirstlineexposesapropertywecaneditdirectlyintheUnityinterface,ScrollSpeed.TheFixedUpdate()functioniscalledasetnumberoftimespersecondby

Unity.Weuseashortformula­­multiplytheScrollSpeedvaluebythecurrenttime­­todefineatextureoffset.

PrettyPropertiesWhenUnity’sInspectordisplayspropertiesandvariables,theirnamesareadjustedtomakethemlooknicer.Usually,thisjustmeanslookingforthecapitallettersinthenameandinsertingaspacebeforeeachone.Inaddition,Unitycapitalizesthefirstlet­terofthename.ThusscrollSpeedisdisplayedasScrollSpeed.

Whenatextureisrendered,thetextureitselfisusuallyjustanimage.ThemainTex­tureOffsetpropertyofamaterialtellsUnitythatitistodrawthetexture’simageoff­setwithinitsUVspace.Thiscanbeusedtoproducesomeveryeffectiveresultswith­outresortingtocomplexanimationsequences.

ExpandthelevelGeometryGameObjecttorevealthedifferentelementsoftheleveldata.WeneedtoanimatethefenceontheimpoundFenceobject,sodropthefence­TextureOffsetscriptontothis.

ScriptingtheCollectableItemsAtthemomentLerpzdoesnotpickupanyoftheitemsonthelevel.ThisisbecauseUnityhasnotbeentoldtoletourherodothis.Weneedtoaddtwoelementstoeachcollectableitem:

45

Page 46: 3D Platform Tutorial

• AColliderComponent,

• ScriptcodetohandletheColliderandupdateplayerhealth,etc.

ThecollectibleitemsintheHierarchyPaneareallPrefabinstances,whicharedis­playedinblue.ByeditingtheoriginalPrefabsdirectly,wewillautomaticallyupdatealltheitemsinthegame.

ThetwoprefabsourherocancollectareFuelCellPrefabandHealthPickUpPrefab.ThesecanbefoundinsidethePropsfolderintheProjectPane.

SelecttherootHealthPickUpPrefabobject.

UseComponent­>Physics­>AddSphereCollidertoaddaspherecollidertothePrefab.YoushouldseeitappearintheInspector.

Finally,SettheIsTriggercheckbox.

TheHealthPickUpPrefabintheInspector.

NOTE AnObjectRotaterscriptisalreadyattachedtothePrefab.Thisjustmakesthepickupspinonthespotandisverysimple.We’llseeamorecomplexexampleofscriptedanimationinalaterchapter.

46

Page 47: 3D Platform Tutorial

Collidershavetwouses:wecanhitthemwithsomethingelse,orwecanusethemasTriggers.

TriggersTriggersareinvisibleComponentswhich,astheirnameimplies,triggeranevent.InUnity,aTriggerissimplyaColliderwithitsIsTriggerpropertyset.ThismeanswhensomethingcollideswiththeTrigger,itwillactlikeavirtualswitchinsteadofaphysicalentity.

Triggerswillsendoneofthreeeventmessageswhensomethingsetsthemoff:On-

TriggerEnter(),OnTriggerStay()andOnTriggerExit().

Triggereventmessagesaresenttoanyscriptattachedtothetriggerobject,sonowweneedtoaddasuitablescripttoourhealthprefab:

GototheComponentsmenuandchoosethePickupscriptfromtheThirdPersonPropssub­menu.ThiswilladdthePickupscripttoourPrefab.

SetthePickupTypepropertyintheInspectortoHealthasshownintheimageabove.

Finally,settheAmountpropertyto3orso.Thisistheamountofhealththepickupbestowsontheplayer.

Howmuchhealth?Theheads­updisplay,or‘HUD’,whichshowstheplayer’scurrenthealthlevel,lives,etc.,canonlyhandleamaximumhealthlevelofsix.Whathappensiftheplayercol­lectsahealthpickupwhenhealreadyhasafullhealthbar?Thisisamatteroftaste,butI’vechosentomakethistriggertheadditionofanextralife.Thelogicforthiscanbefoundintheplayer’sstatecheckingscript,ThirdPersonStatus.

Thefuelcellpickupsaresetinmuchthesameway,withtheonlytwodifferences:

• ThePickupTypesettingshouldbeFuelCell,

• TheAmountvalue,whichistheamountoffuelthepickuprepresents.(1seemsbest.)

47

Page 48: 3D Platform Tutorial

JumpPads

AJumpPad.

TheJumpPadsarethebrightyellowandblackstripedspacesinourlevel.ThesearesupposedtoboostLerpzintotheair.Weshalluseacolliderwithanattachedscriptforthispurpose.

First,createanemptyGameObjectandcallitJumpPadTriggers.We’llusethislikeafoldertokeepourJumpPadtriggerobjectstogether.

Nowwe’llbuildourPrefab:

CreateanewemptyGameObject.

RenamethisobjecttoJumpPadTrigger1.

AddaBoxColliderobjecttoit.(ASphereColliderwouldworkintheory,buttheBoxColliderisabetterfitgiventhejumppad’sshape.)

SettheColliderasaTrigger.

AddtheJumpPadscript.

That’stheobjectcreated.NowweneedtoturnitintoaPrefab:

ChoosePrefabfromtheCreatemenuabovetheProjectpane.

48

Page 49: 3D Platform Tutorial

DraganddropourJumpPadgameobjectontothenewPrefab.

RenamethePrefabtoJumpPadTrigger.

DeletetheoriginalGameObjectfromourHierarchypane.

DragaJumpPadPrefabintothesceneandpositionitdirectlyinsideoneoftheJumpPadlocations.(Therearesixtoplace.Irecommendusingthe4Splitviewlayouttohelpwithpositioning.)

ThedefaultjumppadJumpHeightsettingof5isn’tenoughtothrowLerpzrightuptothegardenlevel.Isuggestusingavalueofaround15­30forthesepads.(Thefinishedprojectexampleusesavalueof30.)

Finally,hitPlayandtestthegametomakesureallournewtriggersworkcor­rectly.

NOTE ScriptsworksimilarlytoPrefabs:we’vejustaddedalinktothePickupscriptinthePropsfolder­­toourPrefab.EditingtheoneinourProjectpanewillalsoaffectanycopiesintheScene.

Goodorganizationisimportantifyouwantyourworkflowtobesmoothandhasslefree.

• UseinstantiatedPrefabswhereverpossible.

• Tryorganizingbyfunctioninsteadoftype.

• UseEmptyGameObjectsascontainers.

Youwillbesurprisedathowmanyassetsareneededforevenasmall­scaleproject.

49

Page 50: 3D Platform Tutorial

TheGUI

TheUserInterfaceGamesusuallyhaveGraphicalUserInterfaces(GUIs),suchasmenus,optionsscreensandsoon.Furthermore,gamesoftenhaveaGUIoverlaidontopofthegameitself.Thiscouldbeassimpleasascoredisplayedinacorner,oramoreelaboratedesigninvolvingicons,inventorydisplaysandhealthstatusbars.

Unity2introducesanewGUIsystemtomakeiteasytobuildsuchGUIsforgamesandthisisthesystemweshalluseforLerpzEscapes.

NOTE TheoldsystemwillremainfortheUnity2.xreleases,butwillbedeprecatedinafutureversionofUnity.Itisnotcoveredinthistutorial.

Unity2'snewGUIsystemPreviously,youwouldtellUnitytodrawabuttonandUnitywouldfireoffrelevantmessagestoyourscriptwhentheuserhoveredoverthebutton,clickedthebuttononit,releasedthebutton,andsoon.

TheoldsystemwasbasedonthetraditionalEvent­DrivenGUImodel,butUnity2in­troducesabrandnewGUIsystem,knownasanImmediateModeGUI.IfyouareusedtotraditionalGUIsystems,theImmediateModeGUIconceptmaycomeasashock.

Here'sanexample:

Howmanylivesdowehavere­maining?WhatisLerpz’shealthlevel?It’stimeforaGUI.

Page 51: 3D Platform Tutorial

function OnGUI(){ If (GUI.Button (Rect(50, 50, 100, 20), "Start Game") ) Application.LoadLevel("FirstLevel"); // load the level.}

OnGUI()iscalledatleasttwiceeverygamecycle.Inthefirstcall,UnitybuildstheGUI

anddrawsit.Inthiscase,wegetasimplebuttondrawnatthecoordinatesspecified,with"StartGame"displayedwithin.

Thesecondcalliswhenuserinputisprocessed.Iftheuserclicksonthebutton,theIf(...)conditionalsurroundingthebutton­drawingfunctionreturnstrue,so

Application.LoadLevel()willbecalled.

OtherGUIelements­­Labels,Groups,Check­boxes,etc.­­allworksimilarly,withthefunctionsreturningtrue/false,oruserinputasappropriate.

Theobviousadvantagehereisthatyoudon'tneedumpteeneventhandlersforaGUI.It'sallcontainedintheoneOnGUI()function.

Unity2providestwosetsofImmediateModeGUIfunctions:thebasicGUIclassasusedintheexampleabove,andasimilarGUILayoutclass,whichhandlesthelayoutofGUIelementsforyoutosavetime.

FurtherInformationMoreinformationonthenewUnityGUIsystemcanbefoundbyfollowingtheselinks:

• http://unity3d.com/support/documentation/Components/GUI%20Scripting%20Guide.html

• http://unity3d.com/support/documentation/ScriptReference/GUI.html

TheIn­gameHUDOurgameneedsanin­gameGUItodisplaytheplayer’shealth,livesremainingandthenumberoffuelcellsheneedstocollect.Thegraphicalelementsarealreadyincludedinourprojectfile.

TheGUIishandledwithintheGameHUDscript,whichusesthenewGUIcomponenttolayoutthevariouselements.ThisscriptneedstobeattachedtotheLevelGameOb­ject,whichisusedtoholdScene­specificelements.(WecouldjustaseasilyhaveaddedittotheNearCameraobjectortoitsown'GUI'GameObject;thisismainlyamatterofpersonaltasteratherthanakeygamedesigndecision.)

WewillusetheLevelGameObjecttomanagelevel­specificstatesandotherscripts.

51

Page 52: 3D Platform Tutorial

TheGUISkinobjectUnity2’snewGUIsystemincludessupportforskinning.ThisgivesyoufullcontroloverthelookandfeelofeveryGUIelement.BuildingyourownGUIskincontentletsyouchangetheshapeofabutton,itsimagery,itsfont,itscolorsanddothesametoeveryotherGUIelement,fromtextinputboxesthroughtoscrollbarsandevenwholewin­dows.

Asourin­gameGUIwillbebasedentirelyaroundgraphicalimages,wewillbuildthegame’sHUDentirelyusingtheGUI.Label()function.However,wedoneedtouse

acustomfontforourHUD,inordertodisplaytheremainingfuelcansandlives.

TheGUISkinassetdefinesthe‘look’ofaUnityGUI,muchasaCSSfiledefinesthelookofawebsite.Theobjectisrequiredifyouneedtochangeanydefaultfeatures.Aswearechangingthefont,weneedtoincludeaGUISkininourScene.

UsetheAssetsmenucommandtocreateanewGUISkinobject.ThiswillappearintheProjectPaneandcontainsthedefaultUnityGUIskindata.

RenamethenewGUISkinobjecttoLerpzTutorialSkin.

Wearegoingtouseadecorativefont,named“Flouride”,forourgame.Thisistheonlychangewearemakingtothedefaultskin.

DragtheFlouridefontobjectontoournewGUISkinasset’s“Font”entry:

GUISkin,settingthefont.

TheGUISkinobjectisnotaddedtotheHierarchyPaneview;insteadwereferencetheGUISkindirectlyinourGameHUDscript.AlongwiththeGUISkin,theGameHUDscriptalsoneedstobetoldwhichassetstousetobuildtheGUIdisplay.TheseincludetheGUIHealthRingasset.

52

Page 53: 3D Platform Tutorial

TheGUIHealthRingimage

NOTE YoumayhavenoticedanumberofwarningsfromUnityaboutimagesnotbeinga“poweroftwo”insize.Manygraphicscardspreferimagestobesizedinpowersoftwo,regardlessofhowmuchoftheactualimagedataisused,asthismakestheunderlyingcalculationsquickertoperform.GUIsrarelyneedthislevelofoptimizationandtheassetsweareusingarethereforenotopti­mized.Ifyouwanttofindoutwhetheranimage’sdimensionsarepowersoftwo,simplybringtheimageupintheInspectorwhichwillletyouknowifitneedssuchoptimizing.

ThisimageisusedtodisplayLerpz'shealthinformation.ThespacetotherightofLerpz'simagedisplayshisremaininglives,whilethecircletotheleftisusedtoshowapiechartofhisremaininghealth.Thepiechartiscreatedbysimplysuper­imposingthecorrectimagefromanarrayofsix2Dtextures,namedhealthPie1throughhealth­Pie6.ThehealthPie5imageisshownbelow:

ThehealthPie5image

NOTE Theseimagesincludealphachannelstodefinetransparencyandtranslucency.

UsingseparateimagesletsussimplydrawtheimagecorrespondingtoLerpz'sactualhealth,insteadofperformingfancycalculationstorotateanddrawsegmentspro­grammatically.

53

Page 54: 3D Platform Tutorial

ThesecondmajorGUIelementisfortheFuelCellstate,themainimageforwhichisGUIFuelCell:

TheGUIFuelCellimage

Thisisdisplayedinthelower­rightofthegamescreenandwillshowthefuelcellsremainingtobecollectedbeforethelevelisunlocked.

AddtheGameHUDscripttotheLevelGameObject.

ClicktheLevelGameObjecttoselectitandlookattheInspector.YoushouldseetheGameHUD(Script)componententry.

AddtheGUIHealthRingandGUIFuelCellimagestotheGameHUDscript.

OpenuptheHealthPieImagesentry.

HealthPieImagesisanarray.Atthemoment,Unitydoesn'tknowhowbigitshouldbe,soithassetittozero.Wehavesixhealthpieimagestodropintothisarray,soweneedtochangethisvalue.

Clickonthe"0"nexttoSize.Changeitto"6".Youshouldnowseesixemptyelements,namedElement0throughElement5.

OpentheGUIassetsfolderintheProjectPanetorevealthehealthpieimages.Therearesixofthese,numbered1to6.

Computerscountfromzero,sohealthPie1needstogointoElement0.health­Pie2needstogointoElement1...andsoon.Dragtheimagesintotheirrelevantslots.

FinallyaddtheLerpzTutorialSkinGUISkinintotheemptyGuiSkinslot.Theset­tingsshownowlooklikethis:

54

Page 55: 3D Platform Tutorial

GameHUDscriptsettings.

Ifyourunthegamenow,youshouldseetheHUDappearingovertheplayarea:

Thein­gameHUD

ResolutionIndependence.

OneproblemwiththeGUIisitssize.Thescreenshotaboveisfroma24"iMacrunningataresolutionof1920x1200.

ClearlyweneedtoscaleourHUDdynamicallyaccordingtothecurrentdisplaysizeandresolution,sohowdoweachievethis?

Unity2'snewGUIsystemincludessupportforatransformmatrix.Thismatrixisap­pliedtoallGUIelementspriortorendering,sotheycantransformed,rotatedorscaled­­inanycombination­­dynamically.

55

GUI.matrix = Matrix4x4.TRS (Vector3.zero, Quaternion.identity, Vector3 (Screen.width / 1920.0, Screen.height / 1200.0, 1));

Page 56: 3D Platform Tutorial

Thelinebelow,fromtheGameHUDscript,showshow:

IfyougototheGameView,disablethe"MazimizeonPlay"optionandsettheaspectrationto4:3,youwillseethattheGUIre­scalestofit.

Ifwewished,wecouldhaveourHUDspinaround,flipupsidedownorzoominfromadistance.Forgamemenus,highscorescreensandthelike,thisisausefulfeaturetohaveandwe'llusethistrickforourlevel­completesequence.

TheStartMenuEverygameneedsastartmenu.Thisisdisplayedwhenthegamestartsandletstheplayerchangeoptions,loadasavedgameand,mostimportantly,startplayingthegame.Inthissection,wewillbuildastartmenufromscratch.

NOTE Splashscreens,menusandthelikearealljustUnityScenes.ThusagamelevelisusuallyaScene,butaSceneisnotalwaysagamelevel.WeusescriptsinoneScenetoloadandrunotherScenestolinkScenestogether.

FortheStartMenu,wewillneed:

• TwoGUItextbuttons:"Play"and"Quit".

• Thenameofthegame.Thiswillberenderedusingacustomfont.

• Somesuitablemusic.

• Abackdropofsomesort.

Inotherwords,somethinglikethis:

56

Page 57: 3D Platform Tutorial

TheStartMenu.

SettingtheSceneThefirststepistocreateanew,emptyScene.

TypeCMD+NonMacorCtrl+NonPC,tocreateone,thenCMD+SorCtrl+Stosaveit.

NameitStartMenu.UnitywillautomaticallyaddaCameratotheSceneforus,butthereisnothingforittoseeatthemoment.

Nowwe'llusethenewGUIsystemtobuildamenu:

GototheProjectPaneandcreateablankJavaScriptfile.

RenameitStartMenuGUIandopenitintheeditor.

First,we’lladdaUnityscriptdirective.DirectivesarecommandswhichgiveUnityin­formationoradditionalinstructionsaboutthescript.Thesecommandsaren’tpartofJavascriptassuch,butaimedatUnityitself.Youcanfindthecompleteassembledscriptcodelistedintheappendixsection.

Inthiscase,wewantUnitytorunourscriptinsidetheEditor,sothatwecanseetheresultsimmediatelywithouthavingtostopandre­runtheprojecteachtime:

// Make the script also execute in edit mode@script ExecuteInEditMode()

57

Page 58: 3D Platform Tutorial

WeneedalinktotheLerpzTutorialSkinasset,sothefirstlineofcodewillbethis:

var gSkin : GUISkin;

We’llneedaTexture2Dobjectforthebackdrop.(We’lldropourbackgroundimage

ontothisintheInspector.)

var backdrop : Texture2D; // our backdrop image goes in here.

Wealsowanttodisplaya"Loading..."messagewhentheplayerclicksonthe"Play"button,sowe'llneedaflagtohandlethis:

private var isLoading = false; // if true, we'll display the "Loading..." message.

Finally,wegettotheOnGUIfunctionitself:

function OnGUI(){ if (gSkin) GUI.skin = gSkin; else Debug.Log("StartMenuGUI: GUI Skin object missing!");

ThecodeabovechecksifwehavealinktoavalidGUISkinobject.TheDebug.Log()

functionspitsoutanerrormessageifnot.(It'sagoodhabittosanity­checkanyexter­nallinksordatainthiswayasitmakesdebuggingmucheasier.)

TheBackdrop.ThebackdropimageisaGUI.Labelelementsettouseourbackgroundimageastheelement’sbackground.Ithasnotextandisalwayssettothesizeofourdisplay,soitfillsthescreen.

var backgroundStyle : GUIStyle = new GUIStyle(); backgroundStyle.normal.background = backdrop; GUI.Label ( Rect( (Screen.width - (Screen.height * 2)) * 0.75, 0, Screen.height * 2, Screen.height), "", backgroundStyle);

58

Page 59: 3D Platform Tutorial

First,wedefineanewGUIStyleobject,whichwe’llusetooverridethedefaultGUISkinstyle.Inthisinstance,we’rejustchangingthe“normal.background”styleele­menttouseourbackdropimage.

TheGUI.Label()functiontakesaRectobject.Thisrectangle’sdimensionsarede­

rivedfromthedisplay’sdimensions,sothattheimagealwaysfillsthescreen.Theim­age’saspectratioisalsotakenintoaccount,sothattheimageiscroppedand/orres­caledtofitwithoutaddingdistortion.

Nextwecometothetitletext.Beforewewritethescriptcodeforthis,weneedtotakeanotherlookatourGUISkinandmakeasmallmodificationtoit:

OurmenuusesthedefaultfontdefinedintheLerpzTutorialSkinasset.Atthemo­ment,thedefaultstylefordisplayingtextisunsuitableforalargegametitle,sowewilladdanewcustomGUIStyletotheskin,namedmainMenuTitle:

GototheProjectPaneandclickonLerpzTutorialSkintobringupitsdetailsintheInspector.

WewillnowaddaCustomStyletoourGUISkin:

DefiningacustomstyleinourGUISkinobject.

Openup“CustomStyles”andensure“Size”issetto“1”.Youshouldseean“Element0”entry.

59

Page 60: 3D Platform Tutorial

Open“Element0”andsetitselementsasshownabove.Specifically:settheTextColortotheorange­brownshown.(Don’tworryabouttheelementsnotshowninthescreenshot:theycanbeleftastheyare.)

Finally,wecannowaddtheremainingcodetoourscript:

GUI.Label ( Rect( (Screen.width/2)-197, 50, 400, 100), "Lerpz Escapes", "mainMenuTitle");

NOTE GUIStylenamesarecase­insensitivewhenyouattempttoaccessthemthroughscripting.Typing“mainMenuStyle”isthesameas“mainmenustyle”.

TheButtons.WenowneedtomodifytheGUIButtonpropertiesoftheLerpzTutorialSkinobjecttoproduceamoreinterestingbutton.

We'reusingthesameGUISkinobjectforthismenuandforthein­gameHeads­UpDisplay,or'HUD'.FortheHUD,onlythedefaultfontneedstobechanged.However,fortheStartMenubuttons,wealsoneedtohaveagraphicalimagebehindthebut­tontext.Thedefaultbuttondesigndoesn'tfitthegame'svisualstyle,soweneedtochangeit.

TIP IfyouwantyourGUIelementstoreacttoHover,FocusandActiveevents,youmustsetabackgroundimagetoo,eveniftheimageisblank.

ClickontheLerpzTutorialSkinassetintheProjectPanetobringupitsdetailsintheInspectorPane.Changeittothesettingsshownbelow­­ignoretheotherGUIelementtypes;wewon'tbeusingthem:

60

Page 61: 3D Platform Tutorial

SettingthebuttonimagesintheLerpzTutorialSkinGUISkinobject.

Nowlet'saddthe“Play”button:

if (GUI.Button( Rect( (Screen.width/2)-70, Screen.height - 160, 140, 70), "Play")) { isLoading = true; Application.LoadLevel("TheGame"); // load the game level. }

Theaboveisallittakestorenderandhandlethe"Play"button.Thecodeforboththerenderingandtheeventhandlingisinthesameplace,makingiteasiertomaintain.TheGUI.Button()functiontakesaRectobjecttodefinethebutton'spositionand

size,followedbythetextlabel.

Iftheuserclicksonthisbuttonthebuttonfunctionreturnstrue,sowecanloadthegamelevel.

WesetisLoadingsothatweknowtoshowthe"Loading..."text,thentellUnitytoloadthegamelevel.

61

Page 62: 3D Platform Tutorial

TIP ThenewGUIsystemsupportsanumberofalternativefunctionsfordrawingelements,allowingyoutospecifyanimageinsteadoftext,orevenanimage,alabelandatooltipcombined.Seethedocumentationformoredetails.

The"Quit"buttonishandledsimilarly,butwithatesttoensurethisisrunningasastandalonebuild(orintheUnityeditor)added.

var isWebPlayer = (Application.platform == RuntimePlatform.OSXWebPlayer || Application.platform == RuntimePlatform.WindowsWebPlayer); if (!isWebPlayer) { if (GUI.Button( Rect( (Screen.width/2)-70, Screen.height - 80, 140, 70), "Quit")) Application.Quit(); }

Asyoucansee,thisisverysimilartothe"Play"button.Wejustdrawitalittlelowerdownandcheckifweneedtodrawitatallfirst.

Thefinalstepisthe"Loading..."text,whichneedstobedisplayedwhentheuserse­lectsthe"Play"button.ThisisbecauseitcantakeafewmomentsforourgameScenetoload­­especiallyifit'sbeingstreamedovertheInternetinsidethewebplayer.

ThisiswhereisLoadingcomesin:

if (isLoading) GUI.Label ( Rect( (Screen.width/2)-110, (Screen.height / 2) - 60, 400, 70), "Loading...", "mainMenuTitle"); }

Again,wemakeuseofthemainMenuTitleCustomGUIstylesothatthetextstylematchesthatofthetitle.(GUIStylesarecase­insensitive,sotheupper­caselettersarepurelyforreadability.)

TheQuitButton.The"Quit"buttonwillonlyworkifyou'rerunningthegameasastandaloneapp.Ifit'sbeingplayedinaweb­player,DashboardwidgetorinsidetheUnityEditoritself,the"Quit"buttonwillnotdoanything.TheUnityweb­playercannotquitasit'sem­beddedinawebpage.Norisitagoodideaforittoclosethebrowser.(Youcouldar­guethatclosingthepagetheweb­playerisrunningonmightbeanoption,butthisisbesthandledbythewebpageitself.)Similarly,whatcoulda“Quit”optiondoinaDashboardwidget?ClosingthewidgetwoulddeleteitfromtheDashboard.

62

Page 63: 3D Platform Tutorial

So,weneedtodisabletheQuitbuttonandstopitdisplayingifthegameisn'trunningasastandalone.TheoneexceptionisifthegameisbeingruninsidetheUnityEditoritself.Thisisbecausewewanttoknowifthebuttonisbeingdisplayedintherightplaceandbehavingitself,whichisdifficultifyoucan'tseeit.

Thenextstepistoaddsomemusic.

GototheProjectPane,openuptheSoundsfolderanddragtheStartMenuaudiofileontotheMainCameraobjectintheHierarchyPane.ThiswilladdanAudioClipcomponent.

ClickontheMainCameraobjectnowand,intheInspector,locatethenewAudioClipcomponent.EnablethePlayOnAwakeandLoopcheckboxes.

AddtheStartMenuGUIscripttotheMainCamera.

Finally,insidetheStartMenuGUIcomponent,setGSkintoLerpzTutorialSkinandBackdroptoStartSplashScreen.

Ifyounowplaythescene,youshouldseesomethinglikethisintheGameView,ac­companiedbyashort,loopingorchestraltune:

TheStartMenuinaction.

Sothat’sit!OurStartMenuisdone.

TIP ThemusicwascreatedusingAppleLoopsfromApple’sOrchestralJamPack,arrangedinGarageband.ThisisahandytoolforMacuserstobuildplace­holdertunes;youcanusethesetodecidewhichmusicalstylebestfitsyourgame.ForPCuserswesuggestusingAudacity.

Foryourownprojects,youwillmostlikelywanttoaddanOptionsMenu,perhapsahighscoremenu,amultiplayerlobby,etc.ThesecanallbebuiltusingtheUnity2GUI.

63

Page 64: 3D Platform Tutorial

GameOverTheGameOverscreenisshownwhentheplayerhaseithercompletedthegameorhasfailedthechallenge.UnliketheStartMenu,thisScenehasnobuttonsorothervisualuserinteraction:itjustshowsa“GameOver”messageoverabackdropwhileplayingashortjingle.Oncethejingleiscompleted,oriftheuserclicksthemouse,weautomaticallyloadthe“StartMenu”Scene.

Firstly,createanewSceneandnameit“GameOver”

DraganddroptheGameOverJingleaudiofileontothedefaultMainCameraobjectandsetitasshown:

TheGameOverJinglesettings.

Wedon’tneedtoaddanythingelsetotheSceneusingtheEditor:thedefaultcameraalonewillsuffice.

Thenextstepistobuildourscript(Youcanfindthecompleteassembledscriptcodelistedintheappendixsection):

CreateanewJavascriptscriptassetandnameit“GameOverGUI”

Openitintheeditorandaddthecodedescribedbelow:

AswiththeStartMenu,wewanttobeabletoseeourGUIintheUnityeditorevenwhentheprojectisn’trunning,soaddthis:

@script ExecuteInEditMode()

FortheStartMenu,weusedtheLerpzTutorialSkinGUISkinasset.TheGUISkindefines abunchofGUIStylesandletsusapplythemtoaGUIwholesale.

64

Page 65: 3D Platform Tutorial

AnalternativetechniqueistodefineindividualGUIStyleobjectsdirectly.WewilldosofortheGameOverscriptbydefiningthreeGUIStylevariableswhichwecanthensetintheInspector,alongwithtwovariablesdefiningthescalingofthetextelements:

var background : GUIStyle;var gameOverText : GUIStyle;var gameOverShadow : GUIStyle;

We’llsettheseGUIStyleobjectsintheInspectorshortly.

Next,weneedtodefinethetextscalingfactorforour“GameOver”messageasitwillberenderedlargerthanthedefaultfontsize.

We’regoingtodrawthismessagetwice,intwodifferentcolors,togiveashadowedtexteffect,sowe’lldefinetwoscalevariables:

var gameOverScale = 1.5;var gameOverShadowScale = 1.5;

AtlastwegettotheOnGUI()function:

function OnGUI(){

First,thebackdrop,rescaledsimilarlytothatusedintheStartMenu:

GUI.Label ( Rect( (Screen.width - (Screen.height * 2)) * 0.75, 0, Screen.height * 2, Screen.height), "", background);

Thenexttaskistodrawtheshadowedversionofthe“GameOver”message.Weneedtoscalethetextupandrenderitcenteredonthescreen.Luckily,wecanusetheGUIsystem’sbuilt­intransformmatrixtohandlethescalingforus.

TIP TheGUItransformmatrixcanalsobeusedtoperformanyarbitrarytransla­tionsyouwish:youcanscale,rotate,flipandspintoyourheart’scontent.

Toensurethetextappearsinadark,shadowcolor,wepassthegameOverShadowGUIStyletotheGUI.Labelfunction.

65

Page 66: 3D Platform Tutorial

GUI.matrix = Matrix4x4.TRS(Vector3(0, 0, 0), Quaternion.identity, Vector3.one * gameOverShadowScale); GUI.Label ( Rect( (Screen.width / (2 * gameOverShadowScale)) - 150, (Screen.height / (2 * gameOverShadowScale)) - 40, 300, 100), "Game Over", gameOverShadow);

Finally,wedrawthesametextagain,butinalightercolor.Unity’sGUIsystemwillal­waysrendertheseelementsintheordertheyappearinthecode,sothistextwillap­pearontopoftheshadow.AsidefromusingthegameOverScalescalingfactorandthegameOverTextGUIStyle,there’snootherdifference.

GUI.matrix = Matrix4x4.TRS(Vector3(0, 0, 0), Quaternion.identity, Vector3.one * gameOverScale); GUI.Label ( Rect( (Screen.width / (2 * gameOverScale)) - 150, (Screen.height / (2 * gameOverScale)) - 40, 300, 100), "Game Over", gameOverText);}

SavethescriptanddropontotheMainCamera.

ClicktheMainCameraobjecttobringitupintheInspector.It’stimetosetthevariables...

First,theBackgroundGUIStyle.Thisjustneedsthe“GameOverSplashScreen”imagedroppedintotheNormal­>Backgroundslot,asshownbelow:

ThepertinentBackgroundGUIStylesettings.

Next,we’llsettheGameOverTextGUIStyleasshowninthenextimage.(Asusual,leaveanyothersettingsastheyare.)

66

Page 67: 3D Platform Tutorial

ThepertinentGameOverTextGUIStylesettings.

Next,settheGameOverScaleto1.69.

NowfortheGameOverShadowGUIsettings:

ThepertinentGameOverShadowGUIStylesettings.

Finally,settheGameOverShadowScaleto1.61.

67

Page 68: 3D Platform Tutorial

YoushouldnowseetheGUIappearintheGameView,asshownbelow:

TheGameOverscreen.

ThefinaltouchistoaddasecondscripttotheMainCamerawhichchecksifthemusichasfinishedplayingand,ifso,loadsuptheStartMenuScene.

CreateanewJavascriptScriptasset.NameitGameOverScript.

Openthescriptinyoureditorandaddthefollowingcode(Youcanfindthecompleteassembledscriptcodelistedintheappendixsection):

function LateUpdate () { if (!audio.isPlaying || Input.anyKeyDown) Application.LoadLevel("StartMenu");}

Thiscodechecksiftheaudiohasfinishedplaying,orwhethertheplayerhaspressedakeybeforeloadingthe“StartMenu”Scene.

AddGameOverScripttotheMainCameraandthat’sit:GUIsdone!

68

Page 69: 3D Platform Tutorial

Adversaries

Antagonists&ConflictThesetwoelementsarekeytoanygame,soweneedsomethingtokeepLerpzonhistoes.Thejobofthegamedesigneristothrowobstaclesintothepathoftheplayer,butmakethemsurmountable.

Lerpzfacestwoadversaries:robotguardsandlaserbarriers.

TheLaserTrapsTheLaserTrapsarelocatedinthelaserpassagesandwillharmourplayerifheshouldtouchthebeam.Theimagebelowshowstwoofthese.Wewilllocatethemintheshortpassagewaystructuresoneithersideofthearena,atthefarendofthelevelfromthejail.

Nogameiscompletewithoutad­versaries.Inthischapter,weaddenemiesforLerpztofight.

Page 70: 3D Platform Tutorial

LerpzfacestwoLaserTraps.

Thelasersriseandfall.Iftheplayertriestopassthroughoneofthese,hewilllosesomehealth.

ImplementingtheLaserTrapsEachlasertrapisjustabeamwhichrisesandfallsinasameverticalplane.IfLerpz(oranenemy)shouldhappentohitthebeam,it’llcausedamage.

ThelaserbeamitselfisproducedbyaLineRenderercomponentcontainedinitsownGameObject.ItsmovementandthelogicwhichcontrolsitisentirelycontainedintheLaserTrapscript.Solet’sbuildourfirstLaserTrap:

CreateanemptyGameObject.

Renameitto“Laser”

AddaLineRendererComponent(Component­>Miscellaneous­>LineRenderer).

NOTE Don’ttrypositioningthelaseryet!Youwillneedtodisablethe“UseWorldSpace”checkboxfirst;we’llcomethatshortly.

AddtheLaserTrapscript.

70

Page 71: 3D Platform Tutorial

AdjusttheLineRendererComponentsettingsasshown.(Thelasermaterialcanbefoundin:Particles­>Sources­>Materials.)

LineRenderersettings.

Positiontheresultingobjectinthelasertunnels.(Thesearetheroofedcorridorstructuresoneithersideofthearena,atthefarendofthelevelfromtheJail.)

AddaPointLightGameObjectasachildtoourLaserobject.

SetthePointLightasshown:

LaserTrapPointLightsettings.

71

Page 72: 3D Platform Tutorial

ThePointLightactsasthelaser’slightsourceandwillriseandfallwiththelaseritself.Thisgivestheillusionthatthelaserbeam,drawnbytheLineRenderer,isemittinglight.

TheLineRendererComponentTheLineRenderer,asitsnamesuggests,drawslinesin3DspacewithinourScene.Itcontainsanarraywhichdefinestheseriesofpointsthroughwhichthelinewillbedrawn.ThelineitselfisdrawnusingthesamerenderingtechniqueastheTrailRen­derercomponent,makingitidealforlasers,lightningandsoon.

Next,settheLaserTrapscript’sproperties,whichshouldbeasshown:

TheLaserTrapscript’sproperties.

TIP TheLaserHitassetcanbefoundinsidethePropsfolderintheProjectPane.

Finally,duplicate(CMD+DonMac,orCtrl+D),(orcreateaPrefab)ofourLaserTrapandplacesomeinthelevelasyouseefit.Isuggestplacingfourintotal,withtwoineachofthetunnels.

FeelfreetovarytheLaserTrapscript’spropertiesandexperimentuntilyougetaresultyouaresatisfiedwith.

72

Page 73: 3D Platform Tutorial

TheLaserTrapScriptThekeytotheLaserTrapistheLaserTrapscript,solet’stakeacloserlook...

OverviewTheLaserTrapisaLineRenderercomponentwhichismovedupanddownbythescript.Thissamescriptalsochecksforcollisionsandtriggerstherequisitevisualeffectsshouldoneoccur.

First,wedefinesomebasicpropertiesforourlasertrap:

• heightdefinestheamplitudeoftheoscillation,dictatinghowfaraboveandbelowitsstartingpointthelaserbeamwilltravel;

• speeddefineshowfastthebeammoves;

• timingOffsetallowsustoseteachlasertrapobjecttostartatadifferentpointinitsoscillationcycle;

• laserWidthdefinesthewidthofthelaserbeamfromendtoend;

• damagedefineshowmuchdamagetheplayerwillsufferifherunsintothebeam.

• hitEffect,canbeusedtolinktoanarbitraryGameObject,whichwillbeinstantiatedwhensomethinghitsthelasertrap.Thisismoreflexiblethanhard­codingtheeffectintheLaserTrapobjectandmakesiteasiertore­usethisassetinotherprojects.

Thescriptdefinestwofunctions,solet’slookattheseinturn:

TheStart()functioninitializestheLineRenderercomponent,storingitsinitialloca­tion(intheYaxis)forlater,andsettingtheLineRenderer’ssecondvertextomatchthepositiondefinedbylaserWidth.Thisletsuseasilytweakthelengthofthebeamtomatchthewidthofthecorridor.

NOTE WecouldjustsettheendcoordinateofeachlinebyhandintheLineRen­derer’sarray,buthandlingthisinthescriptgivesusabitmoreflexibilityshouldwedecidetomakethelaser’sbeamanimatein­game.

NowwecometothemainUpdate()function.Thisiswheretheinterestingstuff

happens.

First,wehavethelaserbeammovementtocalculate.WejustusetheMathf.Sin()

functionforthis.WegrabthecurrenttimeusingTime.time,(whichreturnsthe

time,inseconds,sincethegamestarted),multiplyitbyourdesiredanimationspeedandaddinthetimingOffsetvalue.Thisgivesusourpositionalongthesinecurve.Fi­nally,wemodulateitbyourdesiredheightandusetheresultasanoffsetfromourbaseline.

73

Page 74: 3D Platform Tutorial

Thenextstepistocheckforcollisions.ThescriptdoesthisbycastingarayalongthepathofthebeamandcheckingifithitsanyGameObjectwhichhaveColliderCompo­nentsattachedtothem.

(Thetime­basedtestistheretoallowtimeforareaction.Ifwedidn’tdothis,theplayerwouldloseahealthpointforeveryframeinwhichhe’shitbytheray­cast.)

Ifwedohitsomething,wecheckifit’saplayerorenemyand,ifso,sendthe“Apply­Damage”messagetoit.Atthesametime,wealsoinstantiatethehitEffectGameOb­ject,sothatitcanperformitsmagic.ThisistheLaserHitGameObject,whichproducesanenergybursteffect:

Beinghitbyalaserisbadforyourhealth.

Ifyouplaythetutorial,youshouldfindthatLerpzlosesahealthpointifhehitsthelaserbeam.

74

Page 75: 3D Platform Tutorial

TheRobotGuardsThemobileantagonistsarerobotguards,placedstrategicallyaroundthelevel.WhenLerpzgetswithinrange,theseguardswillhomeinonhimandattempttocausehimharm.

Arobotguard.

Lerpz'senemiesinthisgamearerobotguards.Thisparticularmodelisnowconsidereda'classic',worthyofpreservation.Collectorsandenthusiastscomparethem,unfa­vorably,withsuchotherclassicgemsofyesteryearastheFordEdsel,AppleLisaandSinclairC5.

Thisparticularmodeliswell­knownforthewayitwascunninglydesignedtobesus­ceptibletoacoupleofwell­aimedpunchesatthetorso.Whensoincapacitated,therobotsejectanyitemstheyarecarrying,afterwhichtherobotwillhappilylieonthegrounduntiltheBIOScanreboot,althoughthiswouldonlytakeplacewhentheprox­imitysensorassertedthattherewasnothinginthevicinity.Thesesecurityrobotsarenowusedasgardenornaments.

Fromtheabove,wecandeterminethat:

• RobotswillhaveafairlyrudimentaryAI.

• Therobotsejectcollectableitemswhenknockeddown.

• Robotswillrespawnwhentheplayercan'tseethem.

75

Page 76: 3D Platform Tutorial

Seek&DestroyMostgameAIisprimarilyfocusedonmodelingbehaviorratherthanintelligenceassuch.Ourrobotsdeliberatelydisplaylittleactualintelligence,butmerelyreactinpre­dictablewaystotheplayer'spresence.Thisisnotnecessarilyabadthing:Manygameplayerslikethiskindofbehavior.Itgivesthemapatterntolookforandthusmakesiteasierthelearnhowtodefeattherobots.

TherobotguardsthereforehaveaverysimplebehaviorpatternandthisisreflectedinthemainAIscript,EnemyPoliceGuy.

Idle­­Inthismode,theguardsjuststandtherethinkingrobotthoughts,tickingawayquietlyandwaitingforanintrudertocomeintorange.

Threaten­­Ifanintrudercomeswithinasetdistanceoftheguard,theguardcomesoutofstandbyandannouncesitsintentionswhilespinningitsbatoninwhatwasad­vertisedinthesalesbrochureasa“threateningmanner".

Seek&Destroy­­Onceactivated,therobotguardswillhomeinontheintruderandtryandattackhim.Iftherobotguardgetswithinstrikingrangeoftheintruder,theguardwillattempttohititwithitsbaton.

NOTE Oneanimationsequenceisusedforbothmodesandisnamed“turnjump”inthemodelandscript.

Struck­­ Iftheplayerstrikestherobotguard,thisanimationisplayed.(“gothit”inthemodelandscript.)

Iftheplayermovesoutsidetherobot'slimitedscanningrange,therobotwillrevertbackto"Idle"mode.

TheabovestatesarehandledbytheEnemyPoliceGuyscript,whichalsodealswithswitchingbetweentheanimationsequences.(Thereweren’tenoughanimationse­quencestojustifysplittingthescriptintotwoaswasdonewiththeplayer.)

AddingtheRobotGuardsWeneedafewoftheserobotsonourlevel,so...

OpentheProjectPaneandlocatetheCopperPrefabinsidetheEnemiesfolder.

FindasuitablespotonthelevelanddropthePrefabontotheScene,ensuringitisontheground.(ThePrefabincludesaCharacterControllerComponent.Makesurethelowerendofthecapsulecolliderisjusttouchingtheground,orslightlyaboveit.)

Areaswithfencesorwhichareotherwisemainlyenclosedarebestasthismakesitlesslikelythattherobotwillfalloffthescenery.

76

Page 77: 3D Platform Tutorial

Ifyouplaythegamenow,youwillseetherobotstandingaroundplayingtheIdleanimation.Wenowneedtoaddsomescriptstomaketheserobotsdosomethingmoreinteresting.

Therearetwoscriptsfortherobotandwe'lladdthemdirectlytotheoriginalPrefab:

SelecttheCopperPrefabtobringitupintheInspector.

DragtheEnemyDamageandEnemyPoliceGuyscriptsontotheCharacterCon­trollercomponentintheInspector.

Ifyouhaven’talreadydoneso,dragtheThirdPersonCharacterAttackscriptontothePlayerobject.Thishandlestheplayer’sendofthepunchingmovement.(Lerpzwon’tpunchrobotswithoutit!)

Ifyouplaythegamenow,therobotshouldreacttoyouifyougettooclosetoit.

BlueSparksOfDeathIftheplayermanagestobeattherobotupandknockhimdown,adeathsequenceisinitiated.Wewanttherobottofalloverinashowerofsparksandliethereuntiltheplayermovesoutofrangebeforeresetting.

Inaddition,whentherobotdies,wewantittospitoutanycollectablesit’scarrying.

Ratherthanaddingyetmorescripting,animationdataandotherelementstoourcur­rentPrefab,we’llcreateanewonejustforthedeaththroesofourelectricfoe.

Divide&ConquerWhenourrobotdies,weneedittostopreactingtotheplayerandsuspendthescripts.Thisisnotquiteassimpleasitseems:scriptstendtorunindependentlyofeachother,sowewouldneedtosendoutmessagesandmaintainadedicatedstatevari­ableineachscriptwhichwouldneedtobecheckedduringeverycycle.

ItismucheasiertosimplyswapoutourrobotPrefabforanotherone­­avirtualstuntdouble,builtspecificallyforthepurposeoffallingover,withsuitablespecialeffectsandscriptingtospitoutuptotworandompickups.Sothisisexactlywhatwe’lldo.

77

Page 78: 3D Platform Tutorial

Arobotguardkeelsover

TheimageaboveshowsthisreplacementPrefabinaction.

ThescriptwhichbringsitintoexistenceisEnemyDamage,solet’stakeacloserlookatit...

WhenanApplyDamagemessageissenttotheGameObject,theApplyDamage()

functioniscalled.Iftherobothassufferedtoomanyhits­­inourexample,thehitpointsaresetto3­­itcallstheDie()function,whichiswherethefunbegins.

TheDie()functionfirstmarksourCopperGameObjectfordestruction.(Thedestruc­

tionisnotperformeduntilaftertheUpdate()functionisdone,sothere’snoreason

nottodothisatthestartofthefunction.)

Next,thereplacementPrefabisinstantiatedandtherobot’scurrentposition­­includ­ingthoseofallchildobjectfromheadthroughtorsoandarms ­­ iscopiedover.

Thesparklyexplosionisjustanotherparticlesystemasset.ThisisinstantiatedandmadeachildofournewdyingrobotGameObjecttoensureitappearsintherightlo­cationandalignedcorrectly.

Thenextstep,nowthatwehaveourinstanceinplace,istogiveitapushawayfromtheplayersothatUnity’sphysicsenginecanmakeitfalloverandrollabout.

Finally,weinstantiateamaximumoftwopickups,eachwitha50:50chanceofbeingeitherahealthorfuelcellpickup,andlaunchthemintotheairinarandomdirection.

78

Page 79: 3D Platform Tutorial

YoucanimplementalloftheabovebyselectingtheCoppergameobjectandalteringthevaluesinEnemyDamagecomponent.Weencourageyoutotryoutdifferentvariations.

DroppablePickups&PhysicsThepickupswhichappearwhenarobotisknockedoverarePrefabsderivedfromtheoriginalswecreatedearlier,butwithaddedparticleeffectsandascript,Droppa­bleMover,whichhandlesmovementandcollisions.

Thescriptisneededbecausethecolliderissetasatriggerforthepickupandthusisignoredbythephysicsengine.Thepickupisgivenaninitialvelocityandthescriptthenusesraycasting­­castinganimaginarylinedownwardstocheckifthepickuphashitasurface.Whenitdoes,thescriptissimplydisabled.(Thiscodedoeshaveonedrawback:itonlychecksdownwards,intheYaxis.Thus,thedroppablepickupitemmayendupembeddedpartwaythroughawall.)

Spawning&OptimizationSpawningenemieswhentheyarewithinasetdistanceoftheplayerisanoldtrickdatingrightbacktotheearliestcomputerandvideogames;itremovedtheneedtostoreeachenemy’sstate.Wecanalsousethistricktoreducetheloadontheproces­sor.Bysimplydeletingeachrobotifit’snotvisibletotheplayer,wecanavoidrunningscriptsandAIunnecessarily.

Let’sbegin:

CreateanemptyGameObjectatthetopleveloftheHierarchyPane.

RenameitCopperSpawn.

DragtheEnemyRespawnscriptontotheobject.

PositionourCopperSpawnobjectsomewherewhereyou’dliketoseearobotappear.(Youshouldseeasmallroboticondisplayedasagizmo,aswellasaspheretoshowthespawnrange.ThesearedrawnbytheEnemyRespawnscript.)

Adjusttheobject’ssettingsasshown.

79

Page 80: 3D Platform Tutorial

CopperSpawnsettings.

We’llneedafewoftheseGameObjects,solet’ssetupaparentGameObjectandmakeourCopperSpawnobjectitschildren...

CreateanemptyGameObjectatthetopleveloftheHierarchyPane.

RenamethisnewobjectEnemies.

MakeourCopperSpawnGameObjectachildofEnemiesbymovingtheformerintothelatter.

NowbuildaPrefabusingourCopperSpawnGameObject.Positioninstancesofthesearoundthelevel.

NOTE Oneside­effectofthistechniqueisthat,ifyouhavekilledarobotandthenmovedoutofrange,itwillreappearasgoodasnewifyoureturntoitsloca­tion.

Howitworks.TheCopperSpawnGameObjectscontainascriptwhichchecksiftheplayerhascomewithinrangeand,ifso,createsaninstanceoftheCopperPrefab.Whentheplayerwalksoutsidethisrange,ourrobot­spawningscriptwillautomaticallyremovethero­botfromtheScene.

ThescriptwhichdoesthisisEnemyRespawn.Thisscriptisheavilycommented,butthetwomainfunctionsare:

Start()­­justcachesalinktotheplayerGameObject’stransformaswe’llneedtomesswithitlater.

Update()­­Firstchecksiftheplayer’sinrangeandinstantiatestherobotifso.If

not,andtheplayerhasjuststeppedoutofrange,therobotprefabisdestroyed.

TherearealsotwoGizmofunctionsusedintheEditor:

EnemyRespawnalsomakesuseofUnity's"Gizmos"feature.AGizmoisusuallyavis­ualaiddisplayedonlyintheSceneViewratherthanin­game,suchas,inthiscase,asphereshowingthespawnrange.Inthisscript,wehavetwotypesofgizmos:thefirst

80

Page 81: 3D Platform Tutorial

drawsaroboticon,whichletsusselectthespawnobjectbyclickingonitsiconintheScenewiththemouse:

NOTE Theiconimageiscurrentlystoredinthe[PROJECT­PATH]/Assets/Gizmosfolder.

TheOnDrawGizmos()functionshowingtheroboticon.

TheGizmocodefortheiconisshownbelow:

function OnDrawGizmos (){ Gizmos.color = Color(1, 1, 1, 1); Gizmos.DrawIcon(transform.position, gizmoName + ".psd"); }

TheOnDrawGizmos()functioniscalledeverytimetheUnityeditorGUIisupdated

orrefreshed,sotheiconwillalwaysbevisible.Inorderthatthisfunctionknowswhichiconimagetouse,weneedto...

...settheGizmoNamepropertyintheInspectorto“Copper”.

Conversely,theOnDrawGizmosSelected()functioniscalledbyUnity'seditorGUIonlywhentheobjectisselected.Aslongasitisselected,itwillbecalledeverytimetheUnityeditorGUIisupdatedorrefreshed.

Inthisexample,thefunctiondrawsasphereusingspawnRangetodefineitsradius,thusprovidingavisualdisplayoftheareawithinwhichtheenemyrobotwillbein­stantiated;whentheplayermovesoutsidethissphere,therobotwillbeautomaticallydestroyed.

81

Page 82: 3D Platform Tutorial

function OnDrawGizmosSelected (){ Gizmos.color = Color(0, 1, 1); Gizmos.DrawWireSphere(transform.position, spawnRange);}

TheOnDrawGizmosSelected()function showingthespheredefinedbytheSpawnRangevariable.

AlternativeOptimizationsInadditiontotheabovetechnique,UnityalsoofferstheOnBecameVisible()and

OnBecameInvisible()functions.However,unlikeourrespawningtechnique,theabovefunctionsarebasedonthecamera'sorientationandothersettingsratherthanthoseoftheplayerobject.ThismeansyouwillseeOnBecameInvisible()called

onanobjectjustbecausethecamerahasturnedawayfromit.Thismaynotbewhatyourequire.

Anothertechnique,evenmoreoptimalthanourown,istouseCollidercomponentsastriggersinsteadofusingscriptcodetocheckfortheplayer'slocation.UnityprovidestheOnTriggerEnter()andOnTriggerExit()functionsforthispurpose.How­ever,thismightnotbefeasibleifyouwantyourrespawnscriptstobeattachedtoanobjectthatneedstouseacolliderforotherpurposes.

82

Page 83: 3D Platform Tutorial

Audio&FinishingTouches

IntroductionWhenyoubuyaCDorwatchamovie,thesoundyouhearhasinvariablybeenthroughanumberofstages,thelastofwhichisknownasmastering.Masteringistheartoflisteningtotheaudioasawholeandadjustingvolumelevels,filteringoutfre­quenciesandanynumberofothertechnicaltrickstomakethefinalmixsoundasgoodaspossible.Sometimestheprocessfixesobviousflawsinamusicalnumberorsoundtrack,suchasaninstrumentbeingtooloudortooharsh.Atothertimes,theprocessisjustusedtoensurethesoundtrackwillsoundgoodenoughonacheapTVloudspeakerwithoutcompromisingthesoundwhichwouldbeheardfromahigh­endhomecinemasystem.

Gamesarenodifferent:oncetheroughgameplayisinplace,itmakessensetospendsometimefine­tuningit,tweakingandadjustinguntilitisasgoodasitcanpossiblybe.

AudioFinalizingaudioandperformingmasteringdutiesisdifficultwithgames.Gamesareinteractiveratherthanpassive,linearformsofentertainment.Thismeansyouneedtoconsiderhowtheindividualaudioassetsinyourprojectwillinteractand,ifnecessary,performsomepreemptivemasteringandmixingyourself.Essentially,masteringisareal­timetaskwhichneedstobehandledwithinthegamelogicitself.

Inanidealworld,everydeveloperwouldhaveaccesstotheirowntameaudioengi­neer,butthisistherealworldandmanysmall­scaleprojectssimplycannotaffordthis.

Inthischapter,weaddsoundef­fectsandtwocut­scenestoourgame.

Page 84: 3D Platform Tutorial

Themostimportantconsiderationisensuringeachsoundinyourprojectsitswellwithallitsfellows.Thisisasubjectiveprocessassomepeoplelikealotofbass,whileotherspreferhigher­frequencysoundsintheirsoundscapes,soit'sagoodideatousealphaandbetatesterfeedbacktogetamixofobjectiveviewsonyourproject'ssound.(Anaudiomonitoringsetupisalsouseful,butthesecanbeexpensiveandannoyingtocol­leaguesifyoucan’taffordadedicatedroomforthepurpose.)

Ideally,youneedtouseaudiofromasinglesource,mixedbythesamepairofears,soyougetaconsistentsound.If­­asinthistutorial'scase­­youmustuseabunchofdif­ferentaudiosources,bepreparedforalotoftweakingofsettingstogetthesoundstositjustrightinthegame'soverallmix.Anover­loudsoundeffectwillbeveryobvi­oustoplayersandcouldbecomeirritatingifitisheardfrequently.Play­testfrequentlyandbepreparedtobudgetsometimeforthisprocess.

SampleNotesUnityhasafairlysimpleinterfaceforaudio,buttherearesomeimportantthingstoconsider:

• Ensureyoursampleshavesimilarlevels.Thismakesvolumelevelsandroll­offsettingsmuchmoreconsistent.Normalizationcanhelphere,butyoushouldalsoconsiderhowyoursoundswillbemixedtogether.Toomanynoisysoundeffectswillconfusetheplayer.

• Usemonosamplesifyouneedthesoundtobepositionedrealisticallywithinthe3Dworld.

• CompresssamplesusingtheOggVorbiscompressionsystem.(Unitycandothisforyou.)

• Ifbuildingfortheweb,youshouldalwayscompressyoursoundsusingOggVorbis.

• Checkthe"Decompressonload"boxforshorter,frequently­usedsoundeffects.

• Onlyusestereosamplesforlongmusicalpieceswhichdonotneedtobepositionedspatiallywithinthescene.Suchsamplefileswillalwaysbeplayedbackastheyare,atthedefaultAudioListenervolume.

AddingSoundtoLerpzEscapes!Wehavealreadyaddedafewspoteffectstothegame.Inthischapterwewilladdsomemoreaudiotoourgameandcompletetheprocess.

Inmanygamegenres,soundeffectscanbeobtainedfromrealsources,ortakenfromasoundeffectslibrary.However,LerpzEscapes!needssomesoundeffectswhichcan­notbeobtainedsoeasily.Pointingamicrophoneatapassingspacecraftorconvenientrobotguardisnotanoption,sowewillneedtothinkcreativelyaboutthese.

84

Page 85: 3D Platform Tutorial

Wewillnotaddeverypossiblesoundeffecttothegameinthischapter;onlyenoughtodemonstrateUnity'saudiofeatures.Onceyouhavereadthroughthischapter,youwillbeabletoaddadditionalsoundeffectsonyourown.

Thecompletelistofsoundeffectsthegameneedsis:

ThePlayer:• Walking/Runningsounds;

• Attackingsound;

• Gettinghitsound;

• Dyingsound;

• Jet­packthrusterssound;

TheRobotGuard:• Idlesound;

• Attackingsound;

• Gettinghitsound;

• Dying/Explodingsounds;

TheCollectables:• FuelCellcollectedsound;

• Healthcollectedsound;

Environmentalsounds:• Along,loopedsoundsampletogiveasenseofambience.

• Awhooshsoundforthejumppads.

SpaceshipImpoundFence:• "Active"sound;

• "Shutdown"sound;

TheSpaceship:• Atake­offsoundtoplaywiththecut­sceneanimation;

Wehavealreadyaddedafewsoundeffectstoourgame,butwestillneedtoaddoverfifteenmore.Someoftheseareobviousfoleysounds,suchastheplayer'sfootstepsandthejet­packthrusters.Someoftheothersamplesarerepurposedfoleysounds.(ThepickupFuelsound,forexample,isjustthesoundofagolfballbeingstruckbyagolfclub.)

85

Page 86: 3D Platform Tutorial

Thesesoundscanbefoundinalmostanysoundeffectslibrary.Apple'sownGarage­Band,LogicStudio8andSoundtrack/SoundtrackProsoftwareincludesuchsoundsandtheselibrarieswerethesourceformostofthesoundeffectsinthistutorial.Manymoresuchsamplesareavailableonline­­oftenforfree!

AmbientSoundsOurgameissetinanenvironmentwayabovethecloudswithanimpressiveviewofnearbyplanets.Suchanexposedlocationneedsasuitablyout­of­this­worldsound­scapetoimprovethesenseofimmersioninthegame.

ThetutorialprojectincludesaloopedambientsoundsamplenamedsceneAtmos­phere.ThisisastereoOggVorbissamplecreatedbythrowingsomeold,monoBBCRadiophonicWorkshopsoundeffectsfromthe1960satGarageBand,andcombiningthemintosomethingthatsoundedsuitablyalien.

ThesampleisaddedtotheNearCameraobjectintheHierarchyPane.

DropthesampleontotheNearCameraobject.UnitywillautomaticallycreateanAudioSourcecomponent.

Adjustitssettingsasshownbelow:

AddingthesceneAtmospheresoundtotheNearCameraobject.

TheAudioSourcecomponentincludesbasiccontrolswhichallowustotellUnityhowtodealwiththesoundeitherthroughtheInspectororthroughscripting.

Inthisinstance,wesetthePlayOnAwakeswitchsothesoundstartsplayingauto­matically.Thevolumelevelisdeliberatelysetlowasthisisambient,backgroundsoundandwedon'twantittodistractfromtheothersoundsinthegame.

86

Page 87: 3D Platform Tutorial

Let’stakeaquicklookatwhattheremainingsettingsmean:

ThePitchsettingdefineshowfastthesampleplays,with1beingthenormalspeed.Thealgorithmusedisabasicone,similartoataperecorder,sothereisnotime­stretchinginvolved;asettingof2herewouldplaythesampleattwiceitsnormalspeed,doublingitseffectivepitch.

TIP ThePitchsettingisusefulforone­shotsoundeffectsasyoucanadjustthevalueslightlywitheachplaybacktoaddvarietytothesounds.It’sidealforshorteffectslikegunshots,lasersandfootstepsandsaveshavingtocreatemultiplesamples.

Nextcomesettingswhichdefinethevolumerangeofthesound.Ifwewantedthecliptobeinaudiblewhenwe'realongwayfromitssource,MinVolumewouldbesettozero.

Asitsnamesuggests,MaxVolumeisthemaximumvolumeoftheaudio.Wecouldstandrightontopofthesoundsourceanditwouldn'tgetlouderthanthisvalue.

NextcomestheRolloffFactor,whichdetermineshowquicklythesound'svolumechangesrelativetothelistener'sdistance.Asmallervaluemeansthesoundwillbeaudibleoveralargerdistance.Thissettingiskeytoensuringrealisticauralbehaviorinthegame.

FinallywehavetheLoopsetting.Thisisenabledbecausewewanttheimpoundfence'ssoundtokeeponplayinguntilwetellittostop.

NOTE Itisimportanttoensureloopedsoundsaredesignedforcleanloopingoryouwillmostlikelyhearanaudibleclickorpopeachtimeplaybackisrestartedfromthebeginningofthesample.Mostsampleeditorsincludea"findzerocrossings"featureforthispurpose.

TheJumpPadsWhenwebuilttheJumpPadPrefab,wedidn’tbotherwithasoundeffect.Nowwe’lladdone.Buthowwillweplayitback?

Luckily,theJumpPad’sscriptalreadyhassupportforasoundeffect.Ifyouopenthescriptupintheeditor,you’llseetheplaybackcodehere:

... if (audio) { audio.Play(); }...Theaudiovariabledoesn’tappeartobedefinedwithinthescript,sowhere’sitcom­ingfrom?

87

Page 88: 3D Platform Tutorial

It’scomingfromUnityitself.Thisisaconveniencevariable­­oneofanumberofsuchvariables­­whichsimplypointsattheAudioSourceComponentattachedtowhateverGameObjectthescripthappenstobeattachedto.Aswehaven’taddedoneoftheseComponents,theaudiovariablewillbenull,sothescriptskipstheplayback.

ClickononeoftheJumpPadinstancesinourScenetobringitupintheInspec­tor,

DragthejumpPadsoundeffectontotheInspector.(Thiswillautomaticallycre­ateanAudioSourcecomponentforus.)

ApplythechangetotheoriginalPrefab,soallourJumpPadscansharethesamesoundeffect.

ExperimentandtweaktheAudioSource’ssettingsuntilyou’rehappywiththesoundeffect.

CollectablesThecollectableitemsaretheeasiesttodealwith.ThesesoundeffectsarepickupFuelandpickupHealthforthefuelcellsandhealthcollectablesrespectively.Addingtheseeffectsissimplicityitself:thePickupscriptalreadyhassupportforaudio;wejustneedtodroptherelevantsoundsampleintothescript'ssoundeffectslotineachpickuptype'sPrefab.

TheimagebelowshowstheFuelCellPrefabInspectordetailsforoneoftheinstancesinthelevel,withthepickupFuelsoundeffectaddedtothePickupscript'sSoundvari­able:

88

Page 89: 3D Platform Tutorial

SettingthefuelCellPrefab’ssoundeffect.

AddtheaudioeitherbydroppingthepickupFuelsoundeffectontotheSoundvariable'sslot,orbypickingthesoundeffectdirectlyfromthelistofavailablesampleswhichyoucanbringupbyclickingthesmalltriangletotherightoftheslot.

SettheSoundVolumeto2,tomakethesoundeffectstandout.

ApplythesameprocessfortheHealthpickuptoo,usingthepickupHealthsam­pleinstead.

TIP YoucanaddthesoundeffectdirectlytothePrefabtosavetime.

Ifyouplaythegamenow,eachcollectableitemwillnowplaytheappropriatesoundeffectwhenyoupickitup.

89

Page 90: 3D Platform Tutorial

TheImpoundFenceTheforcefieldthatsurroundsthespaceshipshouldmakeahumming,fizzingnoisewhileitisactive.AsuitablesoundeffectwascreatedusingGarageBandbycombiningsomeloopedambienttexturestogetherandexportingtheresultingsoundtoafile.

TIP Apple'sGarageBandonlyexportstotheAAC(".M4A")soundformat,soAudacitywasusedtoconvertthistotheuncompressed,monoAIFFaudiofileusedintheproject.

ThesampleisnamedactiveFence.

GototheHierarchyPane,openlevelGeometryandfindtheimpoundFenceob­ject.

DroptheactiveFencesounddirectlyontothisobject.ThiswilladdanAudioSourcecomponenttotheimpoundFenceobject.

Finally,changetheAudioSource'ssettingstoreadasfollows:

ImpoundFenceAudioSourcesettings.

ThePlayerLerpzhimselfmakesnosoundsatthemoment.Addingsoundeffectsmakessense,butwhichones?

Thesoundeffectswewillimplementinthistutorialare:

• Apunchsound;

• A“struck”sound,playedwhenLerpzishitbyarobot;

• Asoundeffectforthejet­packthrusters;

90

Page 91: 3D Platform Tutorial

• Asoundtoplaywhentheplayerdiesandisrespawned.

(Thefootstepeffectisleftasanexerciseforthereader.)

Thesesoundswillbeplayedbyourscripts.

Punching.ThepunchingmovementandanimationarehandledbytheThirdPersonCharacterAttackscript.ApropertyforthesoundeffectisexposedbythescriptintheInspector.SetthePunchSoundpropertyasshown:

LerpzPunchsoundeffect.

Thescriptplaysthissoundusingthefollowingcode:

if (punchSound) audio.PlayOneShot(punchSound);

Thisfirstchecksifapunchsoundeffecthasbeensuppliedtothescript.Ifso,itusesthePlayOneShot()functiontoplaythesound.Thisfunctioncreatesatemporary

GameObjectwithanAudioSourcecomponent,addsittotheSceneandplaysit.Whenthesoundeffectisfinished,theGameObjectisremovedfromthescene.

NOTE Whilethegameisrunning,youwillseetheseone­shotsoundsappearingbrieflyintheHierarchyPane.Thisisnormalbehavior.

Strucksound&ScreamsoundTheThirdPersonStatusscripthandlestwomoresoundeffects:theoneplayedwhenLerpzisstruckbyanenemy,andtheoneplayedwhenLerpzdies(justbeforeheis

91

Page 92: 3D Platform Tutorial

respawnedorthegameends).Bothsoundeffectsarehandledthesamewayasthe“lerpzPunch”soundeffectweaddedearlier.

AddboththeLerpzStruckandLerpzScreamSFXsoundsasshownbelow:

AddingtheStruckSoundandDeathSoundeffects.

TheJet­packThejet­packisalooped,ratherthanaone­shot,soundeffect,sowe’lladdthesoundeffecttothePlayerGameObjectdirectlyasanAudioSourceComponent.TheJetPackParticleControllerscriptwillautomaticallycreateanemptyAudioSourcecomponent,butweneedtoaddtheaudiofiletoit:

DragthethrusterSoundaudiofileontotheAudioSourcecomponentinsidethePlayerGameObject.

EnsuretheAudioSourcesettingsareasshown:

92

Page 93: 3D Platform Tutorial

Jet­packaudiosettings

Therearetwosectionsofscripttohandlethissoundeffect,bothofwhichareintheJetPackParticleControllerscript.ThefirstsectiongoesintheStart()functionand

initializestheAudioSourcecomponent:

audio.loop = false; audio.Stop();

Thesecondchunkisfurtherdowninthesamefunction:

if (isFlying) { if (!audio.isPlaying) { audio.Play(); } } else { audio.Stop(); }

TIP TheaudiovariableiscreatedbyUnityitself.ItisalwayssettopointattheAu­dioSourceComponentoftheGameObject.

93

Page 94: 3D Platform Tutorial

Thiscodeisself­explanatory.TheneedtotestifthesoundeffectisalreadyplayingisbecausethePlay()functionwillalwaysstartplayingthesoundeffectfromthebe­ginning,regardlessofwhetheritisalreadyplaying.Thismeanswe’dhearastutteringsoundasUnitywouldrepeatedlyrestartthesoundsampleeverytimethePlay()

functioniscalled.

TheRobotGuardsTheseguyshaveanumberofscripts,someofwhichalsohaveaudiosamplesasprop­erties.Inaddition,eachrobotguardhasanAudioSourcecomponent.

UnlikethePlayer,whichusesitsAudioSourcecomponentsolelyforthejetpacksound,theEnemyPoliceGuyscriptusestheCopperPrefab’sAudioSourcecomponentformultipleloopingsounds,switchingbetweenthemasrequired.Anexampleofthecodeusedtoachievethisisshownbelow:

if (idleSound) { if (audio.clip != idleSound) { audio.Stop(); audio.clip = idleSound; audio.loop = true; audio.Play(); } }

TheaboveexamplecanbeseenatthetopoftheEnemyPoliceGuyscript’sIdle()

function.Thecalltoaudio.Stop()isimportanthereasswappingoutasamplewhileit’sbeingplayedcanhaveunpredictableresults.

Toaddthesoundeffects:

SelecttheCopperPrefabintheProjectPanetobringitupintheInspector.

DragtheCopperIdleLoopsoundeffectintotheAudioSourcecomponent.

AddthesameaudiofiletotheIdleSoundpropertyintheEnemyPoliceGuyscriptcomponent.

AddtheCopperActiveLoopsoundeffecttotheAttackSoundpropertyinthesamecomponent.

Finally,addtheMetalHitsoundeffecttotheStruckSoundpropertyoftheEn­emyDamagescriptcomponent.

(TheresultingviewintheInspectorPaneisshownonthenextpage.)

94

Page 95: 3D Platform Tutorial

TheMetalHitsoundisplayedintheApplyDamage()functionwithintheEnemy­Damagescript.Thecodewhichdoesthisisshownbelow:

if (audio && struckSound) audio.PlayOneShot(struckSound);

AswiththeJetPack,theaudiovariableweusehereisactuallyashortcutvariable

createdbyUnityitself.ItistheequivalentofaGetComponent (AudioSource)

functioncall.TheupshotisthattheAudioSourcecomponentattachedtotheCopperPrefabisusedtoplaythesoundeffectforus,avoidingtheneedtoinstantiateatem­poraryaudiosourcecomponentforthepurpose.

NOTE ThesametrickisusedforsomeofthePlayersoundeffects.

CopperPrefabInspectorsettingsafteraddingtheaudiofiles.

95

Page 96: 3D Platform Tutorial

CutScenesCut­scenesprovideausefulwaytofillintheplayeronaneventorstoryelementwhichtheyneedtoknow.Ideally,theneedtocutawayfromtheplayershouldbekepttoaminimum.Forthisreason,wehaveonlytwocut­scenes.

Thefirstoccurswhentheplayermanagestocollectalltherequiredfuelcellsinthelevel,thusunlockingthespaceship.Thiscut­sceneappearsusingapicture­in­picturetechnique,sothattheplayercancontinueplayingwhilethesequenceplaysout.

Oneextremelypragmaticreasonfornottakingoverthewholescreenwiththiscut­sceneisthatwe’dotherwisehavetofreezeallthegameelements­­robots,playercontrols,etc.­­whilethecut­sceneplays,becausetheplayerwouldbeeffectivelyblinduntilthescenecompletes.

NOTE Itisstillpossibletogetatthespaceshipifyouclimbontothenearbycrates,butthespaceshipisstilllockeddownatthispointandwon'ttakeoff.(Themeshcolliderisn’tchangedtoaTriggertypeuntiltheimpoundfenceisun­locked.)

Thesecondcut­sceneoccurswhentheplayertouchesthespaceshipafterthefencehasbeendisabled.Inthisscene,whichisplayedfull­screen,weseethespaceshiptakingoffandflyingofftofreedomandnewadventuresbeforeswitchingtotheGameOversequence.

Let’slookatthefirstsceneindetail...

UnlockingtheimpoundfenceWefirstcameacrosstheImpoundFencepartofthelevelwhenweaddedsomeani­mationtoit.Earlierinthischapter,weaddedasoundeffecttoitaswell.Nowwe’llanimateit.

Butfirst,weneedtomakesurewedothiswhenwe’vepickedupallourfuelcans(Youcanfindthecompleteassembledscriptcodelistedintheappendixsection):

OpentheThirdPersonStatusscriptandlookfortheFoundItem()function.

Aftertheblockofcomments,addthefollowinglinesofcode.(Makesureyouinsertthisbeforethefunction’sclosingbrace­­}­­symboloritwon’twork!)

if (remainingItems == 0) { levelStateMachine.UnlockLevelExit(); // ...and let our player out of the level. }

96

Page 97: 3D Platform Tutorial

Savethescript.

OpentheLevelStatusscript.Thisiswhereboththecut­scenesarehandled(Youcanfindthecompleteassembledscriptcodelistedintheappendixsection).

Atthetopofthescript,addthefollowingcode:

var exitGateway: GameObject;var levelGoal: GameObject;

var unlockedSound: AudioClip;var levelCompleteSound: AudioClip;

var mainCamera: GameObject; var unlockedCamera: GameObject; var levelCompletedCamera: GameObject;

NOTE Theabovecodeincludesvariableswe’llneedforthesecondcut­scenetoo.

Next,addthefollowinglineofscriptcodetotheAwake()function:

levelGoal.GetComponent(MeshCollider).isTrigger = false;

Thatlineiscrucial.Itstopsthesecondcut­scenefromtriggeringprematurely.WiththeisTriggerswitchturnedoff,thespaceshipbecomesjustanotherpartofthesceneryaslongastheimpoundfenceisstillactive.

Nowfortheunlockingsequenceitself.

AddthefollowingfunctiontotheLevelStatusscript.(I’llexplainitaswego.)

function UnlockLevelExit(){ mainCamera.GetComponent(AudioListener).enabled = false;

Unitysupportsjustone­­andonlyone­­AudioListenercomponentinanyoneScene.UsuallythisisattachedtothemaincameraintheScene,butwe’reusingmultiplecamerasinourScene,soweneedtoensureweonlyhavetheoneAudioListenerac­tiveatanyonetime.Wewanttohearour“fencedisabled” soundeffects,sowe’llbrieflyenabletheAudioListeneronourcut­scene’scamerahere.

Next,weneedtoactivatethecut­scenecameraandenableitsAudioListenercompo­nent:

97

Page 98: 3D Platform Tutorial

unlockedCamera.active = true; unlockedCamera.GetComponent(AudioListener).enabled = true;

TheImpoundFencehasaloopedsoundeffectattachedit.Weneedthatsoundtostopplayingnow:

exitGateway.GetComponent(AudioSource).Stop();

Nowwecanstartplayingour“fencedisabled”soundeffect:

if (unlockedSound) { AudioSource.PlayClipAtPoint(unlockedSound, unlockedCamera.GetComponent(Transform).position, 2.0); }

Withoursoundeffectstarted,wecanstarttheanimationsequence.We’lldothispro­cedurallybyusingscriptcodetoachievetheanimation.Thenextfewlinesperformthissequence.Thefirstlineaddsadelaybeforethesequencebeginstogivetheap­pearanceofthecut­scenetimetoregisterontheplayer’sconsciousness.(I’veleftthecommentsinplacesoyoucanfollowtheanimationsequence):

yield WaitForSeconds(1); exitGateway.active = false; // ... the fence goes down briefly... yield WaitForSeconds(0.2); //... pause for a fraction of a second... exitGateway.active = true; //... now the fence flashes back on again... yield WaitForSeconds(0.2); //... another brief pause before... exitGateway.active = false; //... the fence finally goes down forever!

Wenowhaveaccesstotheship!Allweneedtodonowismakethespaceship’sMeshCollidercomponentaTriggerratherthananormalcollider:

levelGoal.GetComponent(MeshCollider).isTrigger = true;

98

Page 99: 3D Platform Tutorial

Finallywepauseafewmoresecondssotheplayerhastimetoseetheresults,beforeshuttingoffourcut­scenecameraandrestoringtheAudioListenercomponentonourNearCamera:

yield WaitForSeconds(4); // give the player time to see the result. // swap the cameras back. unlockedCamera.active = false; // this lets the NearCamera get the screen all to itself. unlockedCamera.GetComponent(AudioListener).enabled = false; mainCamera.GetComponent(AudioListener).enabled = true;}

Creatingthecut­scenecameraitselfisournexttask.ThisisjustanotherCameragameobject,exactlyliketheonewe’vebeenusinginthegameitself.Let’ssetitup:

AddanewCameratotheScene.

RenameitCutSceneCamera1

AddtheSmoothLookAtscripttotheCamera.

DropalinktothespaceShipmodelontotheSmoothLookAtscriptsothescriptknowswhatthecameraneedstopointat.

Settheremainingpropertiesasshown:

99

Page 100: 3D Platform Tutorial

CutSceneCamera1properties.

Thiscamerashouldbesettolookatthespaceshipimpoundlot,clearlyshowingthefencing.Thesettingsshownaboveshouldbeagoodapproximation,butfeelfreetotweakit:

PositioningCutSceneCamera1.

Next,adjusttheCutSceneCamera1Camerasettingslikeso:

100

Page 101: 3D Platform Tutorial

CutSceneCamera1CameraComponentsettings.

Thecameraneedstobedisabledbydefault.It’llbeenabledbyourscripts,butwedon’twantitrenderinganythinguntilthescriptsaysso.Disablingthecameraissim­ple:justunchecktheboxnexttoCutSceneCamera1,rightatthetopoftheInspector.

Also,takecaretosettheNormalizedViewPortRectsettingsasshownabove.Thesedefinethecamera’soutput’spositiononthescreensothatitappearsinthetoprightcornerofthedisplay.Thecamera’sdepthisalsosetto10,whichishigherthanthatoftheNearCamerasothecut­scenewillappearontopofthemaingameimagery.

Weneedtotestthesequence,sosettheLevelStatusscript’spropertiesasshownbe­low:

SettingtheLevelStatusscriptproperties.

101

Page 102: 3D Platform Tutorial

ThespaceShipobjectisthespaceshipmodelsittinginsidetheimpoundfence.

TIP I’vetemporarilysettheItemsNeededvalueto2inthescreenshotabove.Thisletsmetestthecut­scenebycollectingonlytwofuelcansinsteadofwastingtimerunningaroundmostofthelevelfirst.Remembertoresetittoahighernumber­­say,20­­whenwe’redone.

Ifyouplaythegamenow,youshouldseethecut­sceneappearasshownbelow.

Ourfirstcut­sceneinaction.

NOTE Theframes­per­secondcountervisibleinboththemainviewandthecut­sceneinsertiscoveredinthechapteronoptimization.

Thelastcut­sceneisalittlemorecomplex.Weneedthespaceshiptotakeoffandflyaway.Wecoulddothisusingscripting,butit’smucheasiertouseanAnimationClipforthisinstead:

ClickonthespaceShipmodelinsidetheimpoundfencetoselectit.(OrclickonitintheHierarchyPane)

NowdragtheShipAnimationAnimationClipfromtheAnimationsfolderintheProjectViewandaddittotheSpaceshipAnimationpropertyoftheAnimationcomponentintheinspector.

Youwillnowseeananimationcomponentinthespaceshipinspector.Ifyouhitplaynowyouwillseetheshiptakingoff.Howeverweonlyneedtheshiptotakeoffwhentheleveliscomplete.Wewilldothiswithascriptinaminute.HoweverUnity,byde­fault,assumesyouwantanimationstoplayautomatically.Wedon’twantthis,so,withthegamestopped:

Disablethe“PlayAutomatically”checkboxofthespaceShipAnimationcompo­nent.

102

Page 103: 3D Platform Tutorial

ThespaceShipobject’sanimationsettings.

Thenextstepistocreateoursecondcut­scenecamera:

CreateanewCameraobject.

RenameitCutSceneCamera2.

Positionitontopoftheimpoundlot’sofficebuilding,asshown:

103

Page 104: 3D Platform Tutorial

PositioningCutSceneCamera2.

Don’tworryaboutthedirectionit’sfacing:we’reonlyinterestedinitslocation.Thescriptwe’llattachtoitwilltakecareoftherest.

AddtheSmoothLookAtscripttotheCamera.

DropalinktothespaceShipmodelontotheSmoothLookAtscriptsothescriptknowswhatthecameraneedstopointat.

AswithCutSceneCamera1,wealsoneedtoensurethisoneisdisabledbydefault,buttheremainingsettingsarealittledifferent,asyoucanseefromthescreenshotonthenextpage.Thekeydifferenceisthatthiscameraneedstotakeoverthewholescreen,ratherthanappearinginthecorner,sotheNormalizedViewPortRectpropertiesaresettotakethisintoaccount.

104

Page 105: 3D Platform Tutorial

SettingsforCutSceneCamera2.

Nowforthecut­sceneitself.Thisisalittletrickierthanourfirstcut­scenebecausethemessagesusedtotriggerthesceneneedtoberelayedalongachain:

TheinitialtriggerhappenswhenthePlayertouchesthespaceShipmodel.(Ifthefirstcut­scenehasplayedthrough,thespaceShipmodelisnowactingasatriggerinsteadofasolidobject.)

ThespaceShipmodelthereforeneedsascriptattachedtoittodealwiththistriggerevent:

CreateanewJavaScriptscriptasset.

RenameitHandleSpaceshipCollision.

Addthefollowingcodetoit(Youcanfindthecompleteassembledscriptcodelistedintheappendixsection):

private var playerLink : ThirdPersonStatus;

function OnTriggerEnter (col : Collider) { playerLink=col.GetComponent(ThirdPersonStatus); if (!playerLink) // not the player. { return;

105

Page 106: 3D Platform Tutorial

} else { playerLink.LevelCompleted(); } }

Alltheabovecodedoesischeckiftheplayerhastouchedthespaceshipand,ifso,calltheLevelCompleted()functioninthePlayer’sThirdPersonStatusscript.

AddthenewscripttothespaceShipobject.

TheThirdPersonStatusscript’sLevelCompleted()functionisevenshorterand

doessomethingverysimilar.

AddthefollowingfunctiontotheThirdPersonStatusscript:

function LevelCompleted(){ levelStateMachine.LevelCompleted();}

levelStateMachineisapropertywhichlinkstotheLevelStatusscript.LevelStatusiswheretheactionisasthelevelcompletionanimationissomethingonlythelevel­relatedscriptsshouldknowabout.

AddthefollowingLevelCompleted()functiontoLevelStatusnow(I’llex­

plainitaswego):

function LevelCompleted(){

First,wehavetodothesameAudioListenerswitchoveraswedidforourfirstcut­scene:

mainCamera.GetComponent(AudioListener).enabled = false; levelCompletedCamera.active = true; levelCompletedCamera.GetComponent(AudioListener).enabled = true;

Next,wewanttogivetheillusionthattheplayerisinsidethespaceship,sowe’llhide

106

Page 107: 3D Platform Tutorial

him.Todothis,we’llsenda“HidePlayer”messagetothePlayer’sThirdPersonControl­lerscript.Thefunctiondisablestherenderingoftheplayer,sohebecomesinvisible:

playerLink.GetComponent(ThirdPersonController).SendMessage("HidePlayer");

Justtobeonthesafeside,we’llalsophysicallyrelocatetheplayertoapositionweknowshouldbesafe.(Therobotsdon’tcheckiftheplayer’svisibleornotandthey’restilloperating.)Inthisinstance,we’lljustmovetheplayer500unitsstraightup,whichshouldbefarenoughawayfromanyimmediatedanger.

playerLink.transform.position+=Vector3.up*500.0; // just move him 500 units

Next,we’llstartthelevelcompletedsoundeffect.Inthiscase,it’sthesoundofthespaceshiptakingoff:

if (levelCompleteSound) { AudioSource.PlayClipAtPoint(levelCompleteSound, levelGoal.transform.position, 2.0); }

Nowwestartthetimeline­basedanimationwerecordedearlierandwaitforittofin­ish:

levelGoal.animation.Play(); yield WaitForSeconds (levelGoal.animation.clip.length);

Andfinally,weloadthe“GameOver”Scene:

Application.LoadLevel("GameOver"); //...just show the Game Over sequence. }

Nextwemustensurethatthespaceshipmodelisnotsetasatriggerwhenourlevelstarts,andalsoensureplayerLinkispointingatthePlayerGameObject.We’lldefinetheplayerLinkvariableinsideanAwake()function,whichwillbecalledautomati­callybyUnitywhenthescriptisloaded.

107

Page 108: 3D Platform Tutorial

ChangetheAwake()functiontomatchthescriptbelow.ItshouldbelocatedjustabovethefirstfunctioninLevelStatus.

private var playerLink: GameObject;

function Awake(){ levelGoal.GetComponent(MeshCollider).isTrigger = false; playerLink = GameObject.Find("Player"); if (!playerLink) Debug.Log("Could not get link to Lerpz"); levelGoal.GetComponent(MeshCollider).isTrigger = false; // make very sure of this!}

Finallyweneedtosetourupdatedscript’spropertiesasshown:

ThefinalsettingsfortheLevelStatusscriptproperties.

Theresult,whenyoucollectallthefuelcansandjumpintothespaceship,shouldlooksomethinglikethis:

108

Page 109: 3D Platform Tutorial

Missioncompleted!Ourherotakesofffornewadventures.

Thenextchapterwrapsthingsupwithalookatoptimizationtechniques.

109

Page 110: 3D Platform Tutorial

Optimizing

We’renowatthewrapping­upstage.Optimizingissomethingdoneneartheendofaproject,onceallthekeyelementsareinplaceandnaileddown.What,whereandhowyouoptimizeyourprojectverymuchdependsonyourproject’sdesignandcon­tent,sothischapterisprimarilyadiscussioncoveringthemorecommontypesofop­timization.

WhyOptimize?Unityprojectsareoftentargetedatoldercomputersthan,say,amainstream,big­budgetgame.Puzzlegames,casualtitlesandotherprojecttypesmayberequiredtorunonanythingfromaG4iBookwithamere256MbofRAMandanancientgraphicschipset,throughtoacurrentIntel­basedMacProstuffedtothefaceplateswithmem­oryandahigh­endgraphicscard.

Forthisreason,weneedtoconsideroptimizingourprojectsforfinalrelease.Inourcase,we’vealreadyoptimizedtheenemyrobotsbymakingthempopintoexistenceonlywheninrange,sothat’salreadycovered.However,therenderingofthescenecanbequiteslowandthisisworthlookingatinmoredepth.

OptimizingRendering:MonitoringFramesPerSecondThebestwaytodeterminewhetheryourgameneedstobeoptimizedistofindouttheframe­rate­­thenumberofimagesbeingrenderedeachsecond­­anddisplaythis.Thelowerthenumber,theslowerthegameisrunning.

Youmayhavenoticedanumber,ortheletters“FPS”,insomeofthescreenshotsinthistutorial.Thisisbecauseoneimportant,butmercifullyveryshort,scriptisrunning

GamesmayberequiredtorunonanythingfromaG4iBooktoacur­rentIntel­basedMacProstuffedwithmemoryandahigh­endgraph­icscardortwo.Forourgamestorunonboth,weneedtoopti­mize...

Page 111: 3D Platform Tutorial

whichwe’lllookatnow:aFrames­per­secondreporterscript.Thescriptisnamedsim­plyFPSandcanbefoundintheScripts­>GUIfolderoftheProjectPane.

Thescriptisthoroughlydocumented,soIwon’tcoveritindetailhere,otherthantoaddthatitrequiresaGUITextComponent.(ThisisaUnity1.xGUIComponenttypeasopposedtothenewUnity2GUI.)

WiththisFPScounter,itiseasiertogetagoodideaofwhereoptimizationsneedtobemade.

NOTE TheFPSscriptisoflimitedvaluewhenrunningthegameintheUnityEditor.ThisisbecausetheEditorrenderermaybelockedtotheframe­rateofthedis­play.ItalsohastoupdatetheSceneViewandgenerallyperformsmoreerror­checkingwhileplayingScenes.Formoreaccurateresults,useaStandaloneBuildofyourproject.

MakingsenseoftheStatsdisplayNewtoUnity2isa“Stats”buttonabovetheGameView.Ifweenablethis,wecangetsomeadditionalmetricsonourgame,whichcanhelpdeterminewhetherthereareissuesofpolygon­countorotherobjectcomplexitytoresolve.

TheStatspanelinaction.

Thesestatisticsarebasedonwhatthecameraisrendering,somovingaroundthescenewillchangemanyofthestatistics.Theimportantelementsare:

DrawCalls­­thenumberofrenderpasses.Elementsinascenemayneedtoberen­deredmultipletimes:forshadows,multipleCameras,renderingtotextures,pixellights,andmore.Complexshaderscanalsocauseadditionaldrawcalls,particularlyifreflectionorrefractionisbeingcalculated.

111

Page 112: 3D Platform Tutorial

Tris­­ Thenumberoftrianglesbeingdrawn.

All3Dmodelsarebuiltupfromtriangles.Thefewerthetriangles,thefasterthey’llrender.Round,curvedobjectswillgenerallyusemoretrianglesthanbasic,straight­edgedshapeslikecubesandplanes.

Verts­­ Thenumberofverticesbeingsenttothegraphicschip.

Avertexisapointin3Dspace.Themoreverticesyoucanshareacrosstriangles,themoreofsaidtrianglesyoucanrenderandthusthemorecomplexyourmodelscanbe.

UsedTextures­­thenumberoftexturesusedtorenderwhatyousee.

Materialscanuseoneormoretextures,dependingonhowthematerialisdefinedandtheshaderscriptitisusing.Theshaderdefineshowthetexturesarecombined,producingeffectssuchasbump­mapping,glossyshinehighlights,reflectionsandre­fraction.

TIP Particlesystemsusetwotrianglesforeachparticle,andatleastonetexture.(Texturesareusuallysharedbyalltheparticlesinaparticlesystem.)Itisveryeasytogetcarriedawaywiththeseeffects,butyoushouldtakecarenottooverdoit.

RenderTextures­­thenumberofcamerasoutputtingtoatextureratherthandirectlytothedisplay.Thisisn’tasclear­cutasyoumightexpect...

Rendertexturesareusedtoachieveanumberofeffects,suchaspost­processingef­fects,aCCTVscreendisplayinganotherareaofalevel,ortoproducereflectioninwa­ter,amirrororglassrefractioneffects.Inaddition,mostshadowsarealsoproducedusingthistechnique,soyouwon’tnecessarilyseeadditionalcamerasinyourSceneView.

OptimizingRendering:TheTwo­CameraSystemIfyouhaveplayedwiththecompletedproject,youmayhavenoticedbynowthatthereare,infact,twoNearCameras:aNearCameraandaFarCamera.Thistwo­camerasystemreducestheamountofrenderingneededforeachframe.

TheNearCamerarenderseverythingwithinacloserange;inthiscase,from0.4to50units.

TIP Aunitcan,intheory,beanyarbitrarylengthyoulike,butmostdeveloperstendtostickwith"1unit=1meter"forthesakeofsanity.Animportanttipistomakesureyourartist(s)areawareofthescaleyouareusing.

TheFarCamerarendersfromthe50unitsmarkthroughtoaround500units.How­ever,itonlyrendersasubsetoftheScenedata.ThesubsetisdefinedbyLayers.Each

112

Page 113: 3D Platform Tutorial

objectintheSceneisgivenaLayertoliveon.TheNearCamerarendersallelementsregardlessoftheirlayer,butasyoucanseefromthescreenshotbelow,theFarCam­erahasbeentoldtoignorethoseitemsinthe“cameraTwo”or“cameraTwoIgnore­Lights”Layers.We’dalsolikeittoignorethefuelcansandhealthpickups.Thesehavethe“noShadow”Layer,sothattooisuncheckedforthiscamera.

NOTE Uncheckingthe“noShadow”Layeralsomeansitwon’trendertheplayerei­ther,whichisfineastheplayerisunlikelytobethatfarawayfromtheNearCamera.

TheFarCameraCullingMasksettings.

NOTE TheFarCameraisalsothecamerawheretheSkyboxisrendered.(SeetheClearFlagssettingintheshotabove.)TheNearCamera’sClearFlagssettingis“DepthOnly”,sothatitscontentissuperimposedoverthatoftheFarCamera.TheFarCamera’scontentisrenderedfirst.

ThisselectionisdefinedbytheCullingMaskpropertyofthecameracomponent.Tickedlayersarerendered;untickedlayersarenot.YoucandefineaLayerintheIn­spectorandassignonetoanyGameObject.

Youcanseethisoptimizationinactionwiththerobotguardsandthecollectableitems.Ifyoumovetowardsoneoftheseitems,youwillseethesceneryarounditisalwaysrenderedwhiletheitemitselfappearsrelativelyclosetotheplayer.

113

Page 114: 3D Platform Tutorial

Endoftheroad.

TheRoadLessTravelledAtthetimeofwriting,thistutorialholdsthedubioushonorofbeingthelongesteverproducedforUnity.Wehaveseenhowtobuildasinglelevelofagamebasedaroundthe3DPlatformergenre,butevenafterallthesepages,wehavebarelyscratchedthesurfaceofwhatispossiblewithUnity2,orevenwiththisparticulargenre.

Ourjourneytogetherisdone:It’stimeforyoutotakethestabilizerwheelsoffthebicycleandcontinuealone,butbeforeyougo,herearesomesuggestionsforwhattotrynext...

SuggestedImprovementsLerpzEscapeshasbeenleftdeliberatelyunfinished.WehaveaverybasicStartMenuandaGameOverscreen,butthere’sonlytheonegamelevelanditisclearlyintendedtobethelastoneinthegame.Howcoulditbeimproved?

FixingthedeliberatemistakesYes,therearesomeminorissueswiththegameasitstands.Thesehavebeendeliber­atelyleftinplacetogiveyouachancetohoneyourskills.Theyare:

• Ifyoukillarobot,butarerespawnednearbybeforeyouhavemovedoutofrange,anewrobotwillappear,buttheoldonewillremain.

• TheLaserTrapsdon’tkicktheplayeraway,soitispossibletoloseallyourhealthratherquickly.

Whatwehavelearned.

Wheretogonext.

Page 115: 3D Platform Tutorial

Bothcanberesolvedbyapplyingwhatyouhavelearnedinthistutorial.

MorelevelsTheProjectPaneincludesa“BuildYourOwn”foldercontainingalltheindividualas­setsusedtobuildthelevel,soaddingnewlevelsshouldnotbedifficult.

YouwillneedtousetheDontDestroyOnLoad()functionsothatyoucancarry

gamestateinformationbetweenthelevelssuchasthecurrentscore,livesremaining,etc.

MoreenemiesThegameonlyhastheoneambulatoryenemyintheformoftherobotguards.Whynotaddsomemore?ThisisagoodwaytoensureyouhaveunderstoodtheanimationandAIaspects.ItwillalsohelpyougetafirmgraspofbuildingmodelsandimportingthemintoUnity.

AddscoringLerpzEscapeslacksascoresystem.Addingoneisnotdifficult,butaddingvisualef­fectswhentheplayerdoessomethingworthyofincreasingtheirscorecanbeaschal­lengingasyoulike.(And,ofcourse,you’llwanttokeeptrackofthescoreacrossScenes.)

Addanetworkedhigh­scoresystemTheUnity2bringssolidnetworkingsupporttothetable,aswellasintegrationwithwebsites.Whatbetterwaytoshowoffthanuploadingyourhighscoretoacentralserversoyoucangloat?Thisisagoodwaytowrapyourheadaroundnetworkingba­sics.

AddmultiplayersupportAddingnetworkedmultiplayergamesupportisprobablythetrickiestthingyoucandowithanygame.Naturally,Unitycanhelpheretoo,butyouwillneedtogetdownanddirtywithscripting.Thisisagood,advanced­levelimprovementtoadd.

FurtherReadingThefirstplacetolookformoreinformationis,asalways,Unity’sowndocumentation.

Therearealsomanytutorials(includingvideoprimers)ontheUnitywebsite:http://unity3d.com/support/documentation/

Inaddition,theUnifyWikiisanexcellentsourceofuser­contributedinfo:http://www.unifycommunity.com

Andfinally,youcantalktoexpertsandnewcomersalikeinourthrivingforums,here:http://forum.unity3d.com/

115

Page 116: 3D Platform Tutorial

ScriptAppendix

StartMenuGUIscript

HereistheassembledcodefortheStartMenuGUIscript.

//@script ExecuteInEditMode()

var gSkin : GUISkin;var backdrop : Texture2D;private var isLoading = false;

function OnGUI(){ if(gSkin) GUI.skin = gSkin; else Debug.Log("StartMenuGUI : GUI Skin object missing!");

var backgroundStyle : GUIStyle = new GUIStyle(); backgroundStyle.normal.background = backdrop; GUI.Label ( Rect( ( Screen.width - (Screen.height * 2)) * 0.75, 0, Screen.height * 2, Screen.height), "", backgroundStyle);

GUI.Label ( Rect( (Screen.width/2)-197, 50, 400, 100), "Lerpz Escapes", "mainMenuTitle");

116

Wherethescriptsareassembled

Page 117: 3D Platform Tutorial

if (GUI.Button( Rect( (Screen.width/2)-70, Screen.height -160, 140, 70), "Play")) { isLoading = true; Application.LoadLevel("TheGame"); }

var isWebPlayer = (Application.platform == RuntimePlatform.OSXWebPlayer || Application.platform == RuntimePlatform.WindowsWebPlayer); if (!isWebPlayer) { if (GUI.Button( Rect( (Screen.width/2)-70, Screen.height - 80, 140, 70), "Quit")) Application.Quit(); }

if (isLoading) { GUI.Label ( Rect( (Screen.width/2)-110, (Screen.height / 2) - 60, 400, 70), "Loading...", "mainMenuTitle"); }}

GameOverGUIHereistheassembledcodeforfortheGameOverGUIscript:

@script ExecuteInEditMode()

var background : GUIStyle;var gameOverText : GUIStyle;var gameOverShadow : GUIStyle;

var gameOverScale = 1.5;var gameOverShadowScale = 1.5;

function OnGUI(){ GUI.Label ( Rect( (Screen.width - (Screen.height * 2)) * 0.75, 0, Screen.height * 2, Screen.height), "", background);

GUI.matrix = Matrix4x4.TRS(Vector3(0, 0, 0), Quaternion.identity, Vector3.one * gameOverShadowScale); GUI.Label ( Rect( (Screen.width / (2 * gameOverShadowScale)) - 150, (Screen.height / (2 * gameOverShadowScale)) - 40, 300, 100), "Game Over", gameOverShadow);

117

Page 118: 3D Platform Tutorial

GUI.matrix = Matrix4x4.TRS(Vector3(0, 0, 0), Quaternion.identity, Vector3.one * gameOverScale); GUI.Label ( Rect( (Screen.width / (2 * gameOverScale)) - 150, (Screen.height / (2 * gameOverScale)) - 40, 300, 100), "Game Over", gameOverText);}

GameOverScriptHereistheassembledcodefortheGameOverScriptscript:

function LateUpdate (){ if (!audio.isPlaying || Input.anyKeyDown) Application.LoadLevel("StartMenu");}

ThirdPersonStatusHereistheassembledcodefortheThirdPersonStatusscript:

// ThirdPersonStatus: Handles the player's state machine.// Keeps track of inventory, health, lives, etc.

var health : int = 6;var maxHealth : int = 6;var lives = 4;

// sound effects.var struckSound: AudioClip;var deathSound: AudioClip;

private var levelStateMachine : LevelStatus; // link to script that handles the level-complete sequence.

private var remainingItems : int; // total number to pick up on this level. Grabbed from LevelStatus.

function Awake(){ levelStateMachine = FindObjectOfType(LevelStatus); if (!levelStateMachine) Debug.Log("No link to Level Status"); remainingItems = levelStateMachine.itemsNeeded;}// Utility function used by HUD script:

118

Page 119: 3D Platform Tutorial

function GetRemainingItems() : int{ return remainingItems;}

function ApplyDamage (damage : int){ if (struckSound) AudioSource.PlayClipAtPoint(struckSound, transform.position); // play the 'player was struck' sound.

health -= damage; if (health <= 0) { SendMessage("Die"); }}

function AddLife (powerUp : int){ lives += powerUp; health = maxHealth;}

function AddHealth (powerUp : int){ health += powerUp; if (health>maxHealth) // We can only show six segments in our HUD. { health=maxHealth; } }

function FoundItem (numFound: int){ remainingItems-= numFound;

if (remainingItems == 0) { levelStateMachine.UnlockLevelExit(); // ...and let our player out of the level. }}

function FalloutDeath (){

119

Page 120: 3D Platform Tutorial

Die(); return;}

function Die (){ // play the death sound if available. if (deathSound) { AudioSource.PlayClipAtPoint(deathSound, transform.position); } lives--; health = maxHealth; if(lives < 0) Application.LoadLevel("GameOver"); // If we've reached here, the player still has lives remaining, so respawn. respawnPosition = Respawn.currentRespawn.transform.position; Camera.main.transform.position = respawnPosition - (transform.forward * 4) + Vector3.up; // reset camera too // Hide the player briefly to give the death sound time to finish... SendMessage("HidePlayer"); // Relocate the player. We need to do this or the camera will keep trying to focus on the (invisible) player where he's standing on top of the FalloutDeath box collider. transform.position = respawnPosition + Vector3.up;

yield WaitForSeconds(1.6); // give the sound time to complete. // (NOTE: "HidePlayer" also disables the player controls.)

SendMessage("ShowPlayer"); // Show the player again, ready for... // ... the respawn point to play it's particle effect Respawn.currentRespawn.FireEffect ();}

function LevelCompleted(){levelStateMachine.LevelCompleted();}

LevelStatusHereistheassembledcodefortheLevelStatusscript:

120

Page 121: 3D Platform Tutorial

// LevelStatus: Master level state machine script.var exitGateway: GameObject;var levelGoal: GameObject;var unlockedSound: AudioClip;var levelCompleteSound: AudioClip;var mainCamera: GameObject;var unlockedCamera: GameObject;var levelCompletedCamera: GameObject;

// This is where info like the number of items the player must collect in order to complete the level lives.

var itemsNeeded: int = 20; // This is how many fuel canisters the player must collect.

private var playerLink: GameObject;

// Awake(): Called by Unity when the script has loaded.// We use this function to initialise our link to the Lerpz GameObject.function Awake(){ levelGoal.GetComponent(MeshCollider).isTrigger = false; playerLink = GameObject.Find("Player"); if (!playerLink) Debug.Log("Could not get link to Lerpz"); levelGoal.GetComponent(MeshCollider).isTrigger = false; // make very sure of this!}

function UnlockLevelExit(){ mainCamera.GetComponent(AudioListener).enabled = false; unlockedCamera.active = true; unlockedCamera.GetComponent(AudioListener).enabled = true; exitGateway.GetComponent(AudioSource).Stop(); if (unlockedSound) { AudioSource.PlayClipAtPoint(unlockedSound, unlockedCamera.GetComponent(Transform).position, 2.0); } yield WaitForSeconds(1); exitGateway.active = false; // ... the fence goes down briefly... yield WaitForSeconds(0.2); //... pause for a fraction of a second... exitGateway.active = true; //... now the fence flashes back on again... yield WaitForSeconds(0.2); //... another brief pause before... exitGateway.active = false; //... the fence finally goes down forever! levelGoal.GetComponent(MeshCollider).isTrigger = true; yield WaitForSeconds(4); // give the player time to see the result.

121

Page 122: 3D Platform Tutorial

// swap the cameras back. unlockedCamera.active = false; // this lets the NearCamera get the screen all to itself. unlockedCamera.GetComponent(AudioListener).enabled = false; mainCamera.GetComponent(AudioListener).enabled = true;}

function LevelCompleted(){ mainCamera.GetComponent(AudioListener).enabled = false; levelCompletedCamera.active = true; levelCompletedCamera.GetComponent(AudioListener).enabled = true; playerLink.GetComponent(ThirdPersonController).SendMessage("HidePlayer"); playerLink.transform.position+=Vector3.up*500.0; // just move him 500 units if (levelCompleteSound) { AudioSource.PlayClipAtPoint(levelCompleteSound, levelGoal.transform.position, 2.0); } levelGoal.animation.Play(); yield WaitForSeconds (levelGoal.animation.clip.length); Application.LoadLevel("GameOver"); //...just show the Game Over sequence.}

HandleSpaceshipCollisionHereistheassembledcodefortheHandleSpaceshipCollisionscript:

function OnTriggerEnter (col : Collider){ playerLink=col.GetComponent(ThirdPersonStatus); if (!playerLink) // not the player. { return; } else { playerLink.LevelCompleted(); }}

122