ios 9 game development essentials -...
TRANSCRIPT
iOS9GameDevelopmentEssentials
TableofContents
iOS9GameDevelopmentEssentials
Credits
AbouttheAuthor
AbouttheReviewers
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmore
Whysubscribe?
FreeaccessforPacktaccountholders
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Conventions
Readerfeedback
Customersupport
Downloadingtheexamplecode
Downloadingthecolorimagesofthisbook
Errata
Piracy
Questions
1.TheSwiftProgrammingLanguage
HelloWorld!
Nomoresemicolons
Variables,constants,andprimitivedatatypes
Variables
Constants
Moreaboutconstants…
Arrays,matrices,sets,anddictionaries
Arrays
2Darrays/matrices
Sets
Dictionaries
Mutable/immutablecollections
Editing/accessingcollectiondata
Iteratingthroughcollectiontypes
Objective-CandSwiftcomparison
Objective-C
Swift
Charactersandstrings
StringInterpolation
Mutatingstrings
Stringindices
CommentinginSwift
Boolean
Ints,UInts,floats,anddoubles
Integersandunsignedintegers
Floatsanddoubles
ObjectsinSwift
Typesafetyandtypeinference
Optionals
Unwrappingoptionals
Optionalbindingandchaining
ControlflowinSwift
Ifstatements
Forloops
Do-whileloops
Switchstatements
Functionsandclasses
Functions
Tuples
Classes
Summary
2.StructuringandPlanningaGameUsingiOS9StoryboardsandSegues
Model-View-Controller
AniOSapp’slifecycle
Themain()function
TheUIApplicationclass/object
TheAppDelegateclass
Viewcontrollers
Viewcontrollertypes
Storyboardsandsegues
Segues
Storyboardsversuscoding
Summary
3.SpriteKitand2DGameDesign
AbriefhistoryofiOSgamedevelopmentengines
Thegameloop
Tilegame–SwiftSweeper
WhatisSwiftSweeper?
CreatingourSpriteKitgame
AnoverviewoftheSpriteKitstructureandobjects
Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfiles
AnSKTransitionexample
ASKScene/storyboardexample
SKScenetransitionswithSKSfiles
Assets,sprites,andicons
Spriteatlasesandanimatingsprites
Creatingourgamelogic
GameBoard
PuttingitalltogetherinGameScene.swift
DemoBots
Summary
4.SceneKitand3DGameDesign
SceneKitbasicsandworkingwithnodes
SpriteKit/SceneKitinteractivity
Theissuewithinheritance-basedstructuringandgamedesign
OurfirstSceneKitscene–theXcodetemplate
SceneKitprojectflowandstructure
SceneKitDebuggingOptions
HandlinguserinputinSceneKit
SceneKitfeaturesintroducediniOS9/Xcode7
Audionodesand3Dsound
Ambienceandmusic
SpriteKitscenetransitionsinSceneKit
Foxdemo
Particlesystems
Placingparticlesintoourpioscene
SpriteKit
SceneKit
IntroducingSceneKitandSpriteKitphysics
Visuallycomposedgamescenescgs
Summary
5.GameplayKit
Entitiesandcomponents
UsingGKEntityandGKComponentobjectsinourgames
Statemachines
Agents,goals,andbehaviors
Pathfinding
MinMaxAI
Randomsources
Rulesystems
Summary
6.ExhibittheMetalinYourGame
TheAppleMetalAPIandthegraphicspipeline
CPU/GPUframeworklevels
Graphicspipelineoverview
Whatareshaders?
Typesofshaders
WhyisMetalfasterthanOpenGLES?
ThebasicMetalobject/codestructure
Summary
7.PublishingOuriOSGame
Theeverchangingprocessofappsubmission
Beforesubmittingyourapp
PreparingourappsforiTunesConnect
Submittingyourappinthetesting/betaphase
CreatinganiTunesConnectapprecord
Updatingthebuildstring
Archiveandvalidateyourapp
UploadyourapptoiTunesConnect
BetatestyourgamewiththeTestFlightservice
Externaltesterinvites
Analyzingcrashreportsandfeedbackfromtesters
Submittingyourgameforreview
Updatingyourgame
Summary
8.TheFutureofiOSGameDevelopment
Agreaterfocusonfunctionalprogramming
TheAppleWatch
Component-basedstructuring
TheriseofVR
Summary
Index
iOS9GameDevelopmentEssentials
iOS9GameDevelopmentEssentialsCopyright©2015PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:October2015
Productionreference:1261015
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78439-143-0
www.packtpub.com
CoverimagebyChuckGaffney
CreditsAuthor
ChuckGaffney
Reviewers
MohitAthwani
RahulBorawar
ChadyKassouf
JoniMikkola
CommissioningEditor
JulianUrsell
AcquisitionEditors
VinayArgekar
SonaliVernekar
ContentDevelopmentEditor
ParitaKhedekar
TechnicalEditor
EdwinMoses
CopyEditor
DiptiMankame
ProjectCoordinator
JudieJose
Proofreader
SafisEditing
Indexer
HemanginiBari
Graphics
AbhinashSahu
ProductionCoordinator
NiteshThakur
CoverWork
NiteshThakur
AbouttheAuthorChuckGaffneyisaprogrammer,voiceactor,andgamedeveloper.Heownsacompany,Chuck’sAnimeShrine,andhasworkedforsomestudiosinNewYork.SomeofChuck’srecentprojectsincludeVRandUnityprojectsformajorstudiosandbusinessfirms,inadditiontoiOSandSwiftprogramming.
Iwouldliketothankmyfamilyforgivingmemyfirstvideogamesystemin1985attheyoungageoftwo.Thewonderandmysteryofcontrollingwhat’sonthescreenhasstuckwithmeeversince.Mostimportantly,Iwouldliketothankmyfiancée,Danielle,forkeepingmeonmytoeswiththisbookandunderstandingthetimeittooktocraftit.Thankyouforbeingtherethroughtheupsanddownsthatcomefrompursuingsteadyandworthwhileworkinthefieldofsoftwaredevelopment;particularlyduringthissummer,whenthingsfinallychangedafteryearsofdoingnothingbutseeminglystaringatcloseddoorsandcallingouttodeafears.
AbouttheReviewersMohitAthwaniisaself-taughtiOSdeveloperandhasbeendevelopingappssincetheearlydaysofiOS3.Hehasworkedwithseveralclientsallaroundtheworld,andhascarriedoutintenseresearchinthefieldoffacialdetectionandrecognitiononiOS.Hisapp,iRajanee,becamethenumberoneappontheIndianappstore,andhasfetchedhimtremendoussuccess.
MohitstartedhiscompanyGeeks(http://www.geeksincorporated.net)withafriendin2010,andhassincealsoinvolvedhimselfinconductingtrainingsessionsoniOSforstudentsandcorporates.Hiswebsite,http://indianios.guru/,hostsalotofintroductoryvideosandtutorialsondevelopingforiOSwithSwift.
IwouldliketothankmyparentsforgiftingmemyfirstMacBookandiPhonethatallowedmetobecomeaniOSdeveloper.Iwouldliketothankmyfriendsandeverybodywhohaveencouragedmetocomeupwithnewideasandconcepts,andPacktpublishingforgivingmetheopportunitytoreviewthisbook.
RahulBorawarisacomputersciencegraduatefromJodhpurInstituteofEngineeringandTechnologyinRajasthan,India.Heisapassionatesoftwarecraftsman,andlovesdesigningsoftwareformobiles.Fromthestart,hehasbeenamobileapplicationdevelopercreatingapplicationsforiOSandAndroidplatforms.Hehasbeenworkingonmobiledevelopmentsince2011andhaspublishedmanyappsontheappstore,suchasCatchtheAliens(a2Dlevel-basedgame)andDrawYourStories(akids’appforcreatingfableswithdrawingstuffs).Heiscurrentlyworkingasasoftwaredevelopmentengineerinthemobileteamforarealestateproduct-basedcompany(CommonFloor).HehasreviewedonemorebookforPacktPublications,namediOSGameProgrammingCookbook,whichcanbeviewedathttps://www.packtpub.com/game-development/ios-game-programming-cookbook.
YoucanfollowhimonTwitter(https://twitter.com/RahulBorawar)andonGitHub(https://github.com/Rahul2389).Youcanalsocheckhiswebsite,http://rahulborawar.branded.me.
Firstofall,IwouldliketothankPacktPublishingforgivingmetheopportunitytoreviewthistechnology-richcookbookandenlightenmyiOSapplicationdevelopmentskills.Secondly,thankstomyfamilyforsupportingmeinmycareerasatechnicalguy.
ChadyKassoufisanindependentiOSandwebdevelopmentexpert.Hestartedprogramming23yearsagoandhasn’tstoppedsince.
Sevenyearsago,hedecidedtoleavehisjobasateamleaderinoneoftheleadingdigitalagenciesandstarthisownbusiness.
Hisinterests,besidescomputers,includearts,music,andfitness.Hecanbefoundonlineathttp://chady.net/.
JoniMikkolaiscurrentlyworkingonhisnextmobilegameinnorthernFinland.Hekeepshisgamedevelopingstaminaupbytrainingregularlyatthegymandeatinghealthily.
Amongdevelopinggames,heoftenreadsbooks,playsthepiano,orbakesbuns,tokeephisideasflowingandhismindfocused.Heconstantlykeepschallengingthestatusquo,which,inturn,helpsinlearningnewwaystocreatethings.
Hehasdevelopedgamesprofessionallyforover4yearsmostlyformobileplatforms.Hetargetscasualgamesandfocusesoncreatingsimplisticdesigns.Withonegamereleased,heiscurrentlyworkingonhisnextgame,whichwillbereleasedinlate2015forAndroidandiOSplatforms.
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmoreForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwithusat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.
https://www2.packtpub.com/books/subscription/packtlib
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigitalbooklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.
Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser
FreeaccessforPacktaccountholdersIfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccessPacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsforimmediateaccess.
PrefaceSincetheintroductionofiOS8in2014,gamedevelopmentfortheiOSplatformhasgonethroughsomemajorchanges.ThefirstofthosechangeswastheintroductionoftheSwiftprogramminglanguage,afunctionalprogramminglanguagemadebyAppletobesimpletocodeandmoderninitscapabilities,allwhilebeingfastenoughtohandlemodernappandgamedevelopment.iOS8alsointroducedthe3Dgamedevelopmentframework,SceneKit.SceneKitalloweddeveloperstoquicklydesign3Dgamesandworkwith3DassetsinasimilarfashiontoiOS7’sSpriteKit.Ayearlater,inthesummerof2015,iOS9wasintroduced,alongwithanewsetoftoolsforbothseasonedandbrandnewiOSgamedevelopers.Thenewframework,GameplayKit,letsdevelopershandlethegamerules,AI,gameentities,andgamestatesseparatelyfromtheinheritancelogic.InadditiontoGameplayKit,AppleshowedofftheFoxgameexamplethatdisplayshowXcodecannowdomuchofthesamevisualeditingfunctionalitiesseeninmultiplatformgameengines,suchasUnityandUnrealEngine.
WewillfirstbecomefamiliarwiththeSwiftprogramminglanguageandhowitcanbeusedinthescopeofgamedevelopment.ThegoalistounderstandiOSgamedevelopmentfromthegroundup,learningthetoughercode-centricmethodologyofmakingagame.InadditiontotakingalookatApple’sownexampleprojectsforthevariousiOSgameframeworks,wewillseesomecodeexamplesfromtwogames,thefirstgamebeingapublishedSwift-developed2DscrollinggamenamedPikiPopandtheotherbeingatile-basedMinesweeperclonenamedSwiftSweeper.Asweprogressthroughthebook,wewillstillkeepcodeasthecoreofourmethodforgamedevelopment,butwilllookintothevisualtoolsintroducediniOS9that,inadditiontoGameplayKitandthecomponent-basedstructuring,canallowustocreateagamethatisonlylimitedbyourimagination.Wethendiveintotopicofthelow-levelAPIs,suchasOpenGLESandMetal,fordeveloperswhowishtogetdowndirectlytotheGPU.
Intheend,wehopethatyouunderstandhowiOScontinuestobeapowergamedevelopmentplatform,whetheryouareadeveloperwhocomesfromthetraditionalcode-centricschoolofcomputerscience,oryouareapartofthegrowingvisual-based/drag-and-dropdesignparadigm.Ourgoalisthatwhenyouarefinishedwiththisbook,youwillhaveanumberofvastlydifferentanddetailedgameideas,whichyouthencanimmediatelybeginbuildingwithSwiftandtheiOS9platforms.
WhatthisbookcoversChapter1,TheSwiftProgrammingLanguage,providesanintroductionandguidancetotheSwiftprogramminglanguage.
Chapter2,StructuringandPlanningaGameUsingStoryboardsandSegues,helpsreadersknowthebasicflowofaniOSappbymakinguseofstoryboardsandseguesinordertoeitherplanorpreplaniOSgames.
Chapter3,SpriteKitand2DGameDesign,introducesandexplainstheuseoftheiOS2Dgamedevelopmentframework,SpriteKit.
Chapter4,SceneKitand3DGameDesign,helpsreaderslookintotheiOS3Ddevelopmentframework,SceneKit,andvisualtoolsintroducediniOSthatcanbeusedforbothSceneKitandSpriteKit.
Chapter5,Gameplaykit,introducestheGameplayKitframework,auniversalgamelogicandAIframeworkintroducediniOS9.
Chapter6,ExhibittheMetalinyourGame,discussesaboutadvancedtopics,suchastheGPUgraphicspipelineandanintroductiontoApple’slow-levelAPI,Metal,forgettingthebestperformanceoutofyourgame.
Chapter7,PublishingOuriOSGame,explainsthestepsneededtobetatestandpublishiOSgamesbymakinguseofthetestingandqualityassuranceservice,TestFlight.
Chapter8,TheFutureofiOSGameDevelopment,discusseshowprogramming,iOS,andgamedevelopmentasawholemightchangeorimproveinthenearfuture,andhowitrelatestothemostrecentiOSplatformsandframeworks.
WhatyouneedforthisbookHere’swhatyouneedforthisbook:
Xcode7orlaterMacOSXYosemiteorlater
WhothisbookisforThisbookisintendedforgamedeveloperswhowishtodevelop2Dand3DgamesforiPhoneandiPad.IfyouareadeveloperfromanotherplatformoragameenginesuchasAndroidorUnity,acurrentiOSdeveloperwishingtolearnmoreaboutSwiftandthelatestfeaturesofiOS9,orevenifyouarenewtogamedevelopment,thenthisbookisforyou.Somepriorprogrammingknowledgeisrecommended,butnotrequired.
ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.
Codewordsintext,filenames,fileextensions,pathnames,anduserinput,areshownasfollows:“Aswecansee,theAppDelegate.swiftandtheViewController.swiftfileswereautomaticallycreatedforusandrightbelowthat,we’dfindtheMain.Storyboardfile.”
Ablockofcodeissetasfollows:
letscore=player.score
varscoreCountNum=0
do{
HUD.scoreText=String(scoreCountNum)
scoreCountNum=scoreCountNum*2
}whilescoreCountNum<score
Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:
//Collisionbitmasks
typedefNS_OPTIONS(NSUInteger,AAPLBitmask){
AAPLBitmaskCollision=1UL<<2,
AAPLBitmaskCollectable=1UL<<3,
AAPLBitmaskEnemy=1UL<<4,
AAPLBitmaskSuperCollectable=1UL<<5,
AAPLBitmaskWater=1UL<<6
};
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:”Alternately,imaginethattheplayerlostalloftheirlivesandgotaGameOvermessage.”
NoteWarningsorimportantnotesappearinaboxlikethis.
TipTipsandtricksappearlikethis.
ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook—whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.
Tosendusgeneralfeedback,simplye-mail<[email protected]>,andmentionthebook’stitleinthesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.
CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.
DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesfromyouraccountathttp://www.packtpub.comforallthePacktPublishingbooksyouhavepurchased.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
DownloadingthecolorimagesofthisbookWealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagramsusedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesintheoutput.Youcandownloadthisfilefromhttp://www.packtpub.com/sites/default/files/downloads/iOS9_GameDevelopmentEssentials_ColorImages.pdf
ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthecode—wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.
Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.
PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpiratedmaterial.
Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.
QuestionsIfyouhaveaproblemwithanyaspectofthisbook,youcancontactusat<[email protected]>,andwewilldoourbesttoaddresstheproblem.
Chapter1.TheSwiftProgrammingLanguageAtthecoreofgamedevelopmentisyourgame’scode.Itisthebrainofyourprojectandoutsideoftheart,sound,andvariousassetdevelopments.Itiswhereyouwillspendmostofyourtimecreatingandtestingyourgame.UpuntilApple’sWorldwideDevelopersConferenceWWDC14inJuneof2014,thecodeofchoiceforiOSgameandappdevelopmentwasObjective-C.AtWWDC14,anewandfasterprogramminglanguage,Swift,wasannouncedandisnowtherecommendedlanguageforallcurrentandfutureiOSgamesandgeneralappcreation.
Asofthetimeofwriting,youcanstilluseObjective-Ctodesignyourgames,butprogrammers,both,newandseasoned,willseewhywritinginSwiftisnotonlyeasierforexpressingyourgame’slogic,butevenmorepreformat.Keepingyourgamerunningatthatcritical60fpsisdependentonfastcodeandlogic.EngineersatAppledevelopedtheSwiftprogramminglanguagefromthegroundupwithperformanceandreadabilityinmind,sothislanguagecanexecutecertaincodeiterationsfasterthanObjective-Cwhilealsokeepingcodeambiguitytoaminimum.Swiftalsousesmanyofthemethodologiesandsyntaxesfoundinmoremodernlanguages,suchasScala,JavaScript,Ruby,andPython.
So,let’sdiveintotheSwiftlanguage.
NoteItisrecommendedthatsomebasicknowledgeofobject-orientedprogramming(OOP)beknownpreviously,butwewilltrytokeepthebuild-upandexplanationofcodesimpleandeasytofollowaswemoveontothemoreadvancedtopicsrelatedtogamedevelopment.
HelloWorld!It’ssomewhattraditionalwhenlearningprogramminglanguagestobeginwithaHelloWorldexample.AHelloWorldprogramissimplyusingyourcodetodisplayorlogthetextHelloWorld.It’salwaysbeenthegeneralstartingpointbecausesometimesjustgettingyourcodeenvironmentsetupandhavingyourcodeexecutingcorrectlyishalfthebattle.Atleast,thiswasmorethecaseinolderprogramminglanguages.
Swiftmakesthiseasierthanever,andwithoutgoingintothestructureofaSwiftfile(whichweshalldolateronandisalsomucheasierthanObjective-Candpastlanguages),here’showyoucreateaHelloWorldprogram:
print("Hello,World!")
That’sit!ThatisallyouneedtohavethetextHelloWorldappearinXcode’sdebugareaoutput.
NomoresemicolonsThoseofuswhohavebeenprogrammingforsometimemightnotethattheusuallyall-importantsemicolon(;)ismissing.Thisisn’tamistake;inSwift,wedon’thavetouseasemicolontomarktheendofanexpression.Wecanifwe’dlike,andsomeofusmightstilldoitasaforceofhabit,butSwifthasomittedthatcommonconcern.
NoteTheuseofthesemicolontomarktheendofanexpressionstemsfromtheearliestdaysofprogrammingwhencodewaswritteninsimplewordprocessorsandneededaspecialcharactertorepresentwhenthecode’sexpressionendsandthenextbegins.
Variables,constants,andprimitivedatatypesWhileprogramminganyapplication,eitherifnewtoprogrammingortryingtolearnadifferentlanguage,firstweshouldgetanunderstandingofhowalanguagehandlesvariables,constants,andvariousdatatypes,suchasBooleans,integers,floats,strings,andarrays.Youcanthinkofthedatainyourprogramasboxesorcontainersofinformation.Thosecontainerscanbeofdifferentflavorsortypes.Throughoutthelifeofyourgame,thedatacanchange(variables,objects,andsoon)ortheycanstaythesame.
Forexample,thenumberoflivesaplayerhaswouldbestoredasavariable,asthatisexpectedtochangeduringthecourseofthegame.Thatvariablewouldthenbeoftheprimitivedatatypeinteger,whichisbasicallywholenumbers.Datathatstores,say,thenameofacertainweaponorpower-upinyourgame,wouldbestoredinwhat’sknownasaconstant,asthenameofthatitemisnevergoingtochange.Inagamewheretheplayercanhaveinterchangeableweaponsorpower-ups,thebestwaytorepresentthecurrentlyequippeditemwouldbetouseavariable.Avariableisapieceofdatathatisboundtochange.Thatweaponorpower-upwillalsomostlikelyhaveabitmoreinformationtoitthanjustanameornumber;theprimitivetypeswementionedprior.Thecurrentlyequippeditemwouldbemadeupofproperties,suchasitsname,power,effects,indexnumber,andthespriteor3Dmodelthatvisuallyrepresentsit.Thus,thecurrentlyequippeditemwouldn’tjustbeavariableofaprimitivedatatype,butbewhatisknownasatypeofobject.Objectsinprogrammingcanholdanumberofpropertiesandfunctionalitiesthatcanbethoughtofasablackboxofbothfunctionandinformation.Thecurrentlyequippediteminourcasewouldbeasortofplaceholderthatcanholdanitemofthattypeandinterchangeitwhenneeded,fulfillingitspurposeasareplaceableitem.
NoteSwiftiswhat’sknownasatype-safelanguage,soweshouldkeeptrackoftheexacttypeofdataandevenit’sfutureusage(thatis,ifthedataisorwillbeNULL),asit’sveryimportantwhenworkingwithSwiftcomparedwithotherlanguages.ApplemadeSwiftbehavethiswaytohelpkeepruntimeerrorsandbugsinyourapplicationstoaminimum,sowecanfindthemmuchearlierinthedevelopmentprocess.
VariablesLet’slookathowvariablesaredeclaredinSwift.
varlives=3//variableofrepresentingtheplayer'slives
lives=1//changesthatvariabletoavalueof1
ThoseofuswhohavebeendevelopinginJavaScriptwillfeelrightathomehere.LikeJavaScript,weusethekeywordvartorepresentavariable,andwenamedthevariablelives.
Thecompilerimplicitlyknowsthatthetypeofthisvariableisawholenumber,theprimitivedatatypeInt.
Thetypecanbeexplicitlydeclaredassuch:
varlives:Int=3//variableoftypeInt
Wecanalsorepresentlivesasthefloatingpointdatatypesdoubleorfloat,asfollows:
//livesarerepresentedhereas3.0insteadof3
varlives:Double=3//oftypeDouble
varlives:Float=3//oftypeFloat
Usingacolonafterthevariable’snamedeclarationallowsustoexplicitlytypecastthevariable.
ConstantsDuringyourgame,therewillbepointsofdatathatdon’tchangethroughoutthelifeofthegameorthegame’scurrentlevel/scene.Thesecanbevariousdata,suchasgravity,atextlabelintheHeads-UpDisplay(HUD),thecenterpointofcharacter’s2Danimation,aneventdeclaration,orthetimebeforeyourgamechecksfornewtouches/swipes.
Declaringconstantsisalmostthesameasdeclaringvariables.
Usingacolonafterthevariable’snamedeclarationallowsustoexplicitlytypecastthevariable.
letgravityImplicit=-9.8//implicitdeclaration
letgravityExplicit:Float=-9.8//explicitdeclaration
Aswecansee,weusethekeywordlettodeclareconstants.
Here’sanotherexampleusingastringthatcouldrepresentamessagedisplayedonthescreenatthestartorendofastage:
letstageMessage="Start!"
stageMessage="YouLose!"//error
SincethestringstageMessageisaconstant,wecannotchangeitonceithasbeendeclared.Somethinglikethiswouldbebetterasavariableusingvarinsteadoflet.
Tip“Whydon’twedeclareeverythingasavariable?”
Thisisaquestionsometimesaskedbynewdevelopersandisunderstandablewhyit’sasked,especiallysincegameappstendtohavealargenumberofvariablesandmoreinterchangeablestatesthananaverageapplication.Whenthecompilerisbuildingitsinternallistofyourgame’sobjectsanddata,moregoesonbehindthesceneswithvariablesthanwithconstants.
Withoutgettingtoomuchintotopics,suchastheprogram’sstackandotherdetails,inshort,havingobjects,events,anddatadeclaredasconstantswiththeletkeywordismoreefficientthanvar.Inasmallapponthenewestdevicestoday,thoughnotrecommended,wecouldpossiblygetawaywiththiswithoutseeingagreatdealoflossinappperformance.Whenitcomestovideogames,however,performanceiscritical.Buyingbackasmuchperformanceaspossiblecanallowabetterplayerexperience.Applerecommendsthatwhenindoubt,alwaysuseletatthetimeofdeclarationandhavethecompilersaywhentochangetovar.
Moreaboutconstants…AsofSwiftversion1.2,constantscanhaveaconditionallycontrolledinitialvalue.
Priortothisupdate,wehadtoinitializeaconstantwithasinglestartingvalueorbeforcedtomakethepropertyavariable.InXcode6.3andnewer,wecanperformthefollowinglogic:
letx:SomeThing
ifcondition
{
x=foo()
}
else
{
x=bar()
}
use(x)
Anexampleofthisinagamecouldbeasfollows:
letstageBoss:Boss
if(stageDifficulty==gameDifficulty.hard)
{
stageBoss=Boss.toughBoss()
}
else
{
stageBoss=Boss.normalBoss()
}
loadBoss(stageBoss)
Withthisfunctionality,aconstant’sinitializationcanhavealayerofvariancewhilestillkeepingitunchangeable,orimmutablethroughitsuse.Here,theconstantstageBosscanbeoneoftwotypesbasedonthegame’sdifficulty:Boss.toughBoss()orBoss.normalBoss().Thebosswon’tchangeforthecourseofthisstage,soitmakessensetokeepitasaconstant.Moreonifandelsestatementsiscoveredlaterinthechapter.
Arrays,matrices,sets,anddictionariesVariablesandconstantscanrepresentacollectionofvariouspropertiesandobjects.Themostcommoncollectiontypesarearrays,matrices,sets,anddictionaries.Anarrayisanorderedlistofdistinctobjects;amatrixis,inshort,anarrayofarrays;asetisanunorderedlistofdistinctobjects;andadictionaryisanunorderedlistthatutilizesakey:valueassociationwiththedata.
ArraysHere’sanexampleofanarrayinSwift:
letstageNames:[String]=["DowntownTokyo","HeavenValley","Nether"]
TheobjectstageNamesisacollectionofstringsrepresentingthenamesofagame’sstages.Arraysareorderedbysubscriptsfrom0toarraylength-1.So,stageNames[0]wouldbeDowntownTokyo;stageNames[2]wouldbeNether;andstageNames[4]wouldgiveanerrorsincethat’sbeyondthelimitsofthearrayanddoesn’texist.Weuse[]bracketsaroundtheclasstypeofstageNames,[String],totellthecompilerthatwearedealingwithanarrayofstrings.Bracketsarealsousedaroundtheindividualmembersofthisarray.
2Darrays/matricesAcommoncollectiontypeusedinphysicscalculations,graphics,andgamedesign,particularlygrid-basedpuzzlegames,istwo-dimensionalarrays/matrices.2Darraysaresimplyarraysthathavearraysastheirmembers.Thesearrayscanbeexpressedinarectangularfashioninrowsandcolumns.
Forexample,the4x4(4rows,4columns)tileboardinthe15-puzzlegamecanberepresentedasfollows:
vartileBoard=[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,""]]
Inthe15puzzlegame,yourgoalistoshiftthetilesusingtheoneemptyspot(representedwiththeblankstring""),toallendupinthe1-15orderaswesaw.Thegamewouldstartwiththenumbersarrangedinarandomandsolvableorder,andtheplayerwouldthenhavetoswapthenumbersandtheblankspace.
TipTobetterperformvariousactionsonand/orstoreinformationabouteachtileinthe15game(andothergames),it’dbebettertocreateatileobjectasopposedtousingrawvaluesseenhere.Forthesakeofunderstandingwhatamatrixor2Darrayis,simplymakeanoteofhowthearrayissurroundedbydoublyencapsulatedbrackets[[]].Wewilllateruseoneofourexamplegames,SwiftSweeper,tobetterunderstandhowpuzzlegamesuse2Darraysofobjectstocreateafullgame.
Herearewaystodeclareblank2Darrayswithstricttypes:
vartwoDTileArray:[[Tiles]]=[]//blank2Darrayoftype,Tiles
varanotherArray=Array<Array<Tile>>()//samearray,usingGenerics
ThevariabletwoDTileArrayusesthedoublebrackets[[Tiles]]todeclareitasablank2Darray/matrixforthemade-uptype,tiles.ThevariableanotherArrayisaratheroddlydeclaredarraythatusesanglebracketcharacters<>forenclosures.Itutilizeswhat’sknownasGenerics.Genericsisaratheradvancedtopicthatwewilltouchmoreonlater.Theyallowveryflexiblefunctionalityamongawidearrayofdatatypesandclasses.Forthemoment,wecanthinkofthemasacatch-allwayofworkingwithobjects.
Tofillinthedataforeitherversionofthisarray,wewouldthenusefor-loops.Moreonloopsanditerationswillbeexplainedlaterinthechapter.
SetsThisishowwewouldmakeasetofvariousgameitemsinSwift:
varkeyItems=Set([Dungeon_Prize,Holy_Armor,Boss_Key,"A"])
ThissetkeyItemshasvariousobjectsandacharacterA.Unlikeanarray,asetisnotorderedandcontainsuniqueitems.So,unlikestageNames,attemptingtogetkeyItems[1]wouldreturnanerroranditems[1]mightnotnecessarilybetheHoly_Armorobject,astheplacementofobjectsisinternallyrandominaset.Theadvantagesetshaveoverarraysisthatsetsaregreatatcheckingforduplicatedobjectsandspecificcontentsearchinginthecollectionoverall.Setsmakeuseofhashingtopinpointtheiteminthecollections,socheckingforitemsinaset’scontentcanbemuchfasterthaninanarray.Ingamedevelopment,agame’skeyitems,whichtheplayermayonlygetonceandshouldneverhaveduplicatesof,couldworkgreatasaset.UsingthefunctionkeyItems.contains(Boss_Key)returnstheBooleanvalueoftrueinthiscase.
NoteSetswereaddedinSwift1.2andXcode6.3.TheirclassisrepresentedbythegenerictypeSet<T>,whereTistheclasstypeofthecollection.Inotherwords,theset,Set([45,66,1233,234]).wouldbeofthetypeSet<Int>,andourexampleherewouldbeaSet<NSObject>instanceduetoithavingacollectionofvariousdatatypes.
WewilldiscussmoreonGenericsandclasshierarchylaterinthischapter.
DictionariesAdictionarycanberepresentedthiswayinSwift:
varplayerInventory:[Int:String]=[1:"BusterSword",43:
"Potion",22:"StrengthBooster"]
Dictionariesuseakey:valueassociation,soplayerInventory[22]returnsthevalueStrengthBoosterbasedonthekey22.Boththekeyandvaluecouldbeinitializedtoalmostanyclasstype*.Inadditiontotheinventoryexamplegiven,wecanhavethefollowingcode:
varstageReward:[Int:GameItem]=[:]//blankinitialization
//useoftheDictionaryattheendofacurrentstage
stageReward=[currentStage.score:currentStage.rewardItem]
Note*Thevaluesofadictionary,thoughratherflexibleinSwift,dohavelimitations.Thekeymustconformtowhat’sknownasthehashableprotocol.Basicdatatypes,suchasIntandString,alreadyhavethisfunctionality.So,ifyouaretomakeyourownclasses/datastructuresthataretobeusedindictionaries,saymappingaplayeractionswithplayerinput,thisprotocolmustbeutilizedfirst.Wewilldiscussmoreaboutprotocolslaterinthischapter.
Dictionariesarelikesetsinthattheyareunorderedbutwiththeadditionallayerofhavingakeyandavalueassociatedwiththeircontentinsteadofjustthehashkey.Likesets,dictionariesaregreatforquickinsertionandretrievalofspecificdata.IniOSappsandinwebapplications,dictionariesareusedtoparseandselectitemsfromJavaScriptObjectNotation(JSON)data.
Intherealmofgamedevelopment,dictionariesusingJSONorviaApple’sinternaldataclass,NSUserDefaults,canbeusedtosaveandloadgamedata,setupgameconfigurations,oraccessspecificmembersofagame’sAPI.
Forexample,here’sonewaytosaveaplayer’shighscoreinaniOSgameusingSwift:
letnewBestScore:Void=
NSUserDefaults.standardUserDefaults().setInteger(bestScore,forKey:
"bestScore")
ThiscodecomesdirectlyfromapublishedSwift-developedgamenamedPikiPop,whichwewillusefromtimetotimetoshowcodeusedinactualgameapplications.
Again,notethatdictionariesareunordered,butSwifthaswaystoiterateorsearchthroughanentiredictionary.Wewillgomoreindepthinthenextsectionandlateronwhenwemoveontoloopsandcontrolflow.
Mutable/immutablecollectionsOneratherimportantdiscussionthatwe’veleftoutishowtosubtract,edit,oraddtoarrays,sets,anddictionaries.However,beforewedothis,youshouldunderstandtheconceptofmutableandimmutabledata/collections.
Amutablecollectionissimpledatathatcanbechanged,addedto,orsubtractedfrom,whereasanimmutablecollectioncannotbechanged,addedto,orsubtractedfrom.
ToworkwithmutableandimmutablecollectionsefficientlyinObjective-C,wehadtoexplicitlystatethemutabilityofthecollectionbeforehand.Forexample,anarrayofthetypeNSArrayinObjective-Cisalwaysimmutable.TherearemethodswecancallonNSArraythatwouldeditthecollection,butbehindthescenes,thiswouldbecreatingbrandnewNSArrayobjects,thuswouldberatherinefficienttodothisofteninthelifeofourgame.Objective-Chassolvedthisissuewiththeclasstype,NSMutableArray.
ThankstotheflexibilityofSwift’stypeinference,wealreadyknowhowtomakeacollectionmutableorimmutable!TheconceptofconstantsandvariableshasuscoveredwhenitcomestodatamutabilityinSwift.Usingthekeywordletwhencreatingacollectionwillmakethatcollectionimmutable,whileusingvarwillinitializeitasamutablecollection.
//mutableArray
varunlockedLevels:[Int]=[1,2,5,8]
//immutableDictionary
letplayersForThisRound:[PlayerNumber:PlayerUserName]=
[453:"userName3344xx5",233:"princeTrunks",6567:"noScopeMan98",211:
"egoDino"]
Thearrayofintegers,unlockedLevels,canbeeditedsimplybecauseit’savariable.TheimmutabledictionaryplayersForThisRoundcan’tbechangedsinceit’salreadybeendeclaredasaconstant.Thereisnoadditionallayerofambiguityconcerningadditionalclasstypes.
Editing/accessingcollectiondataAslongasacollectiontypeisavariable,usingthevarkeyword,wecandovariouseditstothedata.Let’sgobacktoourunlockedLevelsarray.Manygameshavethefunctionalityofunlockinglevelsastheplayerprogresses.Let’ssaythattheplayerhasreachedthehighscoreneededtounlockthepreviouslylockedlevel3(as3isn’tamemberofthearray).Wecanadd3tothearrayusingtheappendfunction:
unlockedLevels.append(3)
AnotherneatattributeofSwiftisthatwecanadddatatoanarrayusingthe+=assignmentoperator:
unlockedLevels+=[3]
Doingitthiswayhoweverwillsimplyadd3totheendofthearray.So,ourpreviousarray[1,2,5,8]isnow[1,2,5,8,3].Thisprobablyisn’tadesirableorder,sotoinsertthenumber3inthethirdspot,unlockedLevels[2],wecanusethefollowingmethod:
unlockedLevels.insert(3,atIndex:2)
Now,ourarrayofunlockedlevelsisorderedto[1,2,3,5,8].
Thisisassumingthoughthatweknowamemberofthearraypriorto3issortedalready.TherearevarioussortingfunctionalitiesprovidedbySwiftthatcouldhelpkeepinganarraysorted.Wewillleavethedetailsofsortingtoourdiscussionsofloopsandcontrolflowlaterinthischapter.
Removingitemsfromanarrayisjustsimple.Let’sagainuseourunlockedLevelsarray.Imaginethatourgamehasanoverworldfortheplayertotraveltoandfromandtheplayerhasjustunlockedasecretthattriggeredaneventthatblockedoffaccesstolevel1.Level1wouldnowhavetoberemovedfromtheunlockedlevels.Wecandoitlikethis:
unlockedLevels.removeAtIndex(0)//arrayisnow[2,3,5,8]
Alternately,imaginethattheplayerhaslostalloftheirlivesandgotaGameOvermessage.Apenaltyforthiscouldbetolockthefurthestlevel.Thoughprobablyaratherinfuriatingmethodandusknowingthatlevel8isthefurthestlevelinourarray,wecanremoveitusingthe.removeLast()functionofarraytypes.
unlockedLevels.removeLast()//arrayisnow[2,3,5]
NoteThisisassumingthatweknowtheexactorderofthecollection.Setsordictionariesmightbebetteratcontrollingcertainaspectsofyourgame.
Herearesomewaystoeditasetoradictionaryasaquickguide.
Set
inventory.insert("PowerRing")//.insert()addsitemstoaset
inventory.remove("MagicPotion")//.remove()removesaspecificitem
inventory.count//counts#ofitemsintheSet
inventory.union(EnemyLoot)//combinestwoSets
inventory.removeAll()//removeseverythingfromtheSet
inventory.isEmpty//returnstrue
Dictionary
varinventory=[Float:String]()//createsamutabledictionary
/*
onewaytosetanequippedweaponinagame;where1.0couldrepresentthe
first"itemslot"thatwouldbeplaceholderfortheplayer's"current
weapon"
*/
inventory.updateValue("Broadsword",forKey:1.0)
//removesanitemfromaDictionarybasedonthekeyvalue
inventory.removeValueForKey("StatusBooster")
inventory.count//countsitemsintheDictionary
inventory.removeAll(keepCapacity:false)//deletestheDictionary
inventory.isEmpty//returnsfalse
//createsanarrayoftheDictionary'svalues
letinventoryNames=[String](inventory.values)
//createsanarrayoftheDictionary'skeys
letinventoryKeys=[String](inventory.keys)
IteratingthroughcollectiontypesWecan’tdiscusscollectiontypeswithoutmentioninghowtoiteratethroughthemenmasse.
Here’ssomewaywe’diteratethoughanarray,aset,oradictionaryinSwift:
//(a)outputseveryitemthroughtheentirecollection
//worksforArrays,SetsandDictionariesbutoutputwillvary
foritemininventory{
print(item)
}
//(b)outputssorteditemlistusingSwift'ssorted()function
//worksforSets
foriteminsorted(inventory){
print("\(item)")
}
//(c)outputseveryitemaswellasitscurrentindex
//worksforArrays,SetsandDictionaries
for(index,value)inenumerate(inventory){
print("Item\(index+1):\(value)")
}
//(d)
//IteratethroughandthroughthekeysofaDictionary
foritemCodeininventory.keys{
print("Itemcode:\(itemCode)")
}
//(e)
//IteratethroughandthroughthevaluesofaDictionary
foritemNameininventory.values{
print("Itemname:\(itemName)")
}
Asstatedpreviously,thisisdonewithwhat’sknownasafor-loop;withtheseexamples,weshowhowSwiftutilizesthefor-invariationusingtheinkeyword.Thecodewillrepeatuntilitreachestheendofthecollectioninalloftheseexamples.Inexample(c),wealsoseetheuseoftheSwiftfunction,enumerate().Thisfunctionreturnsacompoundvalue,(index,value),foreachitem.Thiscompoundvalueisknownasatuple,andSwift’suseoftuplesmakesforawidevarietyoffunctionalitiesforfunctions,loops,aswellascodeblocks.
Wewilldelvemoreintotuples,loops,andblockslateron.
Objective-CandSwiftcomparisonHere’saquickreviewofourSwiftcodewithacomparisontotheObjective-Cequivalent.
Objective-CHere’sasamplecodeinObjective-C:
constintMAX_ENEMIES=10;//constant
floatplayerPower=1.3;//variable
//ArrayofNSStrings
NSArray*stageNames=@[@"DowntownTokyo",@"HeavenValley",@"Nether"];
//SetofvariousNSObjects
NSSet*items=[NSSetsetWithObjects:Weapons,Armor,
HealingItems,"A",nil];
//DictionarywithanInt:Stringkey:value
NSDictionary*inventory=[NSDictionarydictionaryWithObjectsAndKeys:
[NSNumbernumberWithInt:1],@"BusterSword",
[NSNumbernumberWithInt:43],@"Potion",
[NSNumbernumberWithInt:22],@"Strength",
nil];
SwiftHere’stheequivalentcodeinSwift:
letMAX_ENEMIES=10//constant
varplayerPower=1.3//variable
//ArrayofStrings
letstageNames:[String]=["DowntownTokyo","HeavenValley","Nether"]
//SetofvariousNSObjects
varitems=Set([Weapons,Armor,HealingItems,"A"])
//DictionarywithanInt:Stringkey:value
varplayerInventory:[Int:String]=[1:"BusterSword",43:
"Potion",22:"StrengthBooster"]
Intheprecedingcode,weusedsomeexamplesofvariables,constants,arrays,sets,anddictionaries.First,weseetheirObjective-CsyntaxandthentheequivalentdeclarationsusingSwift’ssyntax.Fromthisexample,wecanseehowcompactSwiftiscomparedwithObjective-C.
CharactersandstringsForsometimeinthischapter,we’vebeenmentioningstrings.Stringsarealsoacollectionofdatatypes,butaspeciallydealtcollectionofcharacters,oftheclasstype,string.SwiftisUnicode-compliant,sowecanhavestringslikethefollowing:
letgameOverText="GameOver!"
Wecanhavestringswithemojicharacterslikethefollowing:
letcardSuits="♠♥♣♦"
Whatwedidintheprecedingcodewascreatewhat’sknownasastringliteral.Astringliteraliswhenweexplicitlydefineastringaroundtwoquotes””.
Wecancreateemptystringvariablesforlateruseinourgamessuchas:
varemptyString=""//emptystringliteral
varanotherEmptyString=String()//usingtypeinitializer
Botharevalidwaystocreateanemptystring””.
StringInterpolationWecanalsocreateastringfromamixtureofotherdatatypes,knownasStringInterpolation.StringInterpolationisrathercommoningamedevelopment,debugging,andstringuseingeneral.
Themostnotableofexamplesaredisplayingtheplayer’sscoreandlives.Thisishowoneofourexamplegames,PikiPop,usesStringInterpolationtodisplaythecurrentplayerstats:
//displaystheplayer'scurrentlives
varlivesLabel="x\(currentScene.player!.lives)"
//displaystheplayer'scurrentscore
varscoreText="Score:\(score)"
Takenoteofthe\(variable_name)formatting.We’veactuallyseenthisbeforeinourpastcodesnippets.Inthevariousprint()outputs,weusedthistodisplaythevariable,collection,andsoonwewantedtogetinformationon.InSwift,thewaytooutputthevalueofadatatypeinastringisbyusingthisformatting.
ForthoseofuswhocamefromObjective-C,it’sthesameasthefollowing:
NSString*livesLabel=@"Lives:";
intlives=3;
NSString*livesText=[NSStringstringWithFormat:@"%@(%ddaysago)",
livesLabel,lives];
NotehowSwiftmakesStringInterpolationmuchcleanerandeasiertoreadthanitsObjective-Cpredecessor.
MutatingstringsTherearevariouswaystochangestrings,suchasaddingcharacterstoastringaswedidtocollectionobjects.Herearesomebasicexamples:
vargameText="Theplayerentersthestage"
gameText+="andquicklylostduetonotlevelingup"
/*gameTextnowsays
"Theplayerentersthestageandlostduetonotlevelingup"*/
Sincestringsareessentiallyarraysofcharacters,likearrays,wecanusethe+=assignmentoperatortoaddtothepreviousstring.
Also,akintoarrays,wecanusetheappend()functiontoaddacharactertotheendofastring.
letexclamationMark:Character="!"
gameText.append(exclamationMark)
//gameTextnowsays"Theplayerentersthestageandlostduetonot
levelingup!"
Here’showweiteratethroughthecharactersinastring,inSwift:
forcharacterin"Start!"{
print(character)
}
//outputs:
//S
//t
//a
//r
//t
//!
Notehowagainweusethefor-inloopandevenhavetheflexibilityofusingastringliteralifwe’dsoliketobewhat’siteratedthroughbytheloop.
StringindicesAnothersimilaritybetweenarraysandstringsisthefactthatastring’sindividualcharacterscanbelocatedviaindices.Unlikearrays,however,sinceacharactercanbeavaryingsizeofdata,brokenin21-bitnumbersknownasUnicodescalars,theycannotbelocatedinSwiftwithInttypeindexvalues.
Instead,wecanusethe.startIndexand.endIndexpropertiesofastringandmoveoneplaceaheadoroneplacebehindtheindexwiththe.successor()and.predecessor()functions,respectively,toretrievetheneededcharacterorcharactersofastring.
HerearesomeexamplesthatusethesepropertiesandfunctionsusingourpreviousgameTextstring:
gameText[gameText.startIndex]//=T
gameText[gameText.endIndex]//=!
gameText[gameText.startIndex.successor()]//=h
gameText[gameText.endIndex.predecessor()]//=p
NoteTherearemanywaystomanipulate,mix,remove,andretrievevariousaspectsofstringsandcharacters.Formoreinformation,besuretocheckouttheofficialSwiftdocumentationoncharactersandstringsathttps://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html
CommentinginSwiftInourcodesnippetsthusfar,onemightnotenotationswithdoubleforwardslashes//orwithforwardslashesandasterisks/**/.ThesearehowwecancommentormakenotationsinourSwiftcode.Anyonewho’scodedinC++,Java,Objective-C,JavaScript,orotherlanguageswillseethatSwiftworkspracticallythesame.
Single-linecommentsarestartedwiththedoubleforwardslashes,//,whilemultilinecommentsoracommentblockbeginswith/*andendswith*/.
Takethefollowingexample:
//Thisisasinglelinecomment
/*
Thisisacommentblock
thatwon'tenduntilitreachestheclosingasterisk/forwardslash
characters
*/
Commentingisusedtohelpnavigateyourcode,understandwhatitmightdo,andcommentoutlinesofcodewemightnotwanttoexecute,butatthesametimewanttokeepforlater(thatis,print()logcallsoralternativestartingpropertyvalues).
NoteFromXcode6Beta4onward,wecanalsoutilizethefollowingcomments:
//MARK:,//TODO:,and//FIXME.//MARKisequivalenttoObjective-C’s#pragmamark,whichallowstheprogrammertolabelasectionofyourcodethatisaccessibleinXcode’stopbreadcrumbdropdownlist.//TODO:and//FIXMEgiveustheabilitytosectionoffpartsofcodethatwewishtomaybeaddfeaturestointhefutureordebug.Evengameswithwell-organizedclassstructuringcanbedauntingtosiftthrough.Theadditionoftheseadditionalmark-uptoolsmakesplanningandsearchingthroughourgames’codethatmucheasiertodo.
BooleanAnintegralpartofallprogramming,game,orotherwiseistheuseofBooleanvalues.Booleanvaluestypicallyreturneithertrueorfalsevalues,yesorno,or0or1.InSwift,thisisthejoboftheBoolclassofobjects.Theuseofthefunction.isEmpty()inourpastcollectiondatatypeexamplesreturnsaBooleanvalueoftrueorfalsebasedonwhetherthatcollectionisemptyornot.
Ingamedevelopment,onewaywecoulduseBooleanvaluesistohaveaglobalvariable(avariableaccessibleinscopethroughoutourgame/app)thatchecksifthegameisover.
varisGameOver=false
Thisvariable,takenfromthePikiPopgame,startsthegameoffwithavariableoftypeboolnamedisGameOverwithastartingvalueoffalse.Iftheeventsofthegamecausethisvaluetochangetotrue,thenthistriggerstheeventsassociatedwiththegameoverstate.
NoteUnlikeBooleanvaluesinObjective-C,SwiftusesonlytrueorfalsevaluestorepresentBooleanvariables.SwiftstricttypesafetydoesnotallowtheuseofYESandNOor0and1,aswehaveseeninObjective-Candotherprogramminglanguages.
However,readingandcontrollingthistypeofinformationaboutourgame,knownasthegame’sstate,isbestcontrolledwithmorethanjustasingleBooleanvalue.Thisisbecauseyourgameandthecharactersinyourgamecouldhavevariousstates,suchasgameover,paused,spawn,idle,running,falling,andmore.Aspecialobjectknownasastatemachinebestmanagesthistypeofinformation.StatemachinesshallbecoveredinmoredetailwhenwediscusstheGameplayKitframework.
Ints,UInts,floats,anddoublesInadditiontoBooleanvalues,anotherbasicdatatypewehaveuptothispointbrieflymentionedisthevariousnumericobjects,suchasintegers(Ints),unsignedintegers(UInts),floatingpointnumbers/decimals(floats),anddoubleprecisionfloatingpointnumbers/decimals(doubles).
IntegersandunsignedintegersIntegersrepresentnegativeandpositivewholenumbers,whileunsignedintegersrepresentpositivewholenumbers.LikewithCandotherprogramminglanguages,Swiftletsuscreatevarioustypesofintegersandunsignedintegersfrom8,16,32,and64bits.Forexample,anInt32typeisa32-bitinteger,whileaUInt8typeisan8-bitunsignedinteger.ThesizeofthebitsforIntsandUIntsrepresentshowmuchspaceisbeingallocatedtostorethevalues.UsingourUInt8example,anumbermadefromthistypeofunsignedIntcanonlystorethevalues0-255(or11111111inabase-2system).Thisisalsoknownas1byte(8bits).Ifweneedtostorenumberslargerthan255ornegativenumbers,thenmaybeanInt16typewouldsufficeasthatcanstorenumbersbetween–32767and32767.Usually,wedon’thavetoworrytoomuchaboutthesizeallocatedbyourintegervariablesandconstants.So,usingjusttheclassnameofIntwillworkfineinmostcases.
NoteThesizeofIntwilldifferdependingonthetypeofsystemweareworkingon.Ifwearecompilingourcodeona32-bitsystem,anintegerwillbeequaltoInt32,whilethesameintegerwouldbeanInt64ona64-bitsystem.
SwiftcanletusseewhatourminimumandmaximumvaluesareforanIntvariablewiththe.minor.maxclassvariables(thatis,Int16.max=32767andUInt.min=0).
FloatsanddoublesFloatsare32bitfloatingpointnumbers/fractions,suchaspi(3.14),orthegoldenratio,phi(1.61803).
Ingamedesigning,weworkwithfloatingpointvaluesandrangesratheroften,beittodeterminetheCGPointinxandyofa2Dsprite,usinglinearinterpolationforsmoothingagame’scameramovementin3Dspace,orapplyingvariousphysicsforcesonanobjector2D/3Dvector.Theprecisionneededforeachsituationwilldetermineifafloatisneededorifthe64-bitfloatingpointvalue,thedoubleisneeded.Doublescanbeaspreciseas15decimalplaces,whileafloatissixdecimalplacesprecise.
TipIt’sactuallybestpracticetousedoublesinsituationsthatwouldworkforeitherfloatsordoubles.
ObjectsinSwiftThecoreaspectofobject-orientedprogramming(OOP)isofcoursetheconceptofobjects.C++beganthisparadigminprogramming,whileJava,C#,Apple’sObjective-C,andotherlanguageswereallessentiallybuiltfromthisfoundation.
SwiftisanOOPlanguagewiththesamedynamicobjectmodelasObjective-C,butpresentedinacleaner,type-safe,andcompactway.
Youcanthinkofanobjectexactlyasitsounds,anabstractthingorcontainer.Anobjectcanbesomethingassimpleasastring,orsomethingascomplexastheplayerobjectinthelatestvideogame.Technicallyspeaking,anobjectinaprogramisareferencetoasetofvariousdatainanallocatedchunkofmemory,butit’ssufficienttojustunderstandthatanobjectcanbeavariableorareferencetoaninstanceofaclass,Struct,orblockofcode.
Anobjectcanhavevariousdatafields/aspectsassociatedwithit,suchasproperties,functions,parentobjects,childobjects,andprotocols.InlanguagessuchasCforexample,anintegervariableisusuallyrepresentedasjustrawdata,buttheintegertypeinSwiftisactuallyanobject.Thus,wecanaccessextrainformationandperformfunctionsonIntobjectsinourcode.WepreviouslysawthiswiththeInt.maxvariable,whichreturnsthehighestnumberthatcanberepresentedbytheIntclass.Again,dependingonthemachineyouareworkingon,thiscouldbethesamevalueasInt32.maxorInt64.max.
varhighestIntNumber:Int=Int.max
Accesstofunctionsandpropertiesofanobjectusesdotnotation,aswesawwiththepreviousexample.Int.maxandInt.minareactuallyspecialpropertiesknownasclassvariables,whichrepresentallinstancesofanInttypeobject.
Let’slookathowSwiftdealswithobtainingpropertiesandfunctionsofaninstanceofanobjectusingamade-upPlayertypeobject.
letcurrentPlayer=Player(name:"Fumi")//(a)
letplayerName=currentPlayer.getName()//(b)
varplayerHealth=currentPlayer.health//(c)
currentPlayer.attackEnemy()//(d)
We’llgetbacktothesecondhalfofline(a),butjustunderstandthatitcreatesaninstanceofanobjectofthetypePlayernamedcurrentPlayer.Line(c)createsavariablenamedplayerHealththat’ssetbythehealthpropertyofcurrentPlayer;herewiththedotnotation.Lines(b)and(d)usethedotnotationtocallthefunctionsgetName()andattackEnemy().ThegetName()functioninthiscaseisafunctionthatreturnsastringthat’sassignedtotheconstant,playerName.Line(c)createsavariablenamedplayerHealththatiscreatedbyreferencingthehealthpropertyofcurrentPlayer,alsousingdotnotation.Line(d)isadirectcalltothePlayerclass’attackEnemy()function,whichyoucanimaginefornowjustperformswhatwouldmakecurrentPlayerdoherattack.Thisfunctiondoesn’treturnavalueandthusiswhat’sknownasavoidtypefunction.
Asforline(a),onemightnotethatitdoesn’tusethedotnotation.ThisishowSwiftdoes
what’sknownasaclassinitializer;designatedbytheparenthesis()aftertheclassnameandwiththeparametercalledname:thatsendsastring,Fumi,totheobject’sclassinitializer.
Wewillbedivingdeeperintotheuseofobjectsmomentarilyaswemoveontofunctionsandclasses.
TypesafetyandtypeinferenceObjectsand,aswe’llsee,functionsontheseobjectsinSwiftaretype-safe.Whatthismeansisthatifweperformafunctiononastringobjectwhenthecodewasexpectinganinteger,thenthecompilerwillwarnusearlyonintheprocess.Intheveinofgamedesign,ifweweretohavetheplayerperformanactiononlyanenemysupposedtodo,thenSwiftwillknowthroughitsinherentlytype-safenature.
Swift’stypeinferenceissomethingwe’vementionedbefore.Unlikeotherlanguageswhereyouhavetodeclaretheobject’stypeeverytimeit’sinitialized,Swiftwillinferwhattypeyoumean.Forexample,wehavethefollowing:
varplayerHealth=100
//SwiftautomaticallyinfersthatplayerHealthisanIntobject
OptionalsAswestatedbefore,Swiftisatype-safelanguage.ApplealsocreatedSwiftwiththeintentionofkeepingasmanypotentialerrorsandbugsinthecompilationstateofdevelopmentasopposedtoruntime.ThoughXcodehassomegreatdebuggingtools,fromtheuseofbreaks,logging,andtheLLDBdebugger,runtimeerrors,particularlyingamescanbetoughtospot,thusbringingthedevelopmentprocesstoahalt.Tokeepeverythingtype-safeandasbug-freeaspossibleduringcompilation,Swiftdealswiththeconceptofoptionals.
Optionals,inshort,areobjectsthatpotentiallycanbeorstartasnil.Nil,ofcourse,isanobjectthathasnoreference.
InObjective-C,wecoulddeclarethefollowingstringvariableforagame:
NSString*playerStatus=@"Poisoned";
playerStatus=nil;
InSwift,wewouldwritethisinthesameway,butwe’dfindoutveryquicklythatXcodewouldgiveusacompilererrorindoingso:
varplayerStatus="Poisoned"
playerStatus=nil//error!
EvenmoreconfusingforanyonenewtoSwift,we’dalsogetanerrorifwedidsomethingassimpleasthis:
varplayerStatus:String//error
Creatingempty/undeclaredobjectsinourgamesmakessenseandissomethingwe’doftenwanttodoatthestartofourclasses.Wewantthatflexibilitytoassignavaluelateronbasedontheeventsofourgame.Swiftseemstobemakingsuchabasicconceptimpossibletodo!Noworries;Xcodewillinformyouinmostcasestosuffixaquestionmark,?,attheendofthesenilobjects.Thisishowyoudeclareanobjectasanoptional.
So,ifwewanttoplanourgame’spropertiesandobjectsinSwift,wecandothefollowing:
varplayerStatus:String?//optionalString
varstageBoss:Boss?//optionalBossobject
UnwrappingoptionalsLet’simaginethatwewanttodisplaywhatcausedaplayertoloseinthegame.
varcausedGameOver:String?=whatKilledPlayer(enemy.recentAttack)
lettext="PlayerLostBecause:"
letgameOverMessage=text+causedGameOver//error
BecausethestringcausedGameOverisoptional,Xcodewillgiveusacompileerrorbecausewedidn’tunwraptheoptional.Tounwrapthevalueinanoptional,wesuffixanexclamationpoint!attheendoftheoptional.
Here’sourGameOvermessagecode,nowfixedusingtheunwrappedoptional:
varcausedGameOver:String?=whatKilledPlayer(enemy.recentAttack)
lettext="PlayerLostBecause:"
letgameOverMessage=text+causedGameOver!//codenowcompiles!
Wecanalsoforceunwrapoptionalsearlyatdeclarationtoallowanypotentialerrorstobetakencareofatruntimeinsteadofwhencompiling.Thishappensoftenwith@IBOutletsand@IBActions(objectsandfunctionslinkedtovariousstoryboardsandothertoolsthatarebasedonmenu/viewtools).
@IBOutletvartitleLabel:UILabel!//labelfromaStoryboard
varsomeUnwrappedOptional:GameObject!//ourownunwrappedoptional
NoteIfpossible,thoughit’srecommendedtousethebasicwrappedoptional?asmuchaspossibletoallowthecompilertofindanypotentialerrors.Usingwhat’sknownasoptionalbindingandchaining,wecandosomegreatearlylogicchecksonoptionalsthatinpriorlanguageswouldhaveinvolvedvariousifstatements/controlflowstatementstosimplycheckfornilobjects.
Keepingcodeclean,safe,andeasytoreadiswhatSwiftaimstodoandwhySwiftgoesoutofitswaysometimestoforcemanyoftheseruleswithoptionals.
OptionalbindingandchainingOptionalbindingischeckingwhetheranoptionalhasavalueornot.Thisisdoneusingtheveryhandyif-letorif-varstatements.Let’slookbackatourearliercode:
varcausedGameOver:String?=whatKilledPlayer(enemy.recentAttack)
lettext="PlayerLostBecause:"
ifletgotCauseOfDeath=causedGameOver{
letgameOverMessage=text+gotCauseOfDeath
}
Thecodeblock,ifletgotCauseOfDeath=causedGameOver{…},doestwothings.First,usingthekeywords,iflet,itautomaticallycreatesaconstantnamedgotCauseOfDeathandthenbindsittotheoptionalcausedGameOver.ThissimultaneouslycheckswhethercausedGameOverisnilorhasavalue.Ifit’snotnil,thentheifstatement’scodeblockwillrun;inthiscase,creatingtheconstantgameOverMessagethatcombinesthetextconstantwithgotCauseOfDeath.
Wecanuseif-vartosimplifythisevenfurther:
lettext="PlayerLostBecause:"
ifvarcausedGameOver=whatKilledPlayer(enemy.recentAttack){
letmessage=text+causedGameOver
}
Theif-varstatementcreatesatemporaryvariableusingourpreviouslyusedoptionalcausedGameOveranddoesaBooleanlogiccheckbasedontheresultofwhatKilledPlayer(enemy.recentAttack).Thestatementistrueifthere’sanon-nilvaluereturned.Notehowwedon’thavetouseeitherwrapped(?)orforcedunwrapping(!)oftheoptionalinsuchacase.
Optionalchainingiswhenwequerydownintothepropertiesofanobjectusingthedotoperatorwhilealsodoinganil/valuecheckaswedidwithoptionalbinding.Forexample,let’ssaythatwehaveagamewherecertainEnemytypescancauseaplayertoloseinstantlyviaanEnemyinstancenamedcurrentEnemy.Inthisexample,currentEnemy.typewouldbeastringthatreturnsthenameofthekindofenemythathittheplayer.Optionalchainingusesthecustomdotmodifier?.whileaccessingapotentiallynilcheckonaproperty.Here’sthecodetogetabetterideaofhowthisworks:
ifletenemyType=currentEnemy?.type{
ifenemyType=="OneHitKill"
{
player.loseLife()//runtheplayer'slostl
}
}
Chancesarethatwe’dprobablynotmakeanenemywithoutadesignatedtype,butforthesakeofunderstandingoptionalchaining,observehowthischecksforthepossiblenilobjectthat’dbereturnedbycurrentEnemy.typeusingcurrentEnemy?.type.Likehowthedotoperatorfunctionswhereyoucandrilldownthepropertiesandpropertiesofproperties,thesamecanbedonewiththerecurring?.perpropertythatisdrilleddown.In
thiscode,wedoaBooleancomparisonwith==toseeifenemyTypeisthestringOneHitKill.
Don’tworryifthesyntaxoftheifstatementsyntaxisabitofamystery;next,wediscusshowSwiftusesifstatements,loops,andotherwayswecancontrolvariousobjectdataandtheirfunctions.
ControlflowinSwiftControlflowinanyprogramissimplytheorderofinstructionsandlogicinyourcode.Swift,likeanyotherprogramminglanguage,usesvariousstatementsandblocksofcodetoloop,change,and/oriteratethroughyourobjectsanddata.Thisincludesblocksofcodesuchasifstatements,for-loops,do-whileloopsandSwitchstatements.Thesearecontainedwithinfunctions,whichmakeuplargerstructureslikeclasses.
IfstatementsBeforewemoveontohowSwifthandlesoneofthemaintopicsofOOP,functionsandclasses,let’squicklyrunthroughif-elsestatements.AnifstatementcheckswhetheraBooleanstatementistrueorfalse.Wehavetheexampleasfollows:
ifplayer.health<=0{
gameOver()
}
Thischeckswhetherornottheplayer’shealthislessthanorequalto0,designatedbythe<=operator.NotethatSwiftisOKwiththerenotbeingparenthesis,butwecanusethisifwewishorifthestatementgetsmorecomplicated,asinthisexample:
if(player.health<=0)&&(player.lives<=0){//&&="and"
gameOver()
}
Here,wechecknotjustwhethertheplayerhaslostalloftheirhealth,butalsoifalloftheirlivesaregonewiththeand(&&)operator.InSwift,likeinotherlanguages,weseparateouttheindividualBooleancheckswithparentheses,andlikeotherlanguages,wedoalogic-orcheckwithtwobarkeys(||).
HerearesomemorewaystowriteifstatementsinSwiftwiththeaddedkeywords,else-ifandelse,aswellashowSwiftcancheckif-notacertainstatement:
//(a)
if!didPlayerWin{stageLost()}
//(b)
ifdidPlayerWin
{
stageWon()
}
else
{
stageLost()
}
//(c)
if(enemy==Enemy.angelType){enemy.aura=angelEffects}
elseif(enemy==Enemy.demonType){enemy.aura=demonEffects}
else{enemy.aura=normalEffects}
//(d)
ifletonlinePlayerID=onlineConnection()?.packetID?.playerID
{
print("ConnectedPlayerID:/(onlinePlayerID)"
}
//(e)
ifletattack=player.attackType,power=player.powerwherepower!=0{
hitEnemy(attack,power)
}
//(f)
letplayerPower=basePower+(isPoweredUp?250:50)
Let’slookatwhatweputinthecode:
(a):Thischecksthenot/reverseofastatementwiththeexclamationpoint,!,via!statement.(b):Thischeckswhethertheplayerhaswonornot.Otherwise,thestageLost()functioniscalled,usingthekeywordelse.(c):Thischecksifanenemyisanangelandsetsitsauraeffectaccordingly.Ifthisisnot,thenitwillcheckifit’sademonusingelse-if,andifthat’snotthecase,thenwecatchallotherinstanceswiththeelsestatement.Wecouldhaveanumberofelse-ifstatementsoneafteranother,butifwestarttostacktoomany,thenusingfor-loopsandSwitchstatementswouldbeabetterapproach.(d):Usingoptionalchaining,wecreateanonlineIDconstantbasedonif;weareabletogetanon-nilplayerIDpropertyusingif-let.(e):Thisusesif-let,whereoptionalbindingbecameafeatureinSwift1.2.Insteadofhavingnestedif-letsandotherlogicchecks,akintohowSQLqueriesaredoneinbackendwebdevelopment,wecancreateverycompact,powerfulearlylogicchecking.Inthecaseofexample(e),wehaveanenemyreceiveanattackbasedonwhattypeofattackitisandthepoweroftheplayer.(f):Thisisanexampleofcombiningthecreationofaconstantwiththekeywordletanddoingashorthandversionofanifstatement.WeshorthenanifstatementinSwiftwiththequestionmark?andcolon:.Hereistheformatforshorthandinganifstatement:bool?trueResult:falseResult.IfisPoweredUpistrue,thenplayerPowerwillequalbasepower+250;iffalse,thenit’sbasepower+50.
ForloopsWetouchedonfor-inloopsbeforedealingwithcollections.Hereagainisafor-inloopinSwiftthatwilliteratethroughacollectionobject:
foritemNameininventory.values{
print("Itemname:\(itemName)")
}
Forsomeofusprogrammerswhoareusedtotheolderwayofusingfor-loops,don’tworry,Swiftletsuswritefor-loopsintheC-style,whichmanyofusareprobablyusedto:
forvarindex=0;index<3;++index{
print("indexis\(index)")
}
Here’sanotherwayofusingafor-loopwithoutusinganindexvariable,notedwiththeunderscorecharacter_butofcourseusingaRange<Int>objecttypetodeterminehowmanytimesthefor-loopiterates:
letlimit=10
varsomeNumber=1
for_in1…limit{
someNumber*=2
}
Notethe…betweenthe1andlimit.Thismeansthatthisfor-inloopwilliteratefrom1-10.Ifwewantedittoiteratefrom0tolimit-1(similartoiteratingbetweentheboundsofanarray’sindex),wecouldhaveinsteadtyped0..<limitwherelimitisequaltothearray’s.countproperty.
Do-whileloopsAnotherverycommoniterationloopinprogrammingisthedo-whileloop.Manytimeswecanjustutilizethewhileportionofthislogic,solet’slookintohowandwhywemightuseawhileloop:
letscore=player.score
varscoreCountNum=0
whilescoreCountNum<score{
HUD.scoreText=String(scoreCountNum)
scoreCountNum=scoreCountNum*2
}
Ingamedevelopment,oneuseofthewhileloop(thoughexecuteddifferentlyinagameapp,thisaccommodatesiteratingonceperframe)isfordisplayingthecountingupofaplayer’sscorefrom0tothescoretheplayerreached—acommonestheticofmanygamesattheendofastage.Thiswhileloopwilliterateuntilitreachestheplayer’sscore,displayingonHUDobjectshowingtheintermediatevaluesupuntilthatscore.
Ado-whileloopispracticallythesameasthewhile-loopwiththeextracaveatofiteratingthroughthecodeblockatleastonce.Theend-stagescorecountexamplecanalsoillustratewhywewouldneedsuchaloop.Forexample,let’simaginethattheplayerdidreallybadandgotnoscorewhenthestageended.Inthewhileloopgiven,ascoreofzerowon’tletusentertheblockofcodeinthewhileloopsinceitdoesn’tfulfillthelogiccheckofscoreCountNum<score.Inthewhileloop,wealsohavecodethatdisplaysthescoretext.Thoughmaybeembarrassingtotheplayer,wewouldwanttocountuptothescoreandmoreimportantly,stilldisplayascore.Here’sthesamecodedonewithado-whileloop:
letscore=player.score
varscoreCountNum=0
do{
HUD.scoreText=String(scoreCountNum)
scoreCountNum=scoreCountNum*2
}whilescoreCountNum<score
Nowscoretextwilldisplayeveniftheplayerscorednothing.
SwitchstatementsSwitchstatementsareusefulwhenwewishtocheckmanydifferentconditionsofanobjectinafullyencompassingandneatwaywithouthavingawallofelse-ifstatements.Here’sacodesnippetfromthegamePikiPopthatusesaSwitchstatementfromthegame,PikiPop,thatsetsthepercentageaGameCenterachievement(inthiscase,a6xcombo)basedonthenumberoftimesthecombowasachievedbytheplayer.Don’tworrytoomuchabouttheGameCentercode(usedwiththeGCHelpersingletonobject);that’ssomethingwewillgooverinfuturechapterswhenwemakegamesinSpriteKitandSceneKit.
switch(comboX6_counter){
case2:
GCHelper.sharedInstance.reportAchievementIdentifier("Piki_ComboX6",
percent:25)
break
case5:
GCHelper.sharedInstance.reportAchievementIdentifier("Piki_ComboX6",
percent:50)
break
case10:
GCHelper.sharedInstance.reportAchievementIdentifier("Piki_ComboX6",
percent:100)
default:
break
}
Theswitchstatementheretakesthevariableusedtocounthowmanytimestheplayerhita6Xcombo,comboX6_counter,andperformsdifferenttasksbasedonthevalueofcomboX6_counter.Forexample,whentheplayerhasdonea6XCombotwice,thePiki_ComboX6achievementgets25%fulfilled.Theplayergetstheachievement(whenat100%)whenthecounterhits10.Thepurposeofthekeywordbreakistotellthelooptoexitatthatpoint;otherwise,thenextcaseblockwilliterate.Sometimes,thismightbedesiredbyyourgame’slogic,butkeepinmindthatSwift,likemanyotherlanguages,willcontinuethroughtheswitchstatementwithoutbreak.Thekeyworddefaultisthecatch-allblockandiscalledwhenthevalueoftheitemcheckedbytheswitchstatementisanythingbutthevariouscases.Itcanbethoughtofasanequivalenttotheelse{}block,whileallofthecasesaresimilartoelseif(){}.ThedifferencethoughisthatSwiftrequiresallcasesoftheswitchbehandled.So,thoughwecansufficewithanifwithoutanelse,wehavetohaveadefaultcaseforaswitchstatement.Again,thisisdonetokeepSwiftcodesafeandcleanearlierinthecodingprocess.
FunctionsandclassesUpuntilthispoint,wehavekeptfromdiscussingprobablythemostimportantaspectsofSwiftoranyOOPlanguagesforthatmatter—howthelanguagehandlesfunctionsonobjectsandhowitorganizestheseobjects,objectproperties,andfunctionsandperformsvariousobject-orienteddesignconcepts,suchaspolymorphismandinheritancewithclasses,Structs,enums,protocols,andotherdatastructures.ThereismuchmoretodiscussabouthowSwiftutilizestheseconcepts,morethanwecanfitinthischapterbutthroughoutthecourseofthisbook,especiallyaswegetintohowtouseApple’sgame-centricSpriteKitandSceneKitframeworks,wewillfleshoutmoreonthesetopics.
FunctionsInObjective-C,functionsarewrittenthefollowingway:
-(int)getPlayerHealth(){
returnplayer.health;
}
Thisisasimplefunctionthatreturnstheplayer’shealthasaninteger—theIntequivalentinObjective-C.
Thestructureofthefunction/methodisasfollowsinObjective-C:
-(return_type)method_name:(argumentType1)argumentName1
joiningArgument2:(argumentType2)argumentName2…
joiningArgumentN:(argumentTypeN)argumentNameN
{
functionbody
}
Here’sthesamefunctioninSwift:
funcgetPlayerHealth()->Int{
returnplayer.health
}
//Howwe'dusethefunction
varcurrentHealth:Int=0
currentHealth=getPlayerHealth()
ThisishowafunctionisstructuredinSwift:
funcfunction_name(argumentName1:argumentType1,argumentName2:
argumentType2,argumentNameN:argumentTypeN)->return_type
{
functionbody
}
Notehowweusethekeywordfunctocreateafunctionandhowtheargument/parameternamesarefirstwiththetypessecond,separatedbythecolon(:)andwithinparenthesis.
Here’swhatatypicalvoidfunctionlookslikeinSwift.Avoid-typefunctionisafunctionthatdoesn’treturnavalue.
//withaPlayertypeasaparameter
funcdisplayPlayerName(player:Player){
print(player.name)
}
//withoutanyparameters;usingaclassproperty
funcdisplayPlayerName(){
print(currentPlayer.name)
}
Inavoidfunction,there’snoneedtowrite->returnType,buteveniftherearenoparameters,wedohavetoputinthe()parenthesisattheendofthefunctionname.
TuplesAratherpowerfulaspectofSwiftisthatfunctionreturntypes(andconstants/variables)canincludeacombinationofvaluesintoasinglevalue.Thesecombinationsarecalledtuples.Here’sanexampleofanunnamedtuple:
lethttp503Error=(503,"ServiceUnavailable")
Here’satupleusedasareturntypeinafunctiondirectfromApple’sSwiftdocumentation.Observehowitusesmuchofwhatwe’velearnedthusfar:
funcminMax(array:[Int])->(min:Int,max:Int){
varcurrentMin=array[0]
varcurrentMax=array[0]
forvalueinarray[1..<array.count]{
ifvalue<currentMin{
currentMin=value
}elseifvalue>currentMax{
currentMax=value
}
}
return(currentMin,currentMax)
}
ExcerptFrom:AppleInc."IOSDeveloperLibrary".
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swif
t_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-
ID164
ClassesInOOP,classesmakeupthebasicframeofanobject,itsfunctionalityandinteractionswithotherclasses,objects,andvariousdatastructures,suchasprotocols,Structs,extensions,generics,andenumerations.Inthefollowingchapters,aswebegintostructureourgames,wewilldivedeeperintoalloftheseconcepts,butfornow,let’sunderstandthebasicsofclassesandhowtheydifferinSwiftfromObjective-Candotherlanguages.
Here’sthebasicstructureofaclassinSwift:
//(a)
Global-projectwideproperties/variables
//(b)
classclassName:parentClassName,protocolName…protocolnName
{
//(c)
classscopeproperties
//(d)
initializers(init(),convenience,required,etc)
//(e)
funcfunction_name1(argumentName1:argumentType1,argumentName2:
argumentType2,argumentNameN:argumentTypeN)->return_type
{
function-scopevariablesandbody
}
.
.
.
funcfunction_nameN(argumentName1:argumentType1,argumentName2:
argumentType2,argumentNameN:argumentTypeN)->return_type
{
function-scopevariablesandbody
}
//(f)
deinit()
}//endoftheclass
//(g)
global-projectwideproperties/variables(alternativeposition)
TheSwiftclassstructureworkssomewhatsimilartowhatweseeinC#andJava,asopposedtoObjective-C’stwofiles’(.h/header,.m/.mm/implementation)setup:
(a):Wecanhaveproperties(likevariables,constants,Structs,andenums)outsideoftheclassdeclaration,whichwouldmakethemglobalinscope,akaaccessiblethroughouttheentireproject/game/app.(b):Thisistheactualclassrepresentedbywhatwenamedour.swiftfile.Again,thisisdifferentfromObjective-C’sclassname.h-classname.m/.mmdualfilesetupforasingleclass.Aclasscanbeachildclassofanotherclass.Wedon’thavetodeclareaparent/baseclassinSwift.Classeswemakecanbetheirownbaseclasses.WecanmakeclassesasObjective-CclassesbysubclassingthemfromNSObject.
ThebenefitofthatisgettingObjective-Cruntimemetadataandcapabilities,butwetakeahitinperformancefromtheextrabaggage.EitherinthesameplaceastheparentClassorafterthecolon:ofparentClass,wecandeclarewhichprotocolsthisclasswilladhereto.We’lldiscussmoreonprotocolslaterinthebook,butjustthinkofthemasmakingsureyourclassutilizesthesamefunctionsastheprotocoldictates.(c):Thesearewherewe’dplacevariables,constants,Structs,enums,andobjectsthatarerelevantforuseinthescopeoftheclass.(d):Initializersarespecialfunctionsweusetosetupthepropertiesinsection(c)whenotherclassesanddatastructuresuseinstancesoftheclassviaclassName(initializerparameters).Wewilldiscussmoreoninitializersmoreinthenextchapteraswestructureourgames.Theydon’thavetobeatthetopoftheclass,butit’sagoodpracticetodoso.(e):Thesearewhereyourclassfunctionswillbedeclaredanddeveloped.Wecanhavefunctionsthatareknownasclassfunctions.Thesearedesignatedwiththekeywordsclassfunc.Inshort,classfunctionsarepartoftheclassasawholeasopposedtoaninstanceoftheclass.It’sbestpracticetoplacetheseabovethenext,morecommontypeoffunction,thepublicfunctions,thatcanbeaccessedbyotherclassesandpropertiesviathedotoperator(thatis,className.function(parameters)).Usingtheprivatefunckeywords,asinC#andJava,wecancreateprivatefunctionsthatareonlyaccessibletotheclass’sownfunctionsandproperties.(f):Thedeinit()functionisaspecialoptionalfunctionthatdealswithhowwecleanupthedataallocatedbyourclasswithmemorymanagementandeliminatingwhat’sknownasmemoryleaks.Apple’sARC(AutomatedReferenceCounting)handlesmostofthis,buttherearekeywords,suchasweakandunowned,thatwewillattimeshavetoputbeforevariouspropertiestomakesurethattheydon’thangaroundafteruse.
Thisisaratherinvolvedtopic,butworthlookingintotoavoidmemoryleaksinyourgame.ARCdoestakecareofmostofthis,buttheremightbeobjectsinyourgamethatcouldpotentiallyhangaround.It’shighlyrecommendedtoreadApple’sowndocumentationonthistopic,asmemorymanagementiniOSisalwaysintheevolvingstage.YoucanviewthefulldocumentationonARCandmemorymanagementinSwiftathttps://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
(g):Ifwewish,wecanhaveglobalpropertiesalsoatthebottomofour.swiftfiles,aftertheendoftheclassdeclaration.Apple’sowngameexample,Adventure(https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.htmlplacesglobalpropertiesinthisspot.
SummaryThere’smuchmoreabouttheSwiftprogramminglanguagethanwecouldfithere.Throughoutthecourseofthisbook,wewillthrowinafewextratidbitsandnuancesaboutSwiftasitbecomesrelevanttoourupcominggamingprogrammingneeds.
IfyouwishtobecomemoreversedintheSwiftprogramminglanguage,Appleactuallyprovidesawonderfultoolinwhat’sknownasaPlayground.
PlaygroundswereintroducedwiththeSwiftprogramminglanguageatWWDC14inJuneof2014andallowustotestvariouscodeoutputsandsyntaxeswithouthavingtocreateaproject,buildit,andrunitandrepeatagain,wheninmanycaseswesimplyneededtotweakafewvariablesandfunctionloopiterations.
ThereareanumberofresourcestocheckoutontheofficialSwiftdeveloperpage(https://developer.apple.com/swift/resources/).
TwohighlyrecommendedPlaygroundstocheckoutareasfollows:
TheGuidedTourPlayground(https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zipThisPlaygroundcoversmanyofthetopicswementionedinthischapterandmore,fromHelloWorldallthewaytoGenerics.TheBalloonsPlayground(https://developer.apple.com/swift/blog/downloads/Balloons.zip):TheBalloonsPlaygroundwasthekeynotePlaygroundsdemonstrationfromWWDC14andshowsoffmanyofthefeaturesPlaygroundshavetooffer,particularlytomakeandtestgames.
Sometimes,thebestwaytolearnaprogramminglanguageistotestlivecode,andthat’sexactlywhatPlaygroundsallowustodo.
Inadditiontotestingsnippetsofcodeinourgames,iOS9alsoallowsustoplanandstructureourgames,whichisthetopicofthenextchapter.
Chapter2.StructuringandPlanningaGameUsingiOS9StoryboardsandSeguesVideogamedevelopmenthashadaninterestinghistory.Itstartedasanoffshootofbothelectricalengineeringandcomputerscience.Gameswereagreatchallengeforengineerstomakethemostoutofthelimitedhardwareand,ofcourse,makesomethingfun.Today,videogamesandvideogamedevelopmentarestillbuiltonthosefoundationsoftechnology,math,andengineeringbut,fordecades,havealsobeenmajorplayersintheworldofentertainment,storytelling,andmedia.
Beitifyouareamajorstudio,asmallteam,orcreatinggamesallbyyourself,planningandstructuringyourgameprojectscangiveyouthefoundationneededtosavetimeinthedevelopmentprocess,dividetheworkouttoothersifonateam,andofcourse,bringyourgametolifeascloseaspossibletohowyouimaginedit.
StartingwithiOS5,Appletookapagefromtheentertainmentindustryinhowtostructureandplanaproject,bigorsmall;byusingtheconceptofstoryboards.Storyboardsareagraphicrepresentationofthevariousstepsandstructuresofaproject;beitananimation,amovie,orinourcase,iOSgames.Storyboardswillgraphicallyshowtheflowofaproductionorapp.Inanimation,forexample,storyboardsareusedtofleshoutmajorframesorstorypointsoftheproduction.Onceit’sagreedonastowhattheseriesofeventsinascenewillbe,animatorswillanimatearoundthosekeypoints.DependingonwhethertheproductionisprelayorADR,voiceactingcouldalsobeplacedintothestoryboardprocess,whichgivestheanimatorsevenmorespecificcontenttoworkwith.
Inthecaseoftheactualgameapplication,storyboardscanrepresentmajorpartsofyourgame,suchastheIntroscene,OpeningMenuscreen,PauseScreen,GameOverScreen,orthegenericlookofamaingamelevel.ApplenamedthesestructuresinXcodestoryboards,andthepathsbetweenthemareknownassegues.Throughoutthischapter,weshallbelookingintohowtomakeuseofthesefeatureswhilemakingagameapp.
TheprecedingisanexampleofasimpleiOSStoryboard.
Model-View-ControllerBeforewegetintostoryboardsiniOS9,it’sbestthatwefirstdiscussthebasicflowofaniOSappandtheconceptofModel-View-Controller(MVC).Model-View-Controllerisanarchitecturalparadigmusedinsoftwareengineering,programming,andevennowinwebdesign.WecanthinkofthemodelportionofMVCasthelogicorbrainsofanapplication’sbehavior.Thislogicisusuallyindependentoftheuserinterfaceanddetermineswhattodowiththeapp’sdata.
We’veactuallyalreadygoneoverthemodelportionofMVC!TheSwiftprogramminglanguagediscussedinthepreviouschapteristhatmodel;thisisthecasewithitsObjective-CpredecessorandanyotherprogramminglanguageusediniOSoranyothergamedevelopment.Yourgame’scodecontrolswhattodowiththeplayer,level,andenemy/goaldata.
TheviewportionofMVCisthevisualrepresentationofthemodel.Thisofcoursewouldincludethenumerousvisualaspectsofourgames,fromourplayer’sanimationframes,variousin-gamestatsontheHUD,particleeffects,andmore.
ThecontrollerportionofMVCcanbethoughtofasthegluethatholdsthemodelandviewtogether.Itisalsothepointatwhichtheuserofyourgameinteractswith.Beitactions,suchasabuttonpress,abasictouch,aswipe,orothergestures,recognizedbyyouriOSdevice,thecontrollertakesthatuserinput,manipulatesyourmodelandthenthemodelupdatesyourviewaccordingly.
ThisdiagramistakenfromApple’sownAdventureGameExample.
WhenweworkwithiOSapps,thefirstrecommendedentrypointforcodeandstoryboardinfoistheRootViewController.Aswe’llcometofindout,MVCisintrinsicallybuiltintoiOSappdevelopmentandtheXcodeIDE.Storyboardsareacollectionofdifferenttypesofviewcontrollerswithvaryingtasksthatarelinkedbysegues.
AniOSapp’slifecycleBeforewemoveontoworkingwithstoryboards,segues,andthefoundationofourgameapps,it’sbestwegoovertheoveralllifecycleofaniOSappasit’simportanttoknowtheentrypointsofourcodeandvariousobjects/structuresofourapps.
Insertapplifecycleimageryherebeforewemoveontoworkingwithstoryboards,segues,andthefoundationofourgameapps.It’sbestwegoovertheoveralllifecycleofaniOSappasit’simportanttoknowtheentrypointsofourcodeandvariousobjects/structuresofourapps.
Source:https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
AnyonewhohasworkedwithC/C++,Java,orotherlanguageswillbefamiliarwiththemain()function.Themain()functionisusedtodesignateyourprogram’smainentrypoint.TheprecedingexampleishowAppledesignatesthetypicalmainfunctionforapps.EssentiallywhatthisisdoingiscallingthefirstclassinthetypicallifecycleofiOSapps,theAppDelegateclass.
Themain()functionHere’sthecodewiththemain()function:
#import<UIKit/UIKit.h>
#import"AppDelegate.h"
intmain(intargc,char*argv[])
{
@autoreleasepool{
returnUIApplicationMain(argc,argv,nil,
NSStringFromClass([AppDelegateclass]));
}
}
//Objective-CexampleoftheMain()function
Notehowthemain()functioniswritteninObjective-C.Swiftagainmakesdeclaringtheentryofyourapplicationeasier.
@UIApplicationMain
classfirstClassCalled
{
//classcode
}
WhilebuildinganiOSappwithSwift,themain.mfileseeninpriorObjective-Cprojectsisnolongerneeded.Instead,weuseanAttributecall,@UIApplicationMain,justbeforethedeclarationoftheclassthatisfirstcalled.
NoteSwiftattributes
Attributes,beginningwiththeatcharacter,@,areusedtoaddadditionalinformationtoadeclarationoratype.InSwift,theyhavethefollowingsyntax:
@attributename
@attributename(attributearguments)
Asinotherprogramminglanguages,attributes,dependingontheirfunctionality,canbeusedtodescribeobjects,functions,andevenentireclasses.
Forexample,the@objcattributeisusedtodeclarecodethatisreadableinObjective-C.
Aswe’llseewhileusingandlinkingvariousobjectsinthestoryboardswithourcode,theattributes@IBOutletand@IBActionareusedtodescribeobjectsandfunctionsrepresentingobjectswecreateinXcode’sInterfaceBuilder.
WewilldiscussmoreonAttributesinChapter7,PublishingOuriOS9.0Game.
TheUIApplicationclass/objectUIApplicationistheobjectresponsibleforcontrollinganapp’sevent-loopaswellashandlingotherupper-levelappprocesses.Gameappornot,thisispresentinalliOSappsandiswhatisfirstcalledatthemainentrypointandworkstogetherwiththeAppDelegateclass.ThoughitispossibletosubclassUIApplication,it’susuallynotrecommended.CustomizationstowhatyourgamedoesduringvariousappstatesarewhatweusetheAppDelegateclassandViewControllersfor,evenifstoryboardsarenotutilized(thatisifyouchoosetomostlyhardcodeyourgame).
TheAppDelegateclassWecanthinkoftheAppDelegateclassasyourapp’smainhub.It’sthetoplevelofgeneralcustomizationforyourgame.WhilemakinganappinSwift(gameornot),it’stheclassthatisgiventhe@UIApplicationMainattributebecauseit’sthegeneralfirstentryofyourgame’smodel/code.
Here’sthecodethatAppleprovideswithalmosteveryiOSapppresetinXcode:
importUIKit
@UIApplicationMain
classAppDelegate:UIResponder,UIApplicationDelegate{
varwindow:UIWindow?
funcapplication(application:UIApplication,
didFinishLaunchingWithOptionslaunchOptions:[NSObject:AnyObject]?)->
Bool{
//Overridepointforcustomizationafterapplicationlaunch.
returntrue
}
funcapplicationWillResignActive(application:UIApplication){
//Sentwhentheapplicationisabouttomovefromactiveto
inactivestate.Thiscanoccurforcertaintypesoftemporaryinterruptions
(suchasanincomingphonecallorSMSmessage)orwhentheuserquitsthe
applicationanditbeginsthetransitiontothebackgroundstate.
//Usethismethodtopauseongoingtasks,disabletimers,and
throttledownOpenGLESframerates.Gamesshouldusethismethodtopause
thegame.
}
funcapplicationDidEnterBackground(application:UIApplication){
//Usethismethodtoreleasesharedresources,saveuserdata,
invalidatetimers,andstoreenoughapplicationstateinformationto
restoreyourapplicationtoitscurrentstateincaseitisterminated
later.
//Ifyourapplicationsupportsbackgroundexecution,thismethod
iscalledinsteadofapplicationWillTerminate:whentheuserquits.
}
funcapplicationWillEnterForeground(application:UIApplication){
//Calledaspartofthetransitionfromthebackgroundtothe
inactivestate;hereyoucanundomanyofthechangesmadeonenteringthe
background.
}
funcapplicationDidBecomeActive(application:UIApplication){
//Restartanytasksthatwerepaused(ornotyetstarted)while
theapplicationwasinactive.Iftheapplicationwaspreviouslyinthe
background,optionallyrefreshtheuserinterface.
}
funcapplicationWillTerminate(application:UIApplication){
//Calledwhentheapplicationisabouttoterminate.Savedataif
appropriate.SeealsoapplicationDidEnterBackground:.
}
}
Thisisthedirectcodeandcomments(asofXcode6.4)thatAppleprovidesforuswhenusingtheiOS9gamepreset.Beforewediveintostructuringourgameswithstoryboardsandthetwomainframeworks(SpriteKitandSceneKit),it’sbesttounderstandwhathappensinthisclass.Eventsthathappentoyourgameapprelatingtothedevice,particularlythosethatareoutsideoftheplayer’scontrol,suchasincomingphonecalls,notifications,andthedeviceshuttingdownduetolowbatterypower,aswellasthosecontrolledbytheplayer(thatispausingthegame),arehandledbythisclass.Aswesee,Applealreadyprovidesgreatinstructionsforwhateachfunctionofthisclassdoes,sobesuretoreviewthem.Wewillcomebacktotheseaswecreateourgamesandhandlethosespecificsituations.NotethattheAppDelegateclasshasanoptionalvariable(meaningitcanbenil)namedwindowandisofthetype,UIWindow.AUIWindowobjectisachildofUIViewandcanallocatevariousdisplays/objectsthatcanbeputintotheviewoftheuser.Technically,wecanuseobjectsofUIWindowandUIViewincodedirectlytocreatethevisualsofourgame,butAppleprovidesmorerobustobjectsthathandleboththeuser’sinteractionwiththescreenandview.TheseobjectsarewhatmakeupiOSstoryboards;theablynamed,ViewControllers.
ViewcontrollersViewcontrollersareprobablyoneofthemostvitalstructuresofiOSdevelopmentandarewhatstoryboardsarevisuallyrepresentingwhendesigningtheminXcode’sInterfaceBuilder.Intermsoftheirtypicalentrypointorder,it’sMAIN—>AppDelegate—>RootViewController—>[callstoanyadditionalViewControllersinstance].
WhenwecreateanewappprojectinXcode,ApplewillmakeadefaultRootViewControllernamedViewControllerforus.Here’sit’scode:
importUIKit
classViewController:UIViewController{
overridefuncviewDidLoad(){
super.viewDidLoad()
//Doanyadditionalsetupafterloadingtheview,typicallyfroma
nib.
}
overridefuncdidReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
//Disposeofanyresourcesthatcanberecreated.
}
}
ThisisthestartercodegiventousinXcodewiththedefaultViewController.swiftclass.Aswesee,it’sasubclassofUIViewControllerandthusinheritsallofitsparentclass’sfunctions.OneofthemshownhereisthefunctionviewDidLoad().InSwift,whenwewishtooverrideafunctionofaparentclass,weusethekeywordoverridebeforethefunctiondeclaration.Wealsoseethatsuper.viewDidLoad()iscalledaswell.Whatthisdoesiscalltheparent’sownversionofthisfunctionbeforeweaddourowncode/customizationsandisrecommendedwhenusinganyofthefunctionsofUIViewController.TheUIViewControllerfunctionshandlevariousviewstates;viewDidLoad()handleswhentheviewisfirstloadedandiscalledonceforthelifeoftheUIViewControllerobjectduringanapp’slifecycle.Ifwewanttocallsomecodeeverytimeaviewisseen,wecanusetheviewDidAppear()functionofUIViewControllerinstead.
Here’savisualrepresentationoftheseviewstates.
Herestoryboardsandsegues,aswe’llsee,essentiallygiveusavisualandcustomizablerepresentationoftheseverystatesandthetransitionsbetweenthemwithoutusingtoomuchcode.
TodiveevendeeperintotheUIViewControllermethods,checkoutApple’sdocumentationonthesubject:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
NoteForanyonefamiliarwiththegamedevelopmentengineUnity(whichhasscriptswrittenineitherC#,JavaScript,orthePythonderivative),onewaywecanimaginetheUIViewControllerfunctionsviewDidLoad()andviewDidAppear()isthattheyaresomewhatsimilartotheUnityfunctionsAwake()andOnEnabled(),respectively.Onefunctioniscalledwhenthesceneisfirstloadedandtheotherjustbeforethefirstframethattheobjectisvisible/enabled.TheUIViewControllerfunctionshoweverareonamoreupper-levelbasisfortheentiretyoftheappasopposedtoapergameObjectbasis.
FormoreinformationandgraphicsontheentireiOSapplifecycle,checkoutthefulldocumentationhere:
https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html
ViewcontrollertypesViewcontrollerscomeinanumberoftypesandwecancreateourownbysubclassingthem.Thetwomaintypesarecontainerviewcontroller,whichholdotherviewcontrollers,andcontentviewcontrollers,whichaswecanimagine,arewhatdisplaythecontent.ContentviewcontrollersincludetheRootViewController,whichisthefirstviewcontrolleraccessedaftertheapp’sentrypointandisalsothefirstviewcontrollerseeninthedefaultMain.StoryboardfileinapresetXcodeproject’sinspector.Therearealsootherspecialtypesofviewcontrollers,liketheUITableViewController,usedtodisplaydatalistedintablecellformatsandtheNavigationController,whichcontrolsthenavigationlogic/imageryoftheappwhenmovingbetweenotherviewcontrollers.
Foramorein-depthlookatthevariousviewcontrollersavailableinUIKit,checkouttheofficialdocumentationseenhere:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/index.html#//apple_ref/doc/uid/TP40007457-CH2-SW1
It’sactuallyatthispointthatwecanbegintocodeourgame,albeitentirelyprogrammingtheMVCmodel.InthebeginningofiOSgamedevelopment,thiswasessentiallyhowonewouldgoaboutdevelopingagamefortheoriginaliPhone.We’dprogrammaticallyworkwiththeUIWindowandViewControllerobjectsandourgame’sowncustomclassestocrafttheapp.AsthefamilyofiOSdevicesgrew,anobviousissuebegantoarise.Thoughwecan,andsometimesmighthaveto.programmaticallychangecodebasedonthedevice,dealingwithagrowingnumberofscreensizesanddevicetypesmadeitsothatourcodewouldalwayshavetoberefactoredandproducedeverincreasingambiguitywheneveranewAppleiOSdevicewasannounced.Also,let’snotforgetthatgamedevelopmentisasmuchofavisualdesigner/animator’sworkasitisaprogrammer’s.Editing,positioning,refining,andlaterupdatingvariousvisualaspectsofagamecanbeverytimeconsumingifdoneentirelyviacode.
Storyboardsweremadetohelpalleviatethisissuebyallowingustovisuallydesignourgameintheprojectitselfasopposetohavingourownpossiblyhandwrittenstoryboardsthatdescribejustamodel-based,code-centricdesign.WiththeintroductionofAutoLayoutinXcode5,wecan,withoutusinganycode,makeoneprojectandgeneralviewforallvarietiesofiOSdevices.WeshalltouchonAutoLayoutaswenowfinallymoveontoworkingwithStoryboardsandsegues,butforamorein-depthlookonAutoLayout,checkouttheofficialdocumentationonApple’sdeveloperportal:https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG
StoryboardsandseguesLet’snowfinallygettoworkingwiththesetoolsandlearnthebasicsofstructuringgameappsonabroaderstoryboardlevel.Asofthewritingofthisbook,thelatestversionofXcodeavailableisversion7.0.Thiswillbetheversionweshallworkwith,butXcodeisalwaysupdatingwithevenabetaversionavailabletoseparatelytestthenewestfeatures.
Visithttps://developer.apple.com/xcode/todownloadandreaduponallthatXcodehastoofferforiOSdevelopers.
Tostartstructuringyourappusingstoryboards,followtheseinstructions:
1. First,openXcodeinyourApplicationsfolder(orinyourDockifyouplaceditthereforeasyaccess).
2. Next,clickonCreateanewXcodeProject.
3. Youwillnowbeaskedtochooseatemplatepreset.4. Forthesakeofjustunderstandingstoryboardsandsegues,selecttheSingleView
Applicationtemplate.(Don’tworry,wewillbeusingthegametemplateinthenextchapter).
5. Nowwechooseourproject’soptions.NameyourprojectStoryBoardExample.6. IntheLanguagedropdown,makesurethatitissettoSwiftandensuretheDevices
dropdownissettoUniversal.7. ThereshouldbeotherfieldsfilledinbyXcode,suchasyourorganizationnameand
organizationidentifier.Thoseareinvolvedwiththeinformationthatwillbepublishedwithyourappwhenitcomestodeploymentaswellasthecontentofyourcode’scopyrightcomments.WecanfornowkeeptheseattheirdefaultsettingthatXcodehasfilledin.
8. ClickonNextandthenselectavalidlocationinyourfilestosavethisproject.
Nowwehaveourdefaultappcreatedbythetemplate.Weshouldseeontheleft-handside,intheFileNavigatorPane,variousfilesandfolderscreatedforus.Aswecansee,theAppDelegate.swiftandtheViewController.swiftfileswereautomaticallycreatedforusandrightbelowthat,we’dfindtheMain.Storyboardfile.Thisisourstoryboardandwhenyouclickonit,youshouldseethetwopanesopenatthecenterofyourXcodewindow.TheleftsideistheviewcontrollerScenedropdown,whichshowsthehierarchyofthescenecontrolledbytheprovidedtheviewcontroller.Therightpaneinthecenterallowsustovisuallyseetheviewcontrollerandeventuallyelementsthatwecanplaceinit.Themainvisualpartofthestoryboardcanbezoomedinandzoomedout.Asweaddmorescenestoit,thiswillallowustoseetheentiretyofourstoryboardortheportionsweareworkingon.
Youmighthavetozoomoutslightlytoseeit(usingyourmouseorusingthepinchinggestureonyourtrackpadwithaMacBook),buttotheleftoftheViewControllerscenethere’sagrayarrow.ThisistheentrypointandthefirstViewControllersceneattachedtothisarrowisyourRootViewController/Initialscene.
TipWhenaddingmorescenestoyourstoryboard,foreitherdebuggingpurposesordesignchoice,youcansimplychangethescenethatisfirstenteredbyclickinganddraggingthatarrowtotheleftofthatscene.
Let’sstartbycreatingaseparatesceneforourstoryboard:
1. AtthebottomoftheUtilitiespanel(thefarrightpaneloftheXcodeproject),therearefouriconsdesignatingthevarioussnippetsandobjectswecanplaceinourproject’scodeandthestoryboard.Clickonthethirdiconfromtheleftifit’snotalreadyselected.ThiswillopentheObjectLibrary.
2. WecanseethattheverytopoftheObjectLibraryhasaViewControllerobject.
3. Dragthisontothestoryboard’scanvas,preferablytotherightoftheinitialscene.
NoteIftheUtilitiespanelisn’topen,clickontheupperright-mosticonatthetopofyourproject’stoolbarwindow.
NoteThethreebuttonsinyourtoolbarcanbetoggledtoclosetheNavigationpane,Debugpane,andUtilitiespane,respectively.Closingthesewhenapplicablecanhelpexpandthegeneralview,knownasthecanvasofyourstoryboardscenes.
Nowwehavetwoscenesinourstoryboard,butnothingistheretotelluswhattheyare.Theyarejusttwoblankscenes!
Let’sputaLabelobjectinthesescenestorepresentwhattheyareandatruntimetelluswhichonewearein.
Tokeepthisinthemindsetofdevelopingagame,let’sputalabelinthefirstonecalledIntroScene,wherewe’dmaybehaveanintroanimationtoourgamewithaStart/Optionsmenu,andinthenextone,putthelabelGameScenetorepresentthatthisiswherethatactualgameplaywouldoccur.
Here’showtodothat:
1. GotothebottomoftheUtilitiespanelandusethesearchfieldtosearchlabel.Thiswillisolatethelabelobject,soyoudon’thavetoscrollthroughtheentirelist.
2. Dragthelabelobjecttothecanvasofthefirstscene.Ifitdoesn’tlooklikeit’stryingtosnaptothescene’scanvas,youmighthavetoselecttheViewportionofthatviewcontrollerscene’shierarchy,usingtheleftpaneoftheMain/Storyboard’smainview.Alternately,youcanalsodouble-clicktheviewintheInspectortogetthesceneinfocussothatyoucanplacethelabelontoit.
3. Aswedragit,trytocenterthelabelasbestaspossible.Thecanvaswillindicatethatweareattheverticaland/orhorizontalpartofthatscenewithdottedbluelines.Dropitinthecenter.
TheUtilitiespaneshouldhavesomefieldsvisiblewhenselectingthelabeltocontrolvariousaspectsofitstextlikefontsize,alignment,andstyle.
4. ThelabelwilljustsayLabelasthedefault,solet’srenameittoIntroSceneforthefirstscenebyeitherdouble-clickingthelabelitselfinthecanvas,orchangingthenameinthesecondfielddownfromTextintheUtilitiespanel.
5. Let’smakethislabelabitmoreprominent,sosingle-clickonthelabel,clickonthe[T]iconintheFontfield,andmakethestyleboldwithasizeof28.
Notehowthelabelisclippedfromthesizeincreaseandhardlyvisible.
6. Simplyclickonthelabelandexpandoutanyoneoftheeightscalingiconsatthecornersofthelabelobjectonthecanvas.
Repositionthelabeltoreturnittothecenterofthescene.
7. CreatethesamelabelforthesecondsceneweaddedbysimplytypingCommand+Dtoduplicatethelabel(astonothavetorepeatallofthesteps)andthendragittothecenteroftheotherscene.Zoomoutasneededandpossiblyclickbackontotheviewpartofthehierarchyifthefocuschangepreventstheabilitytodragthelabelacross.
Thoughratherrudimentaryandwithstillsomemoreworktodowith,thisisallittakestocreateseparatescenesvisually.Ifyouhaveanideaofhowyouwanttostructureyourgame,thisiswhereyoucanstartwiththeuseofstoryboards.Ofcourse,thereisstillmoretodoherebeforewemakethisstoryboardhaveanyfunction.
WecanseethatXcodeisgivingusthefollowingwarning:
Sceneisunreachableduetolackofentrypointsanddoesnothaveanidentifierforruntimeaccessvia-instantiateViewControllerWithIdentifier.
ThisisreferringtotheGameSceneobjectthatisessentiallyorphanedduetonoconnectiontotheIntroScenenortheapp’sentrypoint.
Thisiswhereseguescomeintoplay.Yet,beforeweworkwithseguesandcreateaflowtothesescenesandmore,ifweweretorunthisapp,we’dnoteanotherissue.Wecouldhaveswornthatwecenteredthetext,butifsimulatingorrunningthisin,say,aniPhone6s,thetextiscompletelyofftotheupper-rightside.ThisisbecausethedefaultcanvasisageneralizedalldevicetemplatetobeginwithviaAutoLayout.
AutoLayouthasgotteneasierwitheachnewbuildofXcode,butonecouldstillargueit’sstillabitofahassleattimestofinetune,particularlywhencreatingconstraints(setspaces/marginingbetweenvariousstoryboardobjects).Let’stakeaquicklookathowtoworkwithconstraints.
OnequickwaytoalleviatetheissuewehavehereistojustworkwiththeBaseValuespanelfoundatthebottomcenterofthestoryboardcanvasbyclickingonthew/Anyh/Anytext.Onceclicked,apop-uptableofcellswillappear.Rollingoverwithyourmouseortrackpadtothevariouscellswillbringupanumberofdifferentconfigurationsasopposetow/Anyh/Any.What’sgreataboutthisisthatyoucanchange/addanddeletevariousobjectssimplybasedonthedevicetypeusingtheseoptions.
NoteBeforestoryboardsandAutoLayout,thiswouldinvolvehugeamountsoftestingandrefactoringofcodeinaviewcontrollerorNibclassestogetthelayoutjustthewayyou’dlikevisually.Applewouldthencreatethenextdevicewithadifferentscreensizetopriordevices,itwouldbecomeanevengreaterhassleorthedeveloperwouldriskabrokengameonthenewestdevice.
TomakethelabelsbeinthecenterforalliPhonesinportraitmodeforexample:
1. Hoverandclickonthecenterleft-handsideoftheAutoLayoutpanelwhereit’llsayCompactWidth|AnyHeightatthetopofthatpop-uppanel/table.
2. Thisshouldnowchangethedisplaytextatthebottomofthecanvastow/Compact
h/AnyandshrinkthewidthofthesceneasthislayoutrepresentsalliPhonesinPortraitandofanyheight(soitcouldbeabitoffinheightonanolderiPhone4SasopposetotheiPhone5orlater).
3. Notehowthelabelsareoffcentertowardtheupperright.ThisiswhatwouldhavebeenseeninthesimulatororonanactualiPhoneintheportraitorientation.Dragthembacktothecenter,andtheyshouldnowlookastheyareseeninthisconfigurationofthestoryboard’scanvas.IfdesigningforiPad,thentheotherconfigurationswouldneedtobechangedforthat.
NotePinningwithconstraintscouldactuallystreamlinethisprocess.Forexample,let’ssaythatyouwanttoplaceaPausebuttonattheupperrightcornerofyourGameSceneandyouknowthatnomattertheorientation,itwillalwaysbeatacertaindistance(inpercentagesorpixels)awayfromtherightandtopofadevice’sscreen.Wecanclick
onthepinbutton atthebottomofthecanvastocreatetheseconstraintsinthew/Anyh/Anyconfigurationandskipmanuallyadjustingtheicononeveryoneofthebaseconfigurations.
Xcodealreadygivesusascene,theLaunchScreen.xibfile,which,ifyouhavealreadyranyourcode,wasactuallywhatwasseenfirstbeforethefirstviewcontrollerinthestoryboard.
TohavejustyourMain.StoryboardfilebeatstartupyoucanselectthemainprojectfileatthetopleftcornerintheNavigationpaneandintheLaunchScreendropdownoftheAppsIconsandLaunchImagessection,selectMain.Storyboard.
Then,youcandeletetheLaunchScreen.xibfileifnolongerneeded.Itcanbeagoodfiletoseeworkingconstraints,andifsobeit,itcanbeyourinitialsplashscreenforyourgame.Moreonconstraintscanbefoundhereintheofficialdocumentation:https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithConstraintsinInterfaceBuidler.html
SeguesGameshavescenes,andallsceneshavetransitionsbetweenthem.Seguesaresimplytransitionsbetweenscenesinastoryboard.Seguescomeinvarioustypes:
Show:Thispushesthenextviewcontrollerontopofthecurrentone;italsoallowsforcallingbackifusingaUINavigationControllerinstance.ShowDetail:WhenusingUISplitViewController,aContainerviewcontrolleristypicallyusediniPadappstobrowsenews/emailapps,wheretheleftsideofthepageisaUITableViewControllerobjectandtheothersideofthesamepagearethedetailsofthattable/list.ThiscallsthedetailsfortheDetailViewcontrollerportionofthepagewhentriggeredbyagesturefromtheselecteditemontheUITableViewControllerside.Presentmodally:Thispresentsthenextviewcontrolleroverthecurrentbutinsuchawaythatitcanbecanceled,suchasafull-pagepopup.Popover:ThisislikePresentmodallybutwithmoreoptionsinsizingtocreateasmallerpop-upwindowthatcanbeclosedanddisposedof.Custom:ThisisaversionofaseguethatyoucancompletelycustomizewithOOPcode.
Thetypicalstoryboardstructurewhenbuildingsay,ane-mailapp,willmorethanlikelyneedtomakeuseofanavigationcontrollerandUITableViewcontrollerstostructurethedataandflowoftheapp.Now,wecanverywelldothesamethingforgameapps.GameOver,Menu,Rankings,andPausescreenscouldmakeuseoftheseviewcontrollers.Forourexample,we’llkeepitsimpleandunrestrictedtoletyou,thedeveloper,haveabetterstartingpointtobranchfrom.
NoteOurexamplehereisrathersimple,butinadditiontoprovidingcodeforthisproject,anevenmoredetailedstoryboardwillbeavailableusingvariousviewcontrollersandobjects.
Let’stakecareofthatwarningandlinkupthesescenesaswellasbegintoshowtheoverallstructureofatypicalgameusingstoryboards.
1. First,intheIntroScene,placeabuttonlabeledSTARTrightundertheIntroScenelabel.Placingabuttononastoryboardisdoneexactlythesameaswithalabel.SearchforbuttonorscrolldowntheobjectsintheUtilitiespanelandthendraganddropthebuttonontothescene.
2. NowcreatetwomorebuttonsontheGameSceneview;onebuttonlabeledPauseatthetop-rightcornerofthesceneandanothernamedQuitoppositethePausebuttonontheupper-leftcorner.
3. CreateanewViewControllerobjectonthescene,preferablyaboveorbelowtheGameSceneonthecanvas.
4. OnthenewPauseScene,createalabelPAUSEDthesamewaytheGameSceneandIntroScenelabelsweremade.
5. Then,addtwobuttons,QuitandResume,andplacethemrightunderthePAUSED
label.
Nowtocreatetheseguesvisuallyusingthestoryboard:
1. Control-ClicktheSTARTbuttonobjectontheIntroSceneandthenwhilestillpressingControl-Click,dragtheobjecttowardtheGameSceneonthecanvas.Youshouldseeabluelinefollowyourcursorasyoudragacross.(ifyouneedmorespace,zoomoutabitandalsotemporarilyclosetheNavigationandUtilitiespanelsusingthetollbarbuttons).
2. Dropthispointanywhereontheviewthatisn’tanotherobject;youshouldseetheentireviewglowbluewhiledoingso.
3. ApopupaskingforthetypeofSeguewillcomeup.SelectShow.4. That’sit!You’vecreatedasegue,andyou’vealsotoldthestoryboardthatwhenthe
userclicksthatbutton,it’llopentheGameScene—ViewController.5. Beforeyoumoveontocreatingmoresegues,clickonthedoor-likesymbolonthe
canvasthatrepresentsthesegue.OnthetoprightintheUtilitiespanel’sAssetsinspector,youshouldseeanemptyIdentifierfield.Wecanleavethesegueemptyifwe’dlike,butnamingitcouldbeofuseifwewishtocallthesegueincodewiththefollowingline:
performSegueWithIdentifier("segueIDNameeWithIdentifi)
6. Nowrepeatsteps1through3tocreatethefollowingsegues:
1. LinkGameScene’sQuitbuttonbacktoIntroScene.2. LinkGameScene’sPausebuttontothePAUSEDScene.3. LinkPAUSEDScene’sResumebuttontotheGameScene.4. LinkPAUSEDScene’sQuitbuttontotheIntroScene.
Thewarningshouldnowbegoneasallofthescenesareconnectedwithsegues,andafterpossiblysomeAutoLayoutfixes,runningtheappnowhasagame-likescenestructurethattransitionthewaywe’dnormallyseeinothergames.Wecangofromhereandmakeotherscenes,suchasaGameOverscene,aStageWinscene,orothers.Evenifthismightnotbethewayyou’dlikeyourfinalgame’stransitionstoendup(particularlysincethedefaulttransitionoftheShowseguedoesaquickvertical),thiscanbeaveryquickwayofprototypingyourgamerightoffthebat.Customseguesandseguestriggeredwithcodearehowwecandivedeeperintofinetuningwhenthedefaultsettingmightnotmatchwithourvisionofourgames.
Here’smoredocumentationonmakingcustomsegueclassesifyoureallywanttodivedeeperintosegues:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStoryboardSegue_Class/index.html#//apple_ref/doc/uid/TP40010911-CH1-SW11
SimilarlytohowweControl-Draggedthebutton’slinkagetothenextviewcontrollerscene,wecandothesametoourViewController.swiftfile.
Here’sasummeryonhowtodothatforthefirstviewcontroller:
1. Removetheprevioussegue.Onewaytodosoistoright-clickthebuttonandtoclickonxintheTriggeredSeguessection.
2. ClickontheIntroScene’sviewinthehierarchytogetitinfocus.3. Control-DragabluelinefromtheyellowicononthetopleftoftheIntroScene’s
viewcontrollertotheGameScene’sviewcontrollerandselecttheShowtypeofsegue.
4. ClickonthesegueiconinthecanvasandnowgivetheidentifierofthisseguethenamestartGame.
5. OpentheAssistantEditor(thetwointerlockingcirclesbuttononthetop-rightportionoftheXcodetoolbar);closesomepanestomakeanyneededroom.
6. Control-DragtheStartbuttonintotheViewControllerclass;preferablyatthebottomofthecodebutstillwithintheclass’sclosingbrackets.
7. Thiswillprompttheoutlet/actionpopup.SelecttheActionoptionintheConnectiondropdownandnameitstartButton.
8. ThiswillcreatetheIBActionfunction:@IBActionfuncstartButton(sender:AnyObject){}.
9. Typethefollowingcodebetweenthebraces:self.performSegueWithIdentifier("startGame",sender:nil)
10. Thistellstheviewcontrollertoperformtheseguewhenthisbuttonispromptedusingcode.
StoryboardsversuscodingThere’snosinglecorrectwaytodothedesignstructureofyourappaslongastheMVCmodelisfollowed.Actually,thereareprogrammersouttherewhoarecompletelyfinewithjustusingtheinitialviewcontrollerandneveruseasingleNiborstoryboardfile;thuspurelybuildingtheirgamecontrolledbythelogicoftheircodeandcallstothevariousViewobjectsprogrammatically.IniOSdevelopment,there’ssomewhatofadesignsplitbetweenthreemainbranches,hardcoding,Nibs,andstoryboards.Theoriginalmethodologywascoding;NibscameinlatertofirstallowdirectvisualeditinginXcodeandthenthatevolvedintoStoryboards,furtherbuiltuponwiththeadditionofAutoLayout.
Thereasonthere’sasplitbetweensomedevelopersandstudiosonthevisualstructuremethodologyofaniOSappisbecauseonedrawbacktoNibsandstoryboardsaretheirlackofportability.Ifyouwantedtoportyourgametoanotherplatform,suchasAndroid,atdescentpace,heavyuseofstoryboardswouldmakeitarathertoughtoporttheapptotheotherplatformsincethesedesignfeaturesarespecifictotheiOSplatform.Thisiswhenpurecodewouldbemorebeneficial.Storyboardsthoughgiveusdevelopersaneditable,visualrepresentationoftheapp/gamewewishtomakeandtheabilitytodolittletonochangesasthefamilyofdeviceschange.
Evenothergamedevelopmentengines,suchasUnity,UnrealEngine,andmore,workonamoresandboxing,visualrepresentationmethodologywithyourcodeactingasmoreofacomponenttothevisualasopposedtothefullstructureofeverythingthatappearsbeforeyourgamecharactersevengetrenderedtothescreen.
SummaryInthischapter,wewentoveranumberofappprojectstructuringandintroductiontopics.First,wewentovertheModel-View-Controllerparadigmfollowedbyallapps,gameornot,andtheoveralllifecycleofaniOSappthatfollowsthisstructuring.Next,wereviewedtheentrypoint(s)andpathwayofyourcodeinatypicalappaswellastheupper-levelobjectsusedalongtheway,suchastheApplicationsystemobject,theAppDelegateclass,andviewcontroller.Lastbutnotleast,wediscussedthemaintopicofthechapter—storyboards,segues,andinstructionsonhowtocreateasimplegameflowstructure.Fromhere,wecanseehowrelativelyeasyandquickitcanbetostructurevariousscenesforyourgameandtransitionbetweenthemwithsegues.Again,notethatalthoughstoryboardsarerecommended,theycansimplystartasageneralguidetowardthefinalproduct,whichgivesyou,thedeveloper,theabilitytovisualizeyourgameevenifintheendpreferringamorecode-heavydesignchoice.
Inthenexttwochapters,wearegoingtofinallygetintoreallycodinganddesigningactualplayablegames.Wewillstartoffwith2Dgames,andsinceiOS7,ApplehasgiveniOSdevelopersit’sownframeworktohandle2Dspritesandgamephysics.ThisframeworkisamplynamedSpritekit.
Chapter3.SpriteKitand2DGameDesignNowthatweunderstandthebasicsofcodinginSwift,thegenericflowandclassstructureofaniOSapp,aswellastheoptionalstructuringofappswithstoryboardsandsegues,wecanmoveontotransformingourappsintoplayablegames.
Forthischapter,wewillbeginwiththe2DgamedesignandgamedevelopmentframeworkcreatedexclusivelybyAppleforiOSgamedevelopersknownasSpriteKit.SpriteKitfirstbecameavailablewithiOS7tohelpsimplifythegamedevelopmentprocessforthefamilyofiOSdevices.Theframeworkrunsatypicalrenderinglooptodrawandupdate2Dobjects/spritestoyourgame’sscene.There’smuchgoingonbehindthescenestorunthisloopanddrawyourgamesprites.Thankfully,Applebuiltthefirstpartygamedevelopmentframeworkstodomuchoftheheavyliftingforus.Thisway,wecanfocusmoreonmakingthegameitselfwithoutworryingtoomuchabouthowthatgamewillconnectandrunwiththehardware,somethingdevelopersinthepasthadtocontendwith.
EveryupdateofiOSandXcodecontinuestoaddmoretoolsandframeworkstoimprovetheeaseofgamedesign,includingthecompanionframeworkintroducedfirstatWWDC15foriOS9knownasGameplayKit.GameplayKitcanallowustoseparate,copy,andmodularizethegamelogicandevencopyforuseinfuturegameprojects,beitSpriteKitorthe3Dframeworkofournextchapter,SceneKit.WewillgooverGameplayKitinlaterchaptersaswell.Attheendofthischapter,wewilllookatacompletegameexamplethatisforasimplegameinitsgameplaybutsomewhatcomplexinitslogic.
AbriefhistoryofiOSgamedevelopmentenginesSpriteKitandthe3Dgameframework,SceneKit,werenotthefirstmethodsusedfordevelopinggamesiniOS.We’llquicklyseewhyitbecameawelcomedadditiontothedevelopertoolset.Initially,we,thegamedevelopers,hadtopracticallytalkdirectlywiththeGPUusingtheOpenGLAPItoputboth2Dand3Dgraphics/verticesontothescreen.Ontheupperlevel,therealwayswasFoundationandCocoaTouchtointeractwithusergesturestomanipulateUIKitobjects,butdealingwithgamedevelopmentessentials,suchasSpriteSheets,mipmaps,normalmaps,partialemitters,boundingboxes,andculling,involvedsomeleveloflower-levelstructuring.ApplemadethosecallstovariousgraphicsbuffersandVBOsslightlyeasierwhentheycreatedtheirGLKitframeworkin2011.Thankfully,variousthird-partyframeworks,suchasCocos2D,Box2D,Sparrow,GameMaker,Unity,UnrealEngine,andothersmadethisprocesslessengineering-intensiveinanefforttokeepthedesignaspectofgamedesignthefocus.GameMaker,Unity,andUnrealEnginearemoresandboxing-/drag-and-drop-styledenginesakintothementalitybehindstoryboardsandsegues,whileenginessuchasCocos2DandSparrowaremorecode-heavy/boilerplateOOPstructuresthatshortcuttheinitialcodingbuildup.EnginessuchasUnityandUnrealEnginearegreatinthattheyofferamorehands-onsandboxing-typeenvironmentwithvariousfeaturesthatsimplifytheMVCmodel.Somedrawbackstosuchenginesarethattheyaresometimesclosedsource,usuallycostmoneytoutilizetotheirfullestandaren’tdevice-specific(Unityparticularlyfallsintothiscategory).Workingwiththesevisualenginescouldsometimesleadtooptimizationsbeingrequiredinplatform-specificIDEssuchasXcode,duetoasometimesone-size-fits-allmethodology.Apple’sSpriteKitandthe3DAPI,SceneKitwhichwe’llseelater,giveusafirst-partyplatform-specificmiddlegroundthatgrantsthedeveloperbothupper-levelAPIediting,butevenlower-levelgraphicAPI(OpenGL/Metal)customizations.
NoteThenegativestosandbox/drag-and-drop-styledengineshavedecreasedovertime.EnginesusedbyAAAstudios,suchasUnrealEngine,Unity,Havok,andothershavelessenedtheirupper-levelambiguitybetweentheAPIandtargeteddevices’lower-levelcode.AgoodexampleofthiswouldbeUnity’sIL2CPP,whichconvertstheupper-levelAPIcallsdirectlytofastdevice-specificC++code.ThisincludescodeandgraphicspipelineoptimizationsthatmakeuseofApple’sslimMetalAPI.Thishomogenizationofupperlevelapplicationswithtraditionalboilerplatecodenowallowsdevelopersfromallskilllevelstomakeamazinggames.ThatiswhyfromiOS8,iOS9,andonwards,theApplegamedevelopmentframeworksadoptedamorevisualdesignmethodology.Xcode7introducedgamestatemachines,components,andtheabilitytoedit/copyandreuseplayeractionsandanimationsthroughoutyourprojects.ThisallowsdeveloperstoworkspecificallyiniOS/Xcodewhileutilizingthevisualdesignbenefitsofthedevice-independentgameengines.
Forthischapter,wewilllearnhowtomakeatilepuzzlegamenamedSwiftSweeperusingtheSpriteKitframeworkandwithamoretraditionalboilerplatecodemethod.Thismeansthatwewillmakeourfirstdemogameinacode-heavy/model-centricfashion.NotonlywillthisgiveusalookintotheinnerworkingsofSpriteKit’scodebutitwillalsoletusutilizemorefromtheSwiftprogramminglanguagefromChapter1,TheSwiftProgrammingLanguage.
WewillconcludethischapterbybrieflymentioningApple’slatestSpriteKitdemogame,DemoBots,whichutilizesmoreofthevisualtools/frameworksfromXcode7andlater.Seeingthemorecode-intensivemethodfirstthoughwilllaterletusappreciatethetimesavedwiththesenewertools.
Applehasgoneoutoftheirwaytomimicthevisualdesignmethodologytogamedesignseeninotherenginessincegamedesignisasmuchaboutcode/logicasitisaboutartanddesign.
ThegameloopThegameloopisagamedeveloper’sroadmap.Thenamesdifferdependingontheframeworkandplatform,butthesamerulesapply.Thegameloopcomprisesofallthemethods,physicsupdates,anddrawcallsthatoccurduringasingleframeofyourgameandtheirorderofexecution.Thegoldenruletogamedevelopmentistotrytokeepthisloopalwaysspinninginfulliterationsatnoslowerthan16.6milliseconds,or60framespersecond.
Thereareaspectsofthegameloopthatdon’thavetobecontrolledbythegamedeveloperasmuchastheyusedtobeinthepast,thoughwedohavetheoptiontoworkdowntotheveryGPUcallsusingOpenGL,orevenbetter,Apple’sMetalAPI.Wewilldiscussmoreonthesetopicslateron.
HereiswhattheSpriteKitgamelooplookslike:
TheprecedingisanillustrationgiventousdirectlyfromtheAppleDevelopersite.Weseeanumberoffunctionsthatarecalledduringasingleframe.Thefirstfunctioniteratedthroughisupdate().Theupdate()functioniswhereweaddmostofourowngame-specificupdatesandvariouschecksongameobjects(suchaspositionsandcharacterstatuses).
Theloopstructuregivesustheoptiontodoupdatesafterweknowacertainsetoftasksintheframehavehappened,that’swheredidEvaluateActions(),didSimulatePhysics(),
didApplyConstraints(),anddidFinishUpdate()functionscomeinhandy.
NoteAnyonecomingfromUnitymightbefamiliarwithitsgeneralgameloopfunctions,suchasAwake(),Start(),FixedUpdate(),update(),andLateUpdate().TheSpriteKitgameloopallowssomesimilarcode/renderflow,butaswe’llsee,therearesomeslightdifferences.
Formoreonthegameloopanditsfunctions,seethefollowinglinkfromtheAppledocumentationathttps://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Actions/Actions.html
Utilizingtheothergameloopmethodscouldmakesurecertaincallsinyourgamedon’tgooutoforderandcanevenhelpwiththeimportanttaskofmakingthemostoutofeachframeinafast,efficientmanner.
Forinstance,inthepublicgamePikiPop,mentionedpreviously,here’showthegameusesthegameloopinitsmainGameScene.swiftcode:
//Update()Example
//FrommainGameScene.swift
overridefuncupdate(currentTime:CFTimeInterval){
//Updateplayer
if(player?.isPlayable==true){
player!.update(currentTime)
}
}
TheprecedingcodefirstcheckswhethertheplayerisplayablewiththeisPlayableBoolean.Thisstatuscanmeananumberofthings,likeiftheplayerisaliveornot,isspawning,andsoon.Thegameloop’supdate()function,whichisbeingoverriddenfromitsparentupdate()functionoftheSKSceneobject,takesaparameterofthetimeutilitytypeCFTimeInterval.CFTimeIntervalisaspecialCoreFoundationdoubletypethatmeasurestimeinsecondsandthusupdatestheplayerobject(ifnotnull)duringeachinterval.
AsabriefsummaryofPikiPop,it’saprocedural2Dside-scrollinggamesomewhatsimilartothegameFlappyBird.UnlikeFlappyBird,Pikiisabletotraversethegameinalldirectionsbasedonplayertapsandswipes.Pikicouldgettrappedbetweenthestageobjectsandtheedgeofthestage.
TheprecedingimageisPikigettinginjuredifpushedintotheleft-handsideofthescreen.
Edgesinthatgame’sstagesuseSpriteKit’sownspecialobjectsnamedSKConstraints.Moreontheselater,butinshort,theydictatetherangeandorientationSpriteKitspritescantake.SpritesinSpriteKit(bothdeveloper-definedobjects,suchasPikiPop’sPlayerobjectandthedefaultSKSpriteNode)areallderivedfromSKNodeobjectsthatworkwithSKConstraintsandotherphysics-basedframeworkfunctionality.
WecouldcheckwhetherPikiisbeingpushedagainstthecornerintheupdate()partofthegameloop,butsinceconstraintsarepartoftheframework’sphysicsarchitecture,it’sbesttodothischeckduringthedidSimulatePhysics()portionoftherenderloopofSKSceneasseenhere:
overridefuncdidSimulatePhysics(){
//runcheckonPlayer
letblock:(SKNode!,UnsafeMutablePointer<ObjCBool>)->
Void={node,stopin
/*checksifthenodeistheplayerandismoved/crushedtotheleft
byaphysicsobject.Thisisdonebycomparingthenode'spositiontoa
positionthatis,inthiscase,lessthan26%offtheleftsideofthe
screen;calculatedbymultiplyingthescreen'swidthby0.26*/
ifletplayerNode=nodeas?Player{
if(playerNode.position.x<self.frame.size.width*0.26&&
playerNode.isPlayable){
playerNode.playerHitEdge()
}
}
}
...morecode
Thefirstpartofthiscode,letblock:(SKNode!,UnsafeMutablePointer<ObjCBool>)->Void={node,stopin,isdoneinwhat’sknownasablockoraclosuresyntax,whichSwiftletsusdoratherdynamically.Don’tmindthedetailsofthiskindofcodeforthemoment;justnotethatwechecktheplayer’spositioninxversustheedgeofthewindow’sframeinthisportionofthegameloop.
NoteHere’smoreinformationonwritingblocks/closuresinSwift:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
Tilegame–SwiftSweeperTimetostoptalkingaboutSpriteKitandgetrightintoit!Asstatedatthebeginningofthischapter,wewillfirstshowyouhowtomakeasimple-lookingtilegameinSpriteKitusingtheslightlymoredifficultboilerplate/code-drive-styleddesign.Don’tworry,thisisnotgoingtoinvolvedirectcallstotheGPUwithC++andhandlingextremelytinymemoryrequirementslikeveterangamedevelopersdidduringtheearlyconsoledays.However,wewillbeusinglotsofcode-heavycallswithSpriteKitobjects,functions,andclasses.Granted,gettingdownintothecodedirectlyiscontinuallybecominglessofthedeveloper’sresponsibilityasApplecontinuestomakemoredesign-centricfunctionalitiesinXcode.
Knowingthecodestructurecangiveyouanedgeoverdeveloperscominginonamoretop-downmethodologyandcodingwillalwaysbebehindcustomgamelogic.
WhatisSwiftSweeper?SwiftSweeperisacloneoftheclassictilepuzzlegame,MineSweeper,writtenentirelyinSwift.SwiftSweepermakesuseofSwift’sabilitytouseUnicodeemoticonssothatwedon’thavetousemanyimageassetsandshouldgiveusagreatstartingpointtomakingourowntile/puzzlergamewithdifficultylevels.
Wewillbuildupmuchofthegamefromscratch,butthefullsourcecodecanbefoundathttps://github.com/princetrunks/SwiftSweeper.
NoteAsatthetimewritingofthisbook,thiswasbuiltinXcode7Beta(7A120f)fortheinitialiOS9releaseandoptimizedforiPhone.
Thegoalofthegameistotapeverytileonthegameboardwithouthittingmineshiddenthroughouttheboard.Youdogetsomehelpthough.Everytilethatisn’taminewilltelltheplayerhowmanytilesarounditaremines.Iftheplayerknowsthatatilewithoutadoubtisamineviatheprocessofelimination,theycanplantaflagonthattiletomakesurethattheydon’ttapthatspace.Tapallofthetilesthataren’taminetowinthegame!SwiftSweeperevensavesthetimeittookyoutowinforeachdifficultylevelyouchosetogivethegameabitofreplayvalue.
CreatingourSpriteKitgameNowthatweknowthegoalofourgame,here’showwegoaboutbuildingitinSpriteKit:
1. First,openXcodeandcreateanewproject.2. NowselecttheGametemplateandclickonNext.
3. Next,fillintheproductname.WewillnamethisprojectSwiftSweeperExampleandmakesurethatthelanguageisSwiftwithSpriteKitselectedasthegametechnologyaswellasthedevicessettoiPhone.
4. Then,clickonNext,andwenowhaveabrandnewSpriteKitgameprojectwithanumberoffilesalreadywrittenupforustogetusstarted.
5. Nowclickontheproject’smainfileinthenavigationpaneanddeselectallbutthePortraitselectionintheDeviceOrientationfield.
6. Sincewearegoingtoworkmostlywithcode,wecanalsoeitherignoreordeletetheGameScene.sksfilefornow.ThesefilesareXcode’soptionforyoutovisuallydesignyourgamescene.WewillknowmoreonthesefileslaterwhenweworkwithourmorevisuallydesignedSpriteKitgameexample.
7. BuildandruntheapptoseeApple’sdefaultSpriteKitproject,whichhasHelloWorldwritteninChalkdusterfontandarotatingspaceshipappearswhereyouclickortaponthescreen.
AnoverviewoftheSpriteKitstructureandobjectsBeforeweaddourcode,let’susethistemplatetogetanideaonhowSpriteKit’sbasicobjects,functions,andflowwork.
Aswestatedinthepreviouschapter,AppDelegate.swiftisthemainentrypoint.ThecodethenmovestoGameViewController.swift,whichisachildoftheUIViewControllerclassthatimportstheSpriteKitframework.ThefollowingcodeiswrittenintheviewDidLoad()functionofGameViewController:
overridefuncviewDidLoad(){
super.viewDidLoad()
ifletscene=GameScene(fileNamed:"GameScene"){
//Configuretheview.
letskView=self.viewas!SKView
skView.showsFPS=true
skView.showsNodeCount=true
/*SpriteKitappliesadditionaloptimizationstoimprove
renderingperformance*/
skView.ignoresSiblingOrder=true
/*Setthescalemodetoscaletofitthewindow*/
scene.scaleMode=.AspectFill
skView.presentScene(scene)
}
}
TipDownloadingtheexamplecode
Youcandownloadtheexamplecodefilesfromyouraccountathttp://www.packtpub.comforallthePacktPublishingbooksyouhavepurchased.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.
Usingthekeywordoverride,thisversionofviewDidLoad()cannoweitheraddtoorwelloverridetheparentclass’sfunctionality.super.viewDidLoad()callstheparentclass’soriginalfunctionalityandthenitworksitsowncustomfunctionality.ThisishowSwifthandlestheOOPconceptofinheritance.
Next,weseehowagamesceneisfirstcreatedwithGameViewController.AmajoraspectofSpriteKitisthatitworksinscenesthataremembersoftheSKSceneclass,whicharethemselveschildrenoftheSKNodeclass.TheSKNodeclassesarethemainbuildingblocksofnearlyeveryobjectinSpriteKit.Beitsprites,lights,videos,effects,physicsfields,audiofiles(SKAudioNodes),cameras(SKCameraNodes),orlabels/UIobjects,theyareSKNodeclasses.Theseobjectsallholdimportantinformation,mostimportantlycoordinateinformationofobject’snodefamily.Forgames,thisallowsthedevelopertocreatecustomclasses,suchasEnemies,GameLights,Tiles,andsoon,thatallhavescreenandotherinformationonbothparentandchildnodes.Forexample,wecanhiteveryenemyonthescreenwithanattackbytheplayerbycallinganinheritedfunctioninaparentEnemyclass.Wedon’tneedtocheckforeachindividualtypeofenemybutinsteadenumeratethrough
theparentnodesinthevariousgameloopfunctionsofSKScene:
enumerateChildNodesWithName("player",usingBlock:block)
Doyouremembertheblock/closurecallinPikiPop?ToactuallyuseitinthedidSimulatePhysics()functionofSKScene,wecalltheenumerateChildNodesWithNamefunctionofSKNodetotargetonlythosenodesinthesceneandhavethatblockofcoderunforeachmemberinthescenewiththatname.
playerNode.name="player"
ThenameissimplyastringthatcanbesetusingtheSKNode.nameproperty.Haveeverycustomnodeinitiatewithagivenname(orchangeduringgameplay),andyouhaveawholegroupofobjectsyoucansingleoutinthescene.
YoucanfindmoreonSKNodeinApple’sofficialdocumentationathttps://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKNode_Ref/.
Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfilesTheGameScene.swiftclassinourprojectinheritsfromSKScene,anditistherethatthegameloop/renderingfunctionswementionedearlieroccur.SpriteKitrunsonscenes,andscenescanbetransitionedandseguedtoandfromit.
Inthepreviouschapter,weshowedhowtostructureagameusingstoryboardsandsegues.SKScenemakesitwhereyoudon’tevenhavetousestoryboardsbutjuststraightcodetotransition.Wecanusestoryboards,andwecanalsovisuallydesigneachindividualsceneusing.sksfilesoracombinationofallthreemethods.Withcode,SKScenecantransitionwiththeSKTransitionobjectsandfunctions.Actually,aswe’llseewithSwiftSweeper,wecanjustusecodetomanuallyrefreshassetsinthescenetodotransitions.ThismethodisratheroldfashionedandnotaselegantasSKTransitionstoryboardsandSKSfiles,solet’stakeaquicklookathowtotransitionscenesincodewithSKTransition,storyboards,andbrieflyintoSKSfilesviacode.Later,andinthenextchapter,wewillfocusmuchmoreonthevisualSKSfilessinceeveryupdatetoiOSandXcodecontinuestoputthefocusonthesevisualtoolstoshortenthecodingtimeandworkflow.
AnSKTransitionexampleThefollowingcodechangesthegame’sscene:
overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:
UIEvent?){
super.touchesBegan(touches,withEvent:event)
ifletlocation=touches.first?.locationInNode(self){
lettouchedNode=self.nodeAtPoint(location)
iftouchedNode.name=="SceneChangeButton"{
lettransition=
SKTransition.revealWithDirection(SKTransitionDirection.Up,duration:1.0)
letscene=AnotherGameScene(size:self.scene!.size)
scene.scaleMode=SKSceneScaleMode.AspectFill
self.scene!.view!.presentScene(scene,transition:transition)
}
}
}
TheSKTransitionclassesarereallyjusttypesofsegues.Asintheprecedingcode,thetransitionisadirectionalswitchtothenextscenewiththeSKTransitionDirection.Upenumeratortype.AswesawinGameViewController,thenewsceneiscreatedwiththesimilarfunctionsthatcontrolthescene’sviewsizeandaspectratioandthenpresentsthatscenetotheunwrappedviewwithself.scene!.view!.presentScene(scene,transition:transition).
Alsonotethatthistakesplaceinthesamefunctionasweseeinourcurrentproject’sGameScene.swiftclass,overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:UIEvent?){}.ThisisthefunctionthathandlestouchgesturesfromtheplayerandcheckswhetherthenameofthenodetouchedmatchestheSceneChangeButtonstring.
MoreonSKTransitionandotherneattransitioneffectsyoucangiveyourgamescanbefoundhereintheofficialdocumentation:
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKTransition_Ref/
NoteAsofSwift2.0/iOS9,thistouchdelegatefunctiontakesinaparameterthatisasetofUITouchesviatouches:Set<UITouch>andanoptionalUIEvent.ThisisachangefrompastSwiftiterationsandcouldchangeinfutureupdates.
ASKScene/storyboardexampleHere’sthecodeforaSKScene/storyboardexample:
@IBActionfuncbuttonPressed(button:UIButton)
{
//Removebuttonfromtheview
button.removeFromSuperview()
ifletscene=GameScene.unarchiveFromFile("GameScene")as?GameScene{
//Configuretheview.
letskView=self.viewasSKView
skView.showsFPS=false
skView.showsNodeCount=false
//usedforoptimizationofSKView
skView.ignoresSiblingOrder=true
scene.scaleMode=.AspectFill
skView.presentScene(scene)
}
}
Aswesawinthepreviouschapter,usingthevisualhelpofstoryboardfilescangiveusgreatvisualroadmapstoourapps,bothgameandnon-game.Theprecedingcodeusesalinktoan@IBActionlinkageonastoryboardfiletosetanewscene.
Storyboardsingamescanbegreatfortheprototypingphasewhenweknowjustthegeneralstructureofourgame,andcanbeperfectforthegame’smenunavigationsorevenforallindividualgamescenes*.
Thebuttonitselfisremovedbeforethetransitionviathebutton.removeFromSuperview()calltopreventamemoryleakcausedbythenewscenebeingdrawnoverwhatcouldhavebeenanunseenmenubutton—unseentotheplayerbutnottothegame’smemorystack.
Tip*It’susuallythebestpracticetoonlyusestoryboardsforoverallnavigationmenusandnotforeachindividuallevel/scene.TheSKSceneandSKNodefunctionalitycanletusreusesimilarscenestructuresandsavemuchofthecodingforsimilarlystructuredlevels.Gameswithmanylevelscouldturnourstoryboardsintoawebofconfusingstructuresandthusundotheirinitialpurpose.Sceneswiththeactualgameplaycouldjustbeintheirownsingleviewcontrollerinthestoryboard,andwe’dhavethepause,share,andothermenusbecontrolledbystoryboardsegues.
SKScenetransitionswithSKSfilesA.sksfileisaspecialSpriteKitscenefilethatcanallowthecreationofasceneaswellastheplacementoftheplayer,particles,enemies,andlevelassetsinavisual,draganddropway.Transitioningtoavisuallydesigned.sksfileinSwiftisthesameasourinitialSKTransitionexample.
overridefunctouchesBegan(touches:Set<NSObject>,withEventevent:
UIEvent){
/*Calledwhenatouchbegins*/
letintroNode=childNodeWithName("introNode")
if(introNode!=nil){
letfadeAway=SKAction.fadeOutWithDuration(1.0)
introNode?.runAction(fadeAway,completion:{
letdoors=SKTransition.doorwayWithDuration(1.0)
letgameScene=GameScene(fileNamed:"GameScene")
self.view?.presentScene(gameScene,transition:doors)
})
}
}
ThecreationofthegameSceneconstantwiththeSKSceneinitializerfileNamedandthenpresentingthatscenetotheviewworksthesamewitheitherthe.swiftfileor.sksfile.Thisgivesustheflexibilitytobothcodeand/orvisuallydesignourgamescenes.InthecaseofSwiftSweeper,wewilldothemorecode-centricmethodology,butfeelfreetobuildonthisgameonyourownifyouwishwitheithermorecode,Storyboards,and/orwithvisuallydesignedSpriteKitScene(.sks)files.
Assets,sprites,andiconsAsofXcode7,gameassetsareplacedintheAssets.xcassetsfolder.PreviousversionsofXcodemighthavehadanImages.xcassetsfolderforthegame’siconsandsprites,butthishaschangedandmightcontinuetochangewitheachnewiOSrelease.
AnimagefromApple’sWWDC15conference
StartingwithiOS9andXcode7,theassetsfolderwasgivenevenmoreflexibilitywiththeabilitytohandlethevariousappiconsizes,thelaunchimage,setsofimages,andspriteatlases.ThisalsoallowsustodevelopwithvariousmemorysavingcapabilitiesintroducediniOS9likeappslicing/appthinningandon-demandresources.Theappslicing/thinningfeaturemakessurethatonlytheassetsrelevanttothedevicearedownloaded,whichsavesspaceontheplayer’siPhoneoriPad.On-demandresourcesletustagassetsthatareavailableinthedevice’smemoryonlyduringcertainpartsofourgames.Thisway,wecancreateevenlargergamesforourplayerstoexperiencewithouttaxingthesometimes-limitedspaceintheApplefamilyofdevices.
Youcanfindmoreonappslicing/thinningathttps://developer.apple.com/library/prerelease/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html
Whensettingupyourgameforon-demandservices,somethingthatcouldbegreattoknowintheinitialplanningofyourgames,canbefoundintheofficialdocumentationathttps://developer.apple.com/library/prerelease/ios/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/
SpriteatlasesandanimatingspritesSwiftSweeperactuallydoesn’tuseanimatingsprites;aswe’llsee,itsimplyusesUnicodeemoticoncharacterstoanimatethescreen.Yet,wecan’tdiscussSpriteKitand2Dgame
developmentwithoutmentioningsprites,animatingandoptimizingthemwithtextureatlases/spritesheets,couldwe?Aspriteatlasisacollectionofimagesbundledintoasingleimage,alsoknownasaspritesheetortextureatlas.Whiledeveloping2Dgames,itishighlyrecommendedtousetextureatlasesasopposedtovariousimagesetsbecausetotherenderer,textureatlaseswillequatetofarfewerdrawcallsandthuscanmakesurethatyourgamerunsatthatneeded60fps.TheCollectables.atlasfolderinAssets.xcassetscouldholdallofyourgame’scollectablesandwiththeSKTextureAtlasclass,efficientlydrawthosecollectablestothescreen.Whenstoringtheimagestosaytheplayer’sidle,walking,andjumpinganimations,weusetextureatlasestostorethem.
Creatingatextureatlasisverysimpleandispresentedasfollows:
1. SimplyclickonyourAssests.xcassetsfolderandright-clickonanemptypartofthefolder’shierarchy.
2. ClickonNewSpriteAtlasandjustlikethis,wehaveafolderwherewecanstorevariousspritesforourgame.
3. Makesuretonamethefolderbasedonhowyouwishtocategorizethegroupsofsprites.You’dneedthisnamewhenreferencingthemincode.
Tocreateareferencetothisatlasincodeandanimatethesprites,weuseSKTextureAtlasasfollows:
letPlayerAtlas=SKTextureAtlas(named:"Player.atlas")
lettextures=map(1…4){numberin
PlayerAtlas.textureNamed("player_sprite_normal_\(number)")
}
letanim=SKAction.animateWithTextures(textures,timePerFrame:
self.animationRefreshRate_)
letidleAnimation=SKAction.repeatActionForever(anim)
self.runAction(idleAnimation)
First,thiscodecreatesanSKTextureAtlasreferencetotheplayer’sspriteatlasusingtheinitializerSKTextureAtlas(named:"Player.atlas").Then,wecreateanarrayoftexturesusingoneofSwift’sordersblockmap(NSRange){…}.Thisisaclosureblockthatiteratesthroughthetexturesinthespriteatlasbasedontherangespecifiedinthemapcall.Thenumberobjectisasimpleindexobjectwecanusetorepresenttheindexofthemapping.
Thisisdonebecauseourplayerhasthesespritenamesforthenormal/idleanimation:
"player_sprite_normal_1","player_sprite_normal_2",
"player_sprite_normal_3","player_sprite_normal_4"
Sinceweknowthatthespriteanimationsarenamedwithanindexednamingstructure,it’sbettertouseSwift’sfunctionalprogrammingtools,suchasmap(),heretosimplifythecode.2DSpriteswithmanyframe-by-frameanimations(gamessuchasMetalSlug)couldbeiteratedthroughinsuchafashion.
SKTextureAtlasalsohasaclassfunctionnamedpreloadTextureAtlaseswecanusetopreloadanarrayoftextureatlases:
SKTextureAtlas.preloadTextureAtlases([PIKIATLAS,BGATLAS,COLLECTABLESATLAS,H
UDATLAS,OBSTACLESATLAS])
{
//performothertaskswhileloadingTextureAtlases
}
Thisisgreattomakesurethatastage’sspritesareloadedbeforeenteringthestage.
CreatingourgamelogicForthesakeofsimplicity,MineSweeperwon’thavemanydifferentassetsoranyspritetextures.ItinsteadusesSwift’sUnicodeemoticoncharactercapabilitiesandUIViewcallstodesignthegame’sgraphicsinaratherold-fashioned,veryMineSweeper-likeway.
Notonlydowedothistogiveusasomewhatsimplisticstartingpoint,buttoshowhowSwiftcodeandSpriteKitclassescanletuscreatetheentiregame’slogicandflowwithouttheinitialneedofspriteassets.Thisway,ifdevelopingasateamorbyyourself,thegamecanbemadebeforedoingthesometimesgruelingprocessofmakingwonderfulvisualassets.Thinkingwithcodeandstructurefirstcanensurethatyouhaveaworkingprototypethatyoucanpolishlaterwithsprites,music,andatmosphere.
We’vesofarleftSwiftSweeperwaitingasjustashelloftheSpriteKitgametemplate.It’sabouttimewegettothegame’smodel:
1. First,let’saddourimageassets.Formoreinformation,visithttps://mega.co.nz/#!XhEgCRgJ!4QqKMl1l1P4opWU7OH2wEN_noVQ86z5mxEyLuyUrcQo
ThisisalinktotheAssets.xcassetsfolderofSwiftSweeper.Wecanaddtheseindividually,butthesimplestwayistojustreplaceyourproject’sAssets.xcassetsfolderdirectlyinyourcomputerwhereyourproject’sfolderislocated.YoucanhaveXcodeopenwhileyoudothis,it’llautomaticallyupdatefromtheoriginaltemplatefiles.
2. Next,let’saddthesoundfilesfromthefollowingURL:
https://mega.co.nz/#!T5dUnJZb!NUT837QQnKeQbTpI8Jd8ISJMx7TnXvucZSY7Frw5gcY
3. Addthesoundsbydoingthefollowing:
1. Right-clickontheSwiftSweeperExamplefolderthatholdstheSwiftfilesandthengotoNew|Groupfromthemenu.
2. NamethisfolderSoundsanddragittothebottomofthefileswithinthesameSwiftSweeperExamplefolder.
3. Right-clicktheSoundsfolderandselectAddFilesTo"SwiftSweeperExample".
4. AddthesoundsfromtheSwiftSweeperSoundsfolder,andtheyshouldnowbeinyourproject.
Alloftheassetsshouldbenowintheproject,sonowwecanbuildourgame.Let’sfirststartwiththeactualtiles.
NowcreateanewSwiftfile,nameitTile,andpastethefollowingcodeintothefile:
classTile{
//Properties
//(1)
letrow:Int
letcolumn:Int
//(2)
varisTileDown=false
varisFlagged=false
varisAMine=false
//(3)
//Minescounter
varnearbyMines:Int=0
//(4)
init(row:Int,col:Int){
self.row=row
self.column=col
}
}
Herearesomestepwiselogicweadheretowhilecreatingtiles:
1. Whilebuildinganycodelogic,weusuallyplacethepropertiesaboutthisobjectatthe
top.WeknowthateachtileinagameofMineSweeperwillbepartofarowandacolumn.Thenumberoftherowandthecolumnthistilewillhaveduringgameplaywon’tchangeduringthecourseofasingleround,sowemakethemconstantswiththekeywordletandsetthemwiththetypeIntasweknowthatyoucan’thavefractionsofaroworacolumn,atleastintermsofthetileobjects.
2. Atilecanhaveafewdifferentstates.Itcouldbealreadytapped,itcouldhaveaflagplacedonit,andifit’saamine.Sincethesearetrue/falseproperties,wesetthemwithasBooleanvariablesisTileDown,isFlagged,andisAMine.Wesetthemtofalseinitially.
3. TilesinMineSweepercounthowmanytilesaroundthemaremines,sowecreatetheintegercounternearbyMinestoholdthatinformation.
4. Whenaninstanceofatileobjectiscreated,wewantthegametosetitsrowandcolumnnumberplacementontheGameBoard,sowecreatethedefaultinitializer,init,tohavetwoparameterinputsforboththerowandcolumn.
That’sallweneedfortheTileobjects,solet’smoveontosettingthebuttonfunctionalityoftheseTileobjectswiththeMineTileButtonclass.
CreateanewSwiftfileandnameitMineTileButtonandpastethefollowingcodeintoit:
//(1)
importUIKit
classMineTileButton:UIButton{
//(2)
vartile:Tile
lettileSize:CGFloat
//(3)
init(tileButton:Tile,size:CGFloat){
self.tile=tileButton
self.tileSize=size
letx=CGFloat(self.tile.column)*tileSize
lety=CGFloat(self.tile.row)*tileSize
lettileBoundingFrame=CGRectMake(x,y,tileSize,tileSize)
super.init(frame:tileBoundingFrame)
}
//(4)
requiredinit(coderaDecoder:NSCoder){
fatalError("init(coder:)hasnotbeenimplemented")
}
//(5)
//buttontext;
//replacebuttonwithanSKSpriteforbetterGUIinterface?
funcgetTileLabelText()->String{
if!self.tile.isAMine{
ifself.tile.nearbyMines==0{
return"0"
}else{
return"\(self.tile.nearbyMines)"
}
}
//(6)
return"💥"
}
}
Here’stheexplanationofthecode:
1. SincewearecreatingaUIButtonobject,weimporttheUIKitframeworkforthisobject.
2. Thesearethepropertiesofthisbuttonobject.WeneedaTileobjectnamedtiletoreference,aCGFloatsizenamedtileSizetorepresenttherectanglethisbuttonwilloccupy.
3. TheinitializerforthisclasstakesinaTileobjectnamedtileButtonandaCGFloatnamedsize.Weassigntheclass’sowntiletotileButtonandtileSizetosizeandthenwemakeasquarenamedtileBoundingFramewiththeCGRectMake()method.ThisisdonejustafterwesetanxandyvalueofCGFloattothesquarebasedonthetileSize.TheUIButtonparentinit(frame:)initializerusesthetileBoundingFrameastheparameterviasuper.init(frame:tileBoundingFrame).
4. SinceXcode5,theinitfunctionisneededmainlytokeepthecompilerhappywhiledealingwithUIobjects.
5. ThefunctiongetTileLabelText()returnsastringbasedonthestatusofthetileobject.Ifthetileisnotamine,weknowthatwehavetoeitherplacesomethingfortherebeingnotiles;traditionally,thisisjustablankspaceoranempty""string,butfornow,wearejustplacing0there,leavingthelogicopenforcustomization.Honestly,wecouldsimplyreturnthenestedif-elsestatement’sreturn\(self.tile.nearbyMines),andit’dreturnthesameresult.Aswesee,it’sreturningtheparticularTileobject’snearbyMinesproperty.
6. Ifthetileisamine,thenwereturnthecollisionUnicodeemojicharacter.ThegetTileLabelText()functioniscalledwhentheplayertapsanunflaggedtile.
7. Swift’sabilitytouseUnicodecharactersymbolscanbeagreatvisualaidintheplanningprocessofyourgames.ThecollisionUnicodeemojiusedinline(6)isU+1F4A5(128165).Ifyouseeonlyasquareboxandnottheredexplosion-likecharacter,itcanbeseeninthefullprojectdownloadmentionedearlierinthechapteroratthefollowinglink.
NoteFindmoreinformationonthisemojiathttp://www.charbase.com/1f4a5-unicode-collision-symbol.
GameBoardNowthatwehaveourtileobjectandbuttonlogicthatwillrepresenteachtileobjectnamedMineTileButton,weneedtocreateanobjectrepresentingthecollectionoftheseobjects,thatis,GameBoard.
ThefullGameBoard.swiftcodeisabittoolargetoshowhereinitsentirety,sowewillsummarizeitsmainfeaturesandsegments.
Wecanviewtheentirecodeeitherinthefullprojectlinkmentionedearlierinthechapter,ordirectlybelowinordertocopytoyourcurrentgameprojectfile:
https://mega.co.nz/#!X8FB2aAK
ForourGameBoard,wearelookingtocreateatiledboardof10x10sizethatalsohasthreelevelsofdifficulty:easy,medium,andhard.Tocreatethedifficulty,wesimplyuseanenumeratornameddifficultytostorethegame’sdifficultylevels.
ThemostimportantpropertiesofGameBoardincludeboardSize_(whichissetto10inthiscase),avariablethatwillrepresentthenumberofminesthatwillbeplacednamedmineRandomizer,thenumberofminesactiveontheboardnamedmineCount,andtheTileobjectsthatwillpopulatetheboardnamedtiles.
Makeanoteofthesyntaxusedforthetilesproperty:
vartiles:[[Tile]]=[]
Inthisway,wecancreateanordered2Darray(ormatrix)inSwift*.TheGameBoardobjectwillbasicallystoreanarrayofanarrayofTiletypeobjects.
Note*Swiftdoeshavemorewaystoexpressmatrices,forexample,wecanuseStructstodefineourownuniquematrices.Asatthetimeofthispublication,Swiftdoesnothaveitsowntruefunctionalityforfixedlengtharrays,asweseeinvariousClanguages.However,usingthenestedbraces[[]]isfineforwhatwearetryingtoaccomplish.
TheinitializerforGameBoard,init(selectedDifficulty:difficulty){},takesintheplayer-selecteddifficultyasit’ssingleparameterthenbuildstheboardbasedontheboardSizepropertyandthenusesthefollowingnestedfor-inlooptopopulatetheentireboardwithTileobjects:
forrowin0..<boardSize_{
vartilesRow:[Tile]=[]
forcolin0..<boardSize_{
lettile=Tile(row:row,col:col)
tilesRow.append(tile)
}
tiles.append(tilesRow)
}
Sincethetilesobjectisa2Darray,wefirstneedtoperformthisnestedloopthatfirstcreatesa1DarrayofTileobjects(namedtilesRow)foreachrowandthenaddatilefor
eachcolumninthatrowwiththe.appendfunction.Themaintiles2DarrayisthenappendedthattilesRowarray.
TipIfyouwishtomakeaGameBoardinstancethatisarectangleorofanothershape,you’dhavetotakeintoaccountthedifferingcolumnandrowamounts.Thiswouldmakethenestedfor-loophavemorecomplexitybyneedingaseparatecolumnSizeandrowSizeproperty.Manypuzzlegameswillmaketheirboardslookcomplextotheplayerbutmightstillkeeptheirinternalstructuressimpletoeithersquaresorrectanglesbyinsteadfillinginthattilewithanonplayablesectionorbackground/transparenttile.
It’sawayforadevelopertocutcornerswhileatthesametimeallowingcomplexfunctionalityanddesign.It’swhywebuiltthisgamewithseparateclassesrepresentingtheTiles,thetilebuttonfunctionalities,andthegameboardlayout.
Usinginheritance,wecancontinuetocustomizewhateachtiledoesandthusallowamyriadoffeaturesbasedonasimplefoundation.
It’swhyvideogameshavealwaysbeentheposterchildrentomakethemostoutofobject-orienteddesign.
Don’tworryifatfirstit’stoughtogetafullunderstandingofthis,asnestedloopstendtobebraintwisters.Justobservehowtheinteriorfor-loopwon’texituntilit’sdonefillingincolumnsbasedontheboardSize_property.Thiskindofloopismadeeasierwiththefactthattherowsandcolumnsareallequalat10.
TheinitializerthencallstheresetBoard()function,whichresetsthemineCountto0,anddoestwomorenestedfor-loops:
forrowin0..<boardSize_{
forcolumnin0..<boardSize_{
self.createRandomMineTiles(tiles[row][column])
tiles[row][column].isTileDown=false
}
}
Thisboard-iteratingfor-looprandomlysetswhichtilesareminesusingthecreateRandomMineTiles()functionaswellasresetsthetilestobeinguntouchedwiththetiles[row][column].isTileDown=falsecall.ThecreateRandomMineTiles()functionworksoffthecurrentdifficultylevel,particularlythemineRandomizerpropertythatisdeterminedintheimplementDifficulty()function.ThehigherthemineRandomizervalue,thelessofachancetheiteratedtilewillbemadeintoamine.
Thenextnestedfor-loopinresetBoard()isthefollowing:
forrowin0..<boardSize_{
forcolumnin0..<boardSize_{
self.calculateNearbyMines(tiles[row][column])
}
}
Thisiteratesthrougheverytileontheboardandsetsthenumbertheplayerwillseeif
tapped.Thatnumberofcoursebeingthenumberofminessurroundinganon-minetile,thatis,thenearbyMinespropertyoftheTileclass.
ThisrathercomplexchainofcalculationsbeginswiththecalculateNearbyMines()functionandrunsthroughthearray/tileindexcalculatingfunctions,getNearbyTiles()andgetAdjacentTileLocation().Weprovidedvariousdetailedcommentsineachofthesefunctionstogetabetterunderstandingonhowtheywork.It’sadvisedthatyoureadtheintricatedetailsonhowit’sdonebuttonotmuddyanalreadycomplexgamelogicexplanation,takenotesonthefollowinglineingetNearbyTiles():
letnearbyTileOffsets=
[(-1,-1),//bottomleftcornerfromselectedtile
(0,-1),//directlybelow
(1,-1),//bottomrightcorner
(-1,0),//directlyleft
(1,0),//directlyright
(-1,1),//topleftcorner
(0,1),//directlyabove
(1,1)]//toprightcorner
Ifanylineinthesethreecomplexfunctionsistobeunderstood,it’sthisone.ThenearbyTileOffsetobjectisanexplicitlywrittenarrayoftuples,whichcontainseveryoffsetthatcouldexistaroundasingle2Dtile.Actually,it’sbesttothinkofeachmemberofthisarrayasan(x,y)2DVector.
Thus,ascommentedintheprecedingcode,theoffsetof(-1,-1)wouldbetothebottomleftofthetilesincex=-1(left1)andy=-1(down1).Similarly,(1,0)istotheright,(1,1)isthetop-rightcorner.
Wealsohavetotakeintoaccountthatsometilesareontheedgeand/orcolumnoftheboard,thussomeofthetileoffsetswon’treturnthereferencetoanothertile;they’llinsteadreturnnil.
for(rowOffset,columnOffset)innearbyTileOffsets{
//optionalsincetilesinthecorners/edgescouldhavelessthan8
surroundingtilesandthuscouldhaveanilvalue
letajacentTile:Tile?=
getAjacentTileLocation(selectedTile.row+rowOffset,col:
selectedTile.column+columnOffset)
//ifvalidAjacentTileisn'tnil,addtheTileobjecttothe
nearbyTilearray
ifletvalidAjacentTile=ajacentTile{
nearbyTiles.append(validAjacentTile)
}
}
Thisfor-loopingetNearbyTiles()notonlycheckstheoffsetsofeverytile,butalso,usingthecalltogetAjacentTileLocation(),accountsforedgeorcornertiles.
Again,thesethreefunctionsarerathercomplex,eveninalessline-by-line/semi-genericexplanationoftheirfunctionality.So,don’tworryifyoudon’tunderstandtheflow/orderatfirst.
Finally,forresetBoard(),wecan’twinthegamewithoutknowingiftheplayergoteverynon-minetile,sowegetthatinformationwiththeline:
numOfTappedTilesToWin_=totalTiles_-mineCount
Whentheplayer’snumberofcompletedmoves(countedintheGameSceneclass)equalsnumOfTappedTilesToWin,theplayerwins!
Thisisalldonebeforetheplayermakesthefirstmove!Thisisdoneinordertohavethevaluesalreadypredetermined.Yes,wecouldmakesomeofthesecalculationsduringtheplayer’stouch,butdealingwithboilerplategamelogicisusuallyfastenoughtopreparethegameatloadtimesothatwecanusethegameplaytofocusoneffects,sequences,andothervisualnotificationsduringthegameloop.
ThisfunctionalityiscontrolledbytheGameScene.swiftfile,whichwewillsummarizenext.
PuttingitalltogetherinGameScene.swiftWenowhavethecoreofSwiftSweeper’slogicsetup,butnowit’stimetopresentitinourSKSceneprovidedbythegametemplate,GameScene.Thissceneusesthegame/renderingloopfunctionsthatwementionedatthebeginningofthechapter.
TheSwiftSweeperversionofGameScene.swiftisratherlargeatabout800linesofcode,solikeGameBoard,wewon’tbegoingoveritlinebylinebutinsteadwe’llbesummarizingsomeoftheimportantaspectsofthescene.Asstatedpreviously,everyupdatetoXcodeandiOSbringsmorevisualwaysofsettingupthesescenes,sogettingtoknoweverylineofcodeinthisexampleisn’tnecessary,butstillrecommendedifyoureallywishtodivedeepintohowtousecodetopresentSpriteKitgamescenes.
Thefullcodecanbefoundinthefullprojectlinkmentionedearlierinthechapteror(ifyou’vebeenbuildingitfromscratchthroughoutthechapter)atthelinkmentionedhere:
https://mega.co.nz/#!PgljBL7b
Weusedvarious//MARK:commentstosectionoffpartsofthiscode,soyoucannavigateeasier.Aftercopyingthecodeintoyourproject,youcouldbuildandruntheapp.Aslongaseverythingwasplacedintotheprojectcorrectly,youshouldhaveaworkingversionofSwiftSweeperrunningonyourphoneorinthephonesimulators.PlaythroughitabittogetanideawhatisbeingdoneinGameScenetopresentthegame.Sometimes,seeingagameinactionletsusseethecodebehinditbetter.Ifanyerrorspopup,somethingwentwrongandifallelsefails,youcandownloadthecompletedprojectfromhttps://github.com/princetrunks/SwiftSweeper.
ThefirstvisualentrypointinGameScene,didMoveToView(),isactuallyrathersmallasfollows:
overridefuncdidMoveToView(view:SKView){
self.backgroundColor=UIColor.whiteColor()
stageView_=view
loadInstructions()
}
Wesimplysetthebackgroundcolortowhiteandloadtheinstructions.Again,wedidn’tsaythatthiswasmeanttobeabeautiful-lookinggame.
TheloadInstructions()functionmanuallyplacestheinstructionsspriteonthescreenandsetsthecurrentGameState_enumto.Instructions.Agamestateorstatemachineiscommongamedevelopmentmethodologythatinstructscharacters,theplayer,andthegameitselfwhatstateitisin.Thiscouldbeusedtomakesurethatcertainpartsofthegameplaydon’thappeninpartstheyaren’tsupposeto.iOS9/Xcode7introducedtheframework;we’lldiveintomorelaterchaptersnamedGamePlayKit,which,amongothergamelogicfunctions,workswithstatemachinesthatcanbemodularandindependentfromaspecificscene.ComponentsfromtheclassSKComponentsandmoremodernusageofSKAction,alsointroducediniOS9,workinthesameway,independentfromOOPinheritance.Thinkofmoredynamic/usableversionsofprotocols.
ThenextoverallstepintheGameSceneisthechooseDifficultyMenu()thatcamewiththeremoveInstructions()function,whichwascalledaftertheplayertapsthescreen.Thistapischeckedinthefunctionwementionedinafewexamplesprior,touchesBegan(),usingthegamestateasalogiccheck:
overridefunctouchesBegan(touches:Set<UITouch>,withEventevent:
UIEvent?){
/*Calledwhenatouchbegins*/
fortouchintouches{
//flagbuttonPressed
ifCGRectContainsPoint(flagButton_.frame,
touch.locationInNode(self)){
flagButtonPressed()
}
//instructionsremovedwhentapped
ifCGRectContainsPoint(instructionsSprite_.frame,
touch.locationInNode(self))&¤tGameState_==.Instructions{
removeInstructions()
}
}
}
NotehowthetouchesBeganfunctionisactuallyrathersimple.Itonlychecksifwetappedtheflagbuttonorifwetappedontheinstructions.Whataboutthetiles?Well,rememberthatwemadethesetilesallmembersofUIButtonwiththeMineTileButtonclass.Here’sthefunctionthatcontrolsthis:
functileButtonTapped(sender:MineTileButton){
//exitfunctionifnotplayingthegame
if(currentGameState_!=.MineTap&¤tGameState_!=
.FlagPlanting){
return
}
//revealstheunderlyingtile,onlyifthegameisinthemain
state,akaMineTap
if(!sender.tile.isTileDown&¤tGameState_==.MineTap){
sender.tile.isTileDown=true
sender.setTitle("\(sender.getTileLabelText())",forState:
.Normal)
//sender.backgroundColor=UIColor.lightGrayColor()
//mineHIT!
ifsender.tile.isAMine{
//sender.backgroundColor=UIColor.orangeColor()
self.mineHit()
}
//countsthemoves;alsousedincalculatingawin
moves_++
}
elseif(!sender.tile.isTileDown&¤tGameState_==
.FlagPlanting){
self.flagPlant(sender)
}
}
MembersoftheUIButtonclasssendoutareferenceofwhathasbeentappedtothescene.
Inthisgame,isanobjectofthetype,MineTileButton.Usingthegamestatetocheckifit’slogicaltothescene,weeitherendtheroundifamineishitwiththemineHit()functionorweincrementthemovesperformed(usedtocalculatethewinbycomparingittonumOfTappedTilesToWin_calculatedatthestartoftheround).Ifthegamestateis.FlagPlanting,thenweinsteaddealwiththelogicbehindplantingaflagonthetiles.Tileswithflagsdon’treactto.MineTapgamestatetapsandthus,ifyouputaflagonthewrongtile,youwon’tgetthewinuntilyouuncoverallofthenon-minetiles.
Throughtherestofthecode,we’llfindatimer,alertsfortheplayerbasedontheoutcome,andeventheabilitytosavetimesperdifficultylevelsusingtheclassfunctionsoftheNSUserDefaultsclass.
Again,it’snotexactlyallthatvisuallyelegant,butintricateincodeandmostimportantlyafullyfunctioninggame.WeadviseyoutocheckoutmoreofthecodeinGameScene.swift,butonemajorissuetoourdesignonemighthavecausedinthebeginningisthatthisonlyworkswithiPhones.
Usingvisualtoolssuchasautolayout,seenbrieflyinthepreviouschapter,willalloweasierdesignchangesfortheentirefamilyofiOSdevices.SincemanyofthevisualassetsinSwiftSweeper’sGameSceneweremanuallyplacedintheview(particularlytheinstructions),we’dhavetoaccountforeverydevicetypeincode.Thisispossible,butasthefamilyofdevicesgrows,manualcodeusedforscreenvisualscouldbebrokenrathereasilyinfutureiOSupdatesanddeviceannouncements.That’swhyinournextchapteraboutSceneKitandlater,wewillmostlydivergefromthiscode-centricstructureandembracethehands-ontoolsandnewerframeworkssuchasGamePlaykitfromXcode7andlater.
DemoBotsAsattheinitialpublicationofthisbook,WWDC15recentlycompletedandgaveusagreatnewSpriteKitdemoprojectforiOS9andXcode7namedDemoBots.
DemoBotsisaSpriteKitprojectprovidedbyApplethatusescomponents,statemachines,on-demandservices,GameplayKit,ReplayKit,andmore!
ThefullprojectdocumentationtoDemoBotscanbefoundathttps://developer.apple.com/library/prerelease/ios/samplecode/DemoBots/Introduction/Intro.html
ToseeitinactionfromWWDC15,seethevideoandPDFfilefromtheDeeperintoGameplayKitwithDemoBotskeynote:
https://developer.apple.com/videos/wwdc/2015/?id=609
TheSpriteKitkeynotecanbefoundhere:
https://developer.apple.com/videos/wwdc/2015/?id=604
DemoBots’sgameplayevenhaseasilyeditableenemyAI/navigationschemesusestheSKCameraNodeintroducediniOS9thatfollowstheplayeranddoesn’tmovethescenearoundintheviewasinpastversionsofSpriteKit.Aswementionedatthebeginningofthechapter,mimickingthetoolsweseeinmultiplatformgameengines.
SummaryWewentthroughanumberoftopicshereinthischapter.WefirstspokeonwhySpriteKitwasawelcomedadditiontoiOSafteryearsofdevelopersonlyhavingthird-partygamingframeworks,suchasCocos2DandSparrow.WediscussedhowSpriteKitfitsinthegamedevelopmentecosystemasratherpowerful,multiplatformgameengines,suchasUnityandUnrealEngine,continuetobecomemoreprominent.Next,wewentintotheSpriteKitgameloopandrenderingcyclethatisusedbySKScene.Then,webegantobuildourdemotilegame,SwiftSweeper,anddovemoreintothebasicstructureofSpriteKit’smostprominentobjectclasses.TheiOS9assetsfolderwasreviewedinadditiontotextureatlasesandhowtoanimatespritesusingtheseassettools.Then,wewentintotherathercomplexlogicandcodethatgoesintomimickingatilegamesuchasMineSweeper.
Next,wemoveontoiOS’s3Dgamedevelopmentframework,SceneKit,wherewewilldivergemoretowardsthevisualtoolsApplenowbringstoussinceiOS8/iOS9.We’lltakelessofacode-centricmethodologynowthatweknowthebasicscene/codestructurethatbothSceneKitandSpriteKitshare.SpriteKitscenescanoverlaySceneKitscenes,sowewillseesomeofwhatwehintedatwithApple’sownDemoBotsdemoshortly.
Chapter4.SceneKitand3DGameDesignForthischapter,wewillbegoingovertheiOSframeworkusedfor3DgamedevelopmentknownasSceneKit.SceneKitfirstbecameavailableiniOS7butoriginallywasjustusedforMacOSdevelopment.Previously,developershadtocode3DgamesusingOpenGLorthird-partyframeworksandengines,suchasCocos3D,UnrealEngine,Havok,andUnity.AsthegraphicalpowerintheiOSfamilyofdevicesimproved,sodidtheneedforanimmersive,hands-onfirst-party3Dgamedesignengine.SceneKitshortlybecameavailableforiOSgivingdevelopersanXcodebuilt-insolutiontomake3Dgames.
Inthepreviouschapter,weapproachediOSgamedevelopmentinamorecode-basedmethodology.We’llstillbeworkinginsomecode,butsincetheintroductionofXcode5andXcode6,ApplehasprovidedsomegreatdemosthatshowhowtheIDEcanbejustasvisuallydynamicofagameengineasmultiplatformgameenginesare.ThebenefitofusingXcodeandtheSpriteKit/SceneKitframeworksoverthoseenginesisthatyouhaveadedicateddesignenvironmentforaspecificplatform.Inourcase,thatplatformisiOSandtheApplefamilyofdevices.AsiOSfrequentlyupdatesandcontinuestogivenewfeatures,Xcodeandtheseframeworkswillupdatewithit.Updatestothemultiplatformenginesusuallyoccuratalaterdatewithsometimestheadditionalneedtoinstallpluginstoensurethatyourapprunssmoothlyinfutureupdates.
Inadditiontotheverydynamicandtool-richDemoBotsSpriteKitdemo,theJune2015WorldWideDeveloper’sConferencealsointroducedawonderfulSceneKitdemonamedFox.TheFoxdemoalsomakesuseoffeaturesintroducediniOS9thatwecanuseforeitherSpriteKitorSceneKit,suchasreusableactions,components,andstatemachines.
Inthischapter,wewillgooverthebasicsofSceneKitandwewillmakeasimpleSceneKitscene(knownasSCNScene)usingbothcodeandthevisualdesigntoolsXcodeprovides.Wewillthenaddphysics,lights,andparticlestoourSceneKitobjectsandscene.WewillthenwrapupwithalookintotheWWDC15FoxDemoandsomeofthefeatures/APIsituses,whichbecameavailableiniOS8andiOS9.
NoteInthepreviouschapter,weleftoutmuchoftheseassetcreationfeaturesinourdiscussiononSpriteKit.WithSpriteKitscenefiles,(.sks),wecanalsocreategameassets,suchaslights,physicsfields,boundingboxes/physicsconstraints,normalmaps,textures,entirelevels,andcharactersinthesamefashionthatSceneKitscenefiles(.scn)work.WewillattimesshowtheSpriteKitmethodtosimilarfeatures.
SinceSpriteKitandSceneKitsceneassetsworksimilarlyandcanbetogetherinthesamescene(thankstotheirinheritnode/treefunctionality),wethoughtthatitwasbesttosavethevisualandassettooldiscussionforthischapter.Thepreviouschapter’stalkonthegame/renderloopandmuchofthescenecodefunctionalitywillworkinSceneKitmuchlikeitdidpreviouslyinSpriteKit.
Soinotherwords,wearealreadysetuptodiverightintoSceneKit.
SceneKitbasicsandworkingwithnodesLikeSpriteKit,SceneKitisbasedontheconceptofnodes.SpriteKitobjectsarechildrenoftheSKNodeclass,whileSceneKitobjectsarechildrenoftheSCNNodeclass.
TheprecedingimageistheSceneGraphhierarchyfromApple’sSceneKitintroduction.Aswesee,SceneKithasvariousnodesthatbranchofffromtheSCNSceneclass.TheseincludethegenericSCNNodeforlights,geometry,andthecamera.
Nodesareatreedatastructurethatcanhaveothernodesaddedtothemandhaveinformationofothernodesinthestructure.Asseenintheprecedinggraph,it’sshownwiththechildNode[]arrayandparentproperties.Spatialinformation,suchasposition,scale,andorientation,canbereceivedfromtheseproperties.Thisiswhatmakesnodesuniquetootherparent-childstructuringinobject-orienteddesign(OOD).
InSpriteKit,we’dtypicallyaddanodetooursceneortoanothernodewithinoursceneviatheaddChild()function.InSceneKit,thesamefunctionalityisdonewithaddChildNode().Forexample,themainrootnodeinaSceneKitsceneistheSCNScenenodethatisplacedintheSCNViewnode,thatis,theframework’suniqueversionoftheUIViewclass.Toaddabasicsphereobjecttoourscene,we’ddothefollowing:
letsphereGeometry=SCNSphere(radius:1.0)
letsphereNode=SCNNode(geometry:sphereGeometry)
self.rootNode.addChildNode(sphereNode)
AsstatedwithSpriteKit,workingwithnodesinSpriteKitcanallowustogroupvarious
membersofourgamescenetogetherintotheirownparentnodesandmakeactionsontheminonecallalsoiteratingthroughforloopsorotheriterationcalls.
SpriteKit/SceneKitinteractivityOnegreatfeatureofSceneKitisthatwecanhaveaSpriteKitsceneoverlayour3Dgame.
self.overlaySKScene=skScene
UsingtheSCNViewpropertyoverlaySKcene,wecantakeanalreadyestablishedSKScenenode(whichcanbeacharacter,ananimationsequence,anHUD,andmore)andhavetheminour3Dscene.
Wanttohaveacutespriteanimationoverlayyour3Dcharacter’sstagewinormaybewanttomakea2.5Dgamewith2Dspritesandphysicsoverlayinga3Dbackground?Thenthisishowyoucandoit.
ThemostcommonfunctionalityofmixingSpriteKitwithSceneKitisthatSpriteKitistheHUDfortheSceneKitscene.Thelives,collectables,andcharactericonseenintheearlierFoxdemoshowsaSpriteKitnodeoverlayingaSceneKitscene.
Nodesingeneralcanhelpaddafunctionalstructuretoyourgameandgamescenes.Ahighrelianceonnodesandinheritanceingamedesigndoesn’tcomewithoutitsflawsthough.
Theissuewithinheritance-basedstructuringandgamedesignBeforegoingforward,weshouldmentionaboutacertainpitfallthatcouldplagueagamethatreliestoomuchontheconceptofnodesandeventhegeneralconceptofinheritance-basedstructuringinOOD.Whenpossible,it’sbestnottorelytoomuchoninheritanceforyourgamelogicandworkmorewithwhat’sknownascomposite-basedstructuring.We’llgodeeperintothisinournextchapterwhenwetalkaboutthehelpergamedevelopmentframeworkfirstintroducediniOS9,GamePlayKit,buthere’saglancesothatweknowthatworkingwithinheritanceandevennodesmightnotalwaysbethebestsolutioninourgames.
Atfirstglance,onemightthinkthatinheritance-basedstructuringisperfectlymadeforgamedevelopment.ManyofusfamiliarwithOODknowthatwecanhavegenericparentclassesornodesofourgameobjects,suchasanall-encompassingGameObjectclass,andthenuseinheritanceandpolymorphismtoworkwithuniquechildclassesfromthisbaseclass.Forsmall,simplisticgamesthatwillholdtrue,butgamestendtohaveobjectsthatcouldsharesomeofthesamefunctionality,butmakenosensetohaveinaparent-childstructure.
Takethistypicalstructuringinatower-basedstrategygame:
Inatypicaltowergame,we’dhaveourbase,tower,andenemyobjectsthatcanallinheritfromagenericGameObjectclasswedefine.Towerscanfireatenemiesbutsocanenemiesbackatthetowersandotherplayer-basedobjects.Partofgoodprogramminganddesignistohavereusablecodeandmethods.Normally,we’ddothiswithinheritance.Theprecedinggraphshowstwo-wayinheritancethatcansolvethis.WewouldthenwantaShootingEnemyclassthatinheritsthemovementandshootingfunctionality.Wecan’tdothis,asthatwouldinvolveinheritingfromtwoseparateandratherunrelatedclassesofobjects.InOOD,there’sonlyonechild-parentrelationship.ThenextsolutionshownontherightwouldbetohavethegenericGameObjectclasshavethisfunctionality.TheissuethatarisesisthatouroncesimpleGameObjectparentclassbecomesallbutsimpleandweinevitablywanttoaddadditionalfeaturesandfunctionalitiestoobjectsinourgame.Inthepast,thiswouldinvolverefactoringtonsofcodetoaccommodatewhatessentiallyaresimpledesignadd-ons.Protocolsusedtobesomewhatofasolutiontothisasthey’dforceustomakeaclassinacertainway,buteventheycouldgetconfusinganddon’tinvolvetheimplementationofthesefeatures.
Thesolutionwouldbetoworkwithentitiesandcomponents.
Thisprecedingdiagramgivesanexampleofcomposite-basedstructuring.Withthismethodology,wecanhavecomponentsthatsharesimilarfunctionality,beingusedbymultipleandusuallyunrelatedgameobjects.Thisway,thegenericGameObjectclassinthisexampledoesn’thavetohaveeverypossiblefunctionofitschildclassandwecankeepEnemyclassesasbeingmembersofEnemy.Thesharedfunctionalitycanbewrittenonceandthenusedthroughoutthegameandevenintheothergameswewishtomake.iOS9’sSpriteKitdemo,DemoBots,andtheSceneKitdemomentionedearlier,Fox,bothusecomposite-basedstructuringforactionsandanimations.
It’simportantwhenthinkingwithnodesinbothSpriteKitandSceneKitthattheyareusedinthecontextoftheViewoftheMVCmodel,orinbothframeworks,thecontextoftheirscenes.
AsforscenesinSceneKit,let’smoveontomakingaverybasicone.
OurfirstSceneKitscene–theXcodetemplate3Dartandanimationisaveryin-depthtopic.Wecouldgoonadnauseamaboutmaterials,shaders,lighting,sculpting,PVRtextures,andallofthetopicsofwhatmakesgreat3Dobjectsforgames,movies,architecture,oranyother3Dobject-basedapplication.
Someofthedetailsofthesetopicsarebeyondthescopeofthisbook,sofornow,let’skeepthingssimple.
Let’sworkwiththedefaultSceneKitsceneandobjectsthatXcodegivesusasastart,asshownintheprecedingimage:
NoteAsofthetimeofwritingthisbook,weusedtheSceneKittemplateforXcode7–Beta.Basedontheversionyouuse,theremightbesomedifferences.
1. First,openXcode,createanewproject,andselecttheGametemplate.
2. Next,nameyourproject,makesurethattheGameTechnologyfieldsaysSceneKit,andclickonNext.
3. TheprojectfilesandstructureareaboutthesameaswesawwithSpriteKitbutwithacoupleofdifferences,particularlytheart.scnassetsfolder.Theonlydifferenceisthatthereisnowanart.scnassetsfolderinadditiontoAssests.xcassets.Thisiswhereour3Dobjectsareheld.Clickonthatfoldertoseetheship.daeassetthatAppleprovides.
WiththeSceneKiteditor,wecanviewandeditthefollowing3Dfiletypes:
DAEOBJAlembicSTLPLY
TheexamplegiventousisaspaceshipofthetypeDAEandwiththeship.daefileastheship’stexturefile(texture.png).Beforewelookintothecodeandhowthesceneworks,buildandruntheprogramoneitheryourowndeviceortheXcodedevicesimulator.
Fromthesamplescene,weseeourspaceshiprotatinginfrontofablackbackgroundandwecanchangeitsorientationwhenweswipetheship.Tappingontheshipcausesittoglowredforamoment.
Let’snowseewhat’sgoingonwiththecodeandthenwe’llgetintothetoolstheeditorgivesustoeditourobjectsandsceneswithoutanycode.
SceneKitprojectflowandstructureLikeSpriteKit,aSceneKitsceneusesthesamegame-renderingloopaswesawfromthepreviouschapterandthesametypeofentrypointstructuringwementionedinChapter2,StructuringandPlanningaGameUsingiOS9StoryboardsandSegues.WehavetheAppDelegate.swiftfilethatisourentrypointwiththeabilitytocontrolspecialappfunctionalitybasedonupperleveldeviceevents,suchastheappclosing,goingintothebackground,andcomingbackfrombeinginthebackground.WealsohavethelaunchscreenandMain.storyboardfilesasseenbeforeinSpriteKit.
ThedifferencewiththeMain.storyboardfileisthatithasaSceneKitsceneicon,shownwiththecube,asseenintheprecedingscreenshot.
TheViewControllerclasstheAppDelegatemovestoistheGameViewController.swiftclass.Thisiswhereallofourcodeforthedemotakesplace:
overridefuncviewDidLoad(){
super.viewDidLoad()
//createanewscene
letscene=SCNScene(named:"art.scnassets/ship.dae")!
...
WeseethatwebeginwiththeoverwrittenviewDidLoad()function.SceneKitletsuscreateanentirescenewithevenaninstanceofour3Dobject/assets,asseenfromtheunwrappedletscene=SCNScene(named:"art.scnassets/ship.dae")!call.Thissimplycreatesthesceneobject.Togettheobjectseenonthescreen,westillneedtoattachthistotheSCNViewnode,aswewillseelaterinthefunction.
Let’slookatsomemoreofthecodehere:
//(1)createandaddacameratothescene
letcameraNode=SCNNode()
cameraNode.camera=SCNCamera()
scene.rootNode.addChildNode(cameraNode)
//placethecamera
cameraNode.position=SCNVector3(x:0,y:0,z:15)
//(2)createandaddalighttothescene
letlightNode=SCNNode()
lightNode.light=SCNLight()
lightNode.light!.type=SCNLightTypeOmni
lightNode.position=SCNVector3(x:0,y:10,z:10)
scene.rootNode.addChildNode(lightNode)
//createandaddanambientlighttothescene
letambientLightNode=SCNNode()
ambientLightNode.light=SCNLight()
ambientLightNode.light!.type=SCNLightTypeAmbient
ambientLightNode.light!.color=UIColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)
//(3)retrievetheshipnode
letship=scene.rootNode.childNodeWithName("ship",recursively:true)!
//(4)animatethe3dobject
ship.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0,y:2,
z:0,duration:1)))
//(5)retrievetheSCNView
letscnView=self.viewas!SCNView
//setthescenetotheview
scnView.scene=scene
//(6)allowstheusertomanipulatethecamera
scnView.allowsCameraControl=true
//showstatisticssuchasfpsandtiminginformation
scnView.showsStatistics=true
//configuretheview
scnView.backgroundColor=UIColor.blackColor()
//(7)addatapgesturerecognizer
lettapGesture=UITapGestureRecognizer(target:self,action:
"handleTap:")
scnView.addGestureRecognizer(tapGesture)
TheviewDidLoad()functionmentionedearlierisprovidedtousinthetemplate.It’sactuallyrathersimpletofollowandotherthanthehandleTap()function,doespractically
allthat’sneededtocreatethisscene.Anyonewho’screated3DgraphicsinOpenGLeitherforiOSorotherplatformswouldappreciatehowSceneKitgivesusanumberofsimpleupperlevelcontrolsforthesceneandobjects.Herearemoredetailsoftheprovidedcode:
Online(1),anSCNNodenamedcameraNodeiscreated,andweassignthecameraattributeofSCNNodetotheSCNCameraNodetype.Then,thecameraisplacedinathree-dimensionalspaceusingtheSCNVector3()functiononthecamera’spositionproperty.Inthiscase,thecameraisplacedat(x:0,y:0,z:15).Inotherwords,thexandycoordinatesaresetattheoriginwhilethecameraismovedslightlybackwardsinthezaxis.
YoucanfindtheSceneKitcoordinatediagramathttps://developer.apple.com/library/ios/documentation/SceneKit/Reference/SceneKit_Framework/
ThecoordinatesysteminSceneKitiswhat’sknownasaRight-HandedCoordinateSystem.Onetricktounderstandthe3Dcoordinatesisifwetakeourrighthand,makeagun-likegestureoutwithourthumbupintheairandpointerfingerstraightaheadofuswhileourmiddlefingertothesideatarightanglefromthepointerfinger,we’dhaveourx,y,andzcoordinates.Yourmiddlefingerwouldbeonthexaxis(left/right),yourthumbwouldbeontheyaxis(up/down),andyourpointerfingerwouldbeonthezaxis(backward/forward).
Inthe(2)blockofcode,weareaddinglightstoourscreen.SceneKit,aswellasSpriteKit,letsuscreateanumberofdifferentlightingeffects,fromambientocclusion,the
useofnormalmaps,andmore.Here,aninstanceofSCNNodeiscreatedwiththenamelightNode,andtheSCNNodepropertylightisassignedtheSCNLightclasstype.Thefirstlightcreatedandaddedtothesceneiswhat’sknownasanSCNLightTypeOmnitypelight,asseenfromtheimplicitlyunwrappedcalllightNode.light!.type=SCNLightTypeOmni.Thistypeoflightistypicallyusedmorefordebuggingasthenextlightadded,ambientLightNode,wouldbeoneofthetypesusedtocreatetheatmospheretoyourgame.Asweseewiththeline,ambientLightNode.light!.color=UIColor.darkGrayColor(),wecanassignacolortothatlightincode.
MoreinformationonSCNLightscanbefoundathttps://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNLight_Class/index.html
We’llsoonseehowtovisuallyaddlightsandotheraspectsofthedemoviewDidLoad()functiontoourscene,butit’susuallybeneficialtounderstandtheboilerplatecodebehindthescenes.
Intheline(3),letship=scene.rootNode.childNodeWithName("ship",recursively:true)!ishowweaddourshipobjecttothescene’srootnode.Thisisnottoomuchdifferentthanotherobjectsinthescene.Ittakesthestringshipfromthenameofourship.daeobjectintheart.scnassetsfolder.Therecursively:trueparameterinthechildNodeWithNamefunctiontellsthescenethatitshouldaddallchildnodesoftheobjecttothescene.Dependingonhowthe3Dobjectwasmodeledandriggedinitsoriginal3Dmodelprogram,theobjectmighthaveacomplexarrayofchildnodes.Settingrecursivelytotruewilliteratethroughnotjustthechildnodesbuttheirchildnodesaswell.
Thefollowinglongline(partofline(4))isacompactwayoftellingtheshiptorotatecontinuallybyx,y,and/orzanglesbasedonitscurrentorientation:
ship.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0,y:2,
z:0,duration:1)))
Thiscanbebrokendownintoitsvariousparts,asit’sanSCNActionwithinanSCNAction,namely,therotateByXfunctionwrappedintoarepeatActionForeverfunctionofSCNAction.ActionsinbothSceneKit(SCNAction)andSpriteKit(SKAction)cannotonlybeaddedtoobjectsbycodebutalsoinXcode’svisualeditor,asweshallseelaterinourreviewoftheFoxdemo.
FindmoreonboththeSCNActionandSKActionclasseshere:
ForSCNAction,refertohttps://developer.apple.com/library/ios/documentation/SceneKit/Reference/SCNAction_Class/
ForSKAction,refertohttps://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKAction_Ref/
Inline(5),wecreatetheSCNViewobjectandassignitastheviewofGameViewControllerwiththelineletscnView=self.viewas!SCNView.Thesceneanditsnodesthatwecreatedwiththeobjectnamedscenebackinline(1)thengetsassignedtothesceneattributeofscnViewviascnView.scene=scene.Thereisaslightbitofambiguityasto
whichsceneisassignedtowhatnode,butthisessentiallyhastodowiththesettingupofrootNodeitself.
Thenextfewlines(of(6))showsomeofthepropertiesthatwecanusefromtheSCNViewclass;thefirstbeingtheabilitytocontrolthecamerawiththeallowsCameraControlproperty.Settingthistofalsewouldpreventtheplayerfrombeingabletomovethecameraabout.Thiscouldbegreatforin-gamecutscenesorlockingthecameraduringapartofastagewhereitwouldbenecessary.ThelinescnView.showsStatistics=truetellsthescenetoshowanyrenderingdatathatwouldbebeneficialtodebugging.Forexample,wecouldseetheframespersecond(fps)ourgameisrunningat.
ThisisequivalenttoaSpriteKitscene’scodepartofskView.showsFPSandskView.showsNodeCount,whereskViewisthenameofanSKViewobject.
Thenextline,scnView.backgroundColor=UIColor.blackColor(),allowsustosetthebackgroundcolortoblack,justthesamewayaswedidwithambientLightNode.light!.color=UIColor.darkGrayColor()usingtheUIColorclass.
SceneKitDebuggingOptionsTipAsofiOS9,evenmoredebuggingoptionsbecameavailablethroughtheuseoftheSCNDebugOptionsstructandthedebugOptionsattributeofSCNView.
Ifweweretowritethefollowing,we’dbeabletoseeourship’sboundingboxes:
scnView.debugOptions=.ShowBoundingBoxes
ThereareotheroptionssuchasShowLightInfluences,ShowPhysicsShapes,andShowWireframe.
WWDC15’sFoxDemowiththe.ShowBoundingBoxesandShowPhysicsShapesoptionsenabled
Finally,inline(7),lettapGesture=UITapGestureRecognizer(target:self,action:"handleTap:")createsaUITapGestureRecognizerobjectnamedtapGesture,whichwillcallthefunctionhandleTap(gestureRecognize:UIGestureRecognizer)whenanytapisperformedandscnView.addGestureRecognizer(tapGesture)addsthatrecognizertothescene.
HandlinguserinputinSceneKitTheUITapGestureRecognizerobjectsaregreatinordertoselectivelyorganizetheinputwereceivefromtheplayer.ThisgoesforbothSceneKitandSpriteKitscenes.Wecanhaverecognizersfortaps,swipesineachdirection,panning,pinches,andlongpresses;longpressesaregreatforwhenyou’dneedtopossiblyhandleacharacterchargingtheirattack.
Here’sthedocumentationoftheUITapGestureRecognizerclassforreference:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/index.html
Let’stakealookatthathandleTapsfunctionasitcontainsanobjectoftheSceneKitclass,SCNTransaction:
funchandleTap(gestureRecognize:UIGestureRecognizer){
//(1)retrievetheSCNView
letscnView=self.viewas!SCNView
//checkwhatnodesaretapped
letp=gestureRecognize.locationInView(scnView)
/(2)
lethitResults=scnView.hitTest(p,options:nil)
//checkthatweclickedonatleastoneobject
ifhitResults.count>0{
//retrievedthefirstclickedobject
letresult:AnyObject!=hitResults[0]
//getitsmaterial
letmaterial=result.node!.geometry!.firstMaterial!
//(3)//highlightit
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
//oncompletion-unhighlight
SCNTransaction.setCompletionBlock{
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(0.5)
material.emission.contents=UIColor.blackColor()
SCNTransaction.commit()
}
material.emission.contents=UIColor.redColor()
SCNTransaction.commit()
}
}
Inline(1),wearejustcreatingareferencetothecurrentSCNViewobjectnamedscnView.Next,theconstantpiscreatedusinggestureRecognize.locationInView(scnView).Whatthisisdoingiscapturingthegesture’slocationintheviewwewishtokeeptabson.Inthiscase,it’stheentireview,scnView.Ifwehadsubviews,sayagame’smenuscreen,thenwecouldifwe’dwishonlytargetgesturesthereinthisfashion.
NoteIfbuildingagamewheretheplayerhastotapatthespurofthemomentandmanytimesforacharacter’smovementordodging,wedidfindthetouchesBegan()functionalitywe
spokeaboutinSpriteKittobeabitfasterthanUITapGestureRecognizer.ThismighteventuallybecomeamootpointwitheachnewandfasteriOSdevice,butifyourgame’scontrolsareheavilydependentonquicknessoftheplayer,youmightnoticesomelaginresponsetothegesturesviatheUITapGesterRecognizerapproach.Thiscouldeffectthegoalofyourgame,sotrythetouchesBegan()functiontoseewhatworksbestforyourgame.UsingtouchesBegan()forswipesandothernon-tapgesturescouldberathertricky,sothere’satrade-offthereonthedevelopmentsidetoo.
Nextinline(2),wetakeacountofhowmanyofthesegestures,tapsinourcase,werecapturedintheviewusingthehitTest()functionofSCNViewandonlycountingifthatgesturemadecontactwithanyobjectinourscenebypassingthepositionconstant,p,asaparameter.ThefunctionhitTest()returnsanarrayofeventresults,andthecountpropertythencountshowlargethatarrayis.Wecanthencaptureareferencetothefirsttapbyreferencingthefirstmemberofthatarray.Weonlyhaveasingleobjectinthisdemoprovidedforus,thespaceship,sowecanjustgetaninstanceofSwift’smostupperparentobject,AnyObject.OurhitTestobject,hitResults,isanarraycontainingreferencestoeveryobjecttappedinthiscontext.Again,thisisjustourspaceshipobject,sowecansimplytakethefirstobjectinstancedathitTest[0].Thisiswhattheresultconstantrepresents.
Thelineletmaterial=result.node!.geometry!.firstMaterial!showsushowwegetareferencetothatobject’smaterialbydrillingdownthenode’schildrenusingthedotoperatorwhilealsoimplicitlyunwrappingeachnodeviatheexclamationpoint(!).Thismaterialreferenceisneededforwhenthetapneedstomakethespaceshipturnred.
TipThisisactuallyanicebroadexampleofhowwecanselectonlycertainobjectsinourSceneKitscenetobethefocusofaplayer’sinput.Here,itjustpicksanyobjectusingthebroadtypeAnyObjectclass,butimagineagamewhereonlyacertaintypeofcharacterorcharactersareselectable;thinkofanisometrictopdownshooterorreal-timestrategy(RTS)game.Wecouldpossiblycheckwhetherthetappedobjectonlyisamemberofacertainclasstype(isKindOfClass())orconformstoacertainprotocol(conformsToProtocol())beforetakinganyactiononthoseselectedgameobjects.WanttheplayerinyourRTSgametoonlytakeactionsonTankobjects?ThencombiningthiswithamenuthattellsthegamewhichobjecttypeisthefocuscouldbewhatgivesyouthatabilityhereinSceneKit.
Inline(3),thedefaultSceneKittemplatealsohandsusthisusefulbitofcodeshowingtheuseofSCNTransaction.TheSCNTransactionclassfirstbecameavailableiniOS8,andwecanthinkofSCNTransactionasalaundrylistofchangesandanimationswewantinthescenetotakeplaceatacertainspecifiedsetoftime.AnSCNTransactionclassbeginswiththeSCNTransaction.begin()callandendsattheSCNTransaction.commit()call.Scenegraphanimationcallsthatarewithinthatblockgetcalled,bydefault,witha0seconddelay.Inmanycases,we’dwanttocontrolthedurationoftheseanimations,thusweusethesetAnimationDuration()functionatthebeginningoftheSCNTransactionblocktosetthat.ThelineSCNTransaction.setAnimationDuration(0.5)setsthetimeto
completethisblockathalfasecond.DonotethatwithinthisblockisanotherblockofcodestartingwithSCNTransaction.setCompletionBlock{…}.WhatthisdoesisexecutethecallonlyaftertheSCNTransationblockit’swithincompletes.Inthecaseofthistemplatedemo,atfirstforhalfasecond,theshipishighlightedred,asdoneinthematerial.emission.contents=UIColor.redColor()line.Afterthiscompletes,foranotherhalfasecond,theshipisbroughtbacktoit’soriginalcolorbysettingitsmaterialemissionbacktoUIColor.blackColor().Thisisabitconfusingatfirstbutthereareaslewofanimationsandtransactionswecandoforoursceneswiththismethodinjustoneblock.Checkoutthislinktothedocumentation;othertransitions/transactionscanbeforfadingin/out,camerafieldofview,rotation,translations,lighting,andmore:https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNTransaction_Class/index.html#//apple_ref/occ/clm/SCNTransaction/valueForKey
AsforthedefaultSceneKittemplate,that’sallthecodeusedtomakethescene.It’sabasicsceneandfarfromagame,butitshouldgiveusabasicunderstandingofwhatessentiallymakesupthemainstructureandlogicofasceneinSceneKit.BeforewelookintotheFoxdemoandthusanactualfullgameproject,let’slookatafewotherfeaturesthatwereaddedtoXcodeasofiOS9/Xcode7.
SceneKitfeaturesintroducediniOS9/Xcode7Let’sgobacktotransitionsandanimations.AsofiOS9,wecanchangeacharacterorother3Dobject’sblendmodeveryeasilyinSceneKit.
AdisplayofthevariousblendmodesinSceneKitfromWWDC2015
Blendmodescanbechangedsimplywithoneline,aSCNMaterial.blendMode=.Add,whereaSCNMaterialisanobjectrepresentingthematerialofSCNNode.Changingblendmodescancreateanumberofeffects.Somegamesuseaplayer’sghosttoshowapastrunthroughtheyaretryingtobeat,orthere’sthefadeeffectbosscharactersmakewhendefeated.CombinewithSCNTransactiontohavecharactersfadeinandoutofthesemodes.
Audionodesand3DsoundAsofiOS9,wecanplace3DsoundsintoourSceneKitscenes.TheaddAudioPlayer()functionoftheSCNNodeclassfunctionletsusappendasoundtothatnode,andwhereverthatnodeisin3Dspace,thesoundwilladhereto3Daudiomixing;thatis,iftheaudiosource’spositionalpropertyissettotrue.Here’showwe’dcreate3Dsoundwithaudionodes:
letsource=SCNAudioSource(named:"sound.caf")
letsoundEffect=SCNAudioPlayer(source:source)
node.addAudioPlayer(soundEffect)
source.positional=true
source.loops=false
Thisgivesasoundeffecttothegameobject,theSCNNodenamednode.
Toactuallyplaythesound,we’dneedtocallSCNActiononit,asshown:
letaction=SCNAction.playAudioSource(source,waitForCompletion:true)
node.runAction(action)
ThewaitForCompletionpropertymakessurethattheactiongoesaslongasthesoundis.Thismightnotbethebestforacharactersoundeffectthoughasyoumightwantitstoppedmidway(thatis,theplayerhitstheenemy,cancelingtheirpreviouslystartedchant,yell,orsomethingtothatdegree).
AmbienceandmusicToaddmusicandambience,wecouldfollowexactlythesamemethodasaddingasoundeffecttoanode:createanSCNAudioSourceobject;addthattoanSCNAudioSourceobject;andaddthistoournodewithaddAudioPlayer.Theonlydifferenceisthatwe’dloopthemusicandsetit’spositionalpropertytofalseasfollows:
source.positional=false
source.loops=true
SpriteKitscenetransitionsinSceneKitSpriteKithassomegreatscenetransitions.Wecouldmakeitlooklikeadoorisopeninguporapageisturning.Thiscouldaddextracharacterandpolishtoyourgame.BeforeiOS9,wecouldn’tdothese2Dtransitionsinour3DSceneKit,butsinceXcode7andiOS9,wecandosoinSceneKitandhere’show:
aSCNView.presentScene(aScene,withTransition:aSKTransition,
incomingPointOfView:nil,completionHandler:nil)
Again,aSCNViewisjustageneralreferencetosomeSCNViewobjectandwhenwepresentthescenetothatview,wehavetheoptionofpassinganSKTransitionobjectforthewithTransitionparameter.TheincomingPointofViewparametercanbeareferencetoacamera’spointofviewduringthetransition,andthecompletionHandlerparameteristhenameofacompletionblockthatiscalledafterthescenetransitions.Forexample,wecouldcallthefunctionsthatstartthecountupofourlaststage’sscoreinascorescenethatwastransitionedtoafterthestagecompleted.Wemightnotwanttobeginthecountingandotherfunctionsofthenewsceneuntilweknowthatthescenehasbeen100%transitionedtoor,inthiscase,afterweknowthetotalpointsfromthepriorscene.
CheckoutsomemoreexamplesofSKTransitionontheclassreferencepage,maybethere’satransitionthatcouldhelpaddtoyourgame’sdesign:
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKTransition_Ref/index.html
FoxdemoWe’vebeenspendingmuchofourtimeinbothSpriteKitandhereinSceneKitontheboilerplatecodethatmakesupourgamelogic.AsXcodecontinuestoupdate,sodoesthevisualdesignfeaturesforiOSgamedesignthatdon’tinvolveastrongunderstandingofcode.There’salwayssomescriptinginvolved,butoneofthekeyfeaturesingamedesignis,well,thedesignaspectofit.AttheWWDC15event,theintroductiontoiOS9andXcode7wasagreatgamedemothatcannotonlyteachussomeofthevisualdesignfeaturesthatXcodecando,butalsogivesusabeautifulstarttoaplatforminggameinSceneKit.ThatdemoisnamedFoxandgranted,thoughitactuallystarsaredpandaandnotafox,wecouldforgivethatmixupforhowfeature-richandessentialitcanbetolearnhowtodevelopSceneKit-powerediOSgames.
TheFoxdemoimageshowingourplayercharacterandlevelassets
There’smuchmoretothisdemothanwecanshowhere,soit’sencouragedtodownloaditforyourselfandcheckoutalloftheSceneKitfeaturesitprovides.WewillfocusonafewtopicsyettobecoveredineitherSceneKitorSpriteKit,suchasparticles,physics,andthescenegraph.TheFoxdemoalsomakesuseof3Dgame/artdesignfeatures,suchasskyboxes,ambientocclusion,cubemaplighting,collisionmeshes,andmore.Itreallyisanice-qualitydemotomakebeautifulgamesiniOS.
HereisthedownloadlinkprovidedbyApple:
https://developer.apple.com/library/prerelease/ios/samplecode/Fox/Introduction/Intro.html
NoteAtthetimeofwriting,theFoxdemowaswrittenonlyinObjective-C.Wehavefocusedon
Swiftintheentiretyofthisbook,butdon’tworrytoomuchifsomeaspectsofObjective-Careforeigntoyou.ThegoalistoseethevisualtoolsXcodeprovides.Inafuturedate,theFoxdemoisboundtobeavailableinSwift,beitbyApplethemselvesorbythird-partyprogrammers.
ParticlesystemsSomeofthebasicassetsinanygame,beitSpriteKitorSceneKitbuilt,arethevariousparticleeffectswe’daddtocharacters,objects,oranentirescene.Particlescanaddtothefeelofcollectingthatitem,givetheplayerasignalthatsomethingishappeningtotheplayer,liketheyaregainingorlosinghealth,orshowthepresenceandpowerofanincomingbossfight.
CollectableparticleeffectfromtheFoxdemo
Inthepast,theprocessofmakingaparticleeffectwouldbetomanuallycreatesometimesrathercomplexparticleemittershaderobjectsusingOpenGLcode.Thiscanstillbedoneifwesochoose(usingeitherApple’sfast,low-levelAPI,Metal,orOpenGL),butovertime,theprocessofvisuallycreatingandeditingparticleeffectshasgotteneasier.NottoolongagointheiOSdevelopmenthistory,frameworkssuchasCocos2D/Cocos3Dallowedustousethird-partyparticleeffectsbuilderstoimportintoourgames.WithbothSpriteKitandSceneKit,XcodeasofaboutiOS7/iOS8,amorevisualrepresentationofparticleswascreatedinXcodethussavingusalargeamountoftimeandeffortincreatingtheeffectswewantandexpecttoseeinourgames.TheimagepreviouslyshowndisplaystheXcodeparticlesystemseditorwiththeFoxdemo’scollectablesparkleeffect.
TocreateyourownparticleeffectinSceneKit,followthesesteps:
1. CreateanewfileaswedidinthepastbynavigatingtoFile|New…orsimplythekeyboardshortcutcommand+N.
2. WethenselecttheResourcesectionunderiOSandselecttheSceneKitParticleSystemtemplate.(IfworkingwithSpriteKit,selectSpriteKitParticleFile.)
3. BoththeSpriteKitandSceneKitparticleoptionsgiveusalistofbasicparticletemplateswecanstartfrom,suchasReactor,Sparkle,orBokeh.Selectoneofyourchoosingorcheckoutacollectableonehereinthedemo.ForSpriteKit,thiscreatesanSKSfileandtheimagemaskfortheparticles.TheSceneKittemplatecreatesthe3DparticleeffectviaanSCNPfileandtheimagemask.
Let’stakealookbackattheparticlesystemwecreatedforthecollectableparticleinthedemo.Ifnotselectedalready,clickontheattributesinspectortoviewvariouscontrolswecanedittocustomizeourparticleeffects.
Feelfreetotestanumberofthevariablesandfieldswithintheinspector.There’sthebirthrate,whichcontrolshowoftentheparticlesrestarttheirstartandendanimation,theimage,whichcanmakeuptheshapeandcoloroftheparticles,andthevariousanglesthatdeterminetheoveralldirectionoftheeffects.There’salsotheLoopingdropdown,whichkeepstheparticlesrepeatingduringthelifeoftheparticlesysteminthescene.Additionally,theaffectedbygravitytoggleiswhatweusetohavetheparticlesfallbasedonthescene’sgravity.Thecollectionparticlesloopconstantlywithoutgravity,andtheconfettiparticleshappenonceandfalltogravity,aswe’dexpectconfettitobehave.Ifanobjectinourscenehasaphysicsfield,wecanalsohavetheparticlesreacttothat.
PlacingparticlesintoourpiosceneWhenwecreateSpriteKitorSceneKitparticles,wecancalltheminoursceneviacodeineithertheSpriteKitorSceneKit:
SpriteKitSpriteKitparticlesaren’tintheFoxdemobuttobacktrackabittoourtalkonSpriteKit,ifwe’dwantedtoaddparticlestoa2DSpriteKitgame,here’sanexampleofhowwe’daccomplishthat:
//(1)
varpath=NSBundle.mainBundle().pathForResource("Spark",ofType:"sks")
//(2)
varsparkParticle=NSKeyedUnarchiver.unarchiveObjectWithFile(path!)
as!SKEmitterNode
//(3)
sparkParticle.position=CGPointMake(self.size.width/2,
self.size.height)
sparkParticle.name="sparkParticle"
sparkParticle.targetNode=self.scene
self.addChild(sparkParticle)
1. Wecreateapathtoourapp’sbundlewiththeNSBundle.mainBundle().pathForResource()functioncall,andwepassthestringoftheparticlefile’sname,inthiscase,Spark,withthefiletype,SKS.
2. Next,wecreatethesparkParticleobjectusingtheNSKeyedUnarchiver.unarchiveObjectWithFile(path!)callthat,aswesee,takesthepathwecreatedinpart(1).It’scastedastheparticleobjectforSpriteKit,SKEmitterNode.NSKeyedUnarchiverisaclassusedtodecodenamedobjectsfromkeyedarchives,anencodedhierarchyofarchives.Thisclasshassomesupportofwhatisknowntypecoercion.Inshort,itcandecodeobjectsinfiles,beitwhetherina32-bitor64-bitarchitecture.Moreonthisspecialfiledecodingclasshere:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSKeyedUnarchiver_Class/
3. Wethensetapositionandnameforthisparticleeffectandtargetittothescenewhilealsoaddingittothescenenode.
Thoughthisexampleisn’tgivenintheFoxdemo,thisisagreatexampleofhowwecantargetspecificfilesinourproject’snavigationhierarchy.
SceneKitSceneKitparticlesaremembersoftheSCNParticleSystemclass.WeaddtheseparticlestoourscenewiththeaddParticleSystemfunctionoftheSCNNodeclass.TheFoxdemodoesthisinthecollectFlower()functionwiththefollowingObjective-Cline:
[self.gameView.sceneaddParticleSystem:_collectParticles
withTransform:particlePosition];
Whatthiscodeisdoingiscallingthesceneinthedesignatedviewandaddingthe
particles,whicharedeclaredearlierintheclassas_collectParticlestoourscene.Itthentellsthesceneatwhichpointinspacethiseffectwillappear.Inthiscase,it’stheparticalPositionvariablethatwhentracedbackistakenfromtheSCNNodeparameterpassedintothecollectFlower()function.
NoteHere’showthiswouldbewritteninSwift:
scene.particleEmitNode.addParticleSystem(_collectable!)
Swift’saddParticleSystemAPIunfortunatelydoesn’thavethewithTransformparameterasinObjective-C,sowe’dhavetoaddtheparticlesystemtothenodeitwillbeemittingfrom,whichisdenotedbytheparticleEmitNodevariable.ThismostlikelywillchangeinfutureAPIchangesofSwift2.xandlater.
IntroducingSceneKitandSpriteKitphysicsWhenwelookatthecollectFlower()functionfromourparticleexample,weseethatthere’sanSCNNodeparameterpassed.ThisnodecomesfromthefunctionphysicsWorld.InbothSpriteKitandSceneKit,wecancreateanoverallsetofphysicsrulesandhandlevariousphysics-relatedinteractions,mostnotably,contactsbetweentwoormorenodes.Oneofthemostbasicaspectsofanygameistodosomethingwhengameobjectshiteachother.Thiscouldbewhentheplayertouchesacollectable,whenenemiescontacttheplayerortheplayerhitstheenemywithanattack.IniOSdevelopmentandingameengines,wecalltheseboundariesbetween2Dspritesor3Dobjectsasboundingboxes.WementionedthesephysicsobjectsbrieflyinourtalkofiOS9andlaterdebugOptionsproperty.BoundingboxesforSceneKitobjectsarecreatedautomaticallybasedonthesimplifiedversionofanobject’sgeometry,butwecanedittheseshapeswiththeSCNBoundingVolumeclass.
Moredocumentationofthisclasscanbefoundathttps://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNBoundingVolume_Protocol/index.html
GamephysicsiniOSandgamedevelopmentingeneralaremuchlargertopicsthanwecandiscussinthischapter.Sofornow,let’sjustseehowtheFoxdemoandiOSgamesingeneralhandlethesimpleconceptoftwonodescontactingtheirboundingboxes.
funcphysicsWorld(world:SCNPhysicsWorld,didUpdateContactcontact:
SCNPhysicsContact){
if(contact.nodeA.physicsBody.categoryBitMask==
AAPLBitmaskSuperCollectable){
self.collectFlower(contact.nodeA)
}
if(contact.nodeB.physicsBody.categoryBitMask==
AAPLBitmaskSuperCollectable){
self.collectFlower(contact.nodeB)
}
PrecedingisaSwiftpseudocodeexampleoftheFoxdemo’sphysicsWorldfunction.Thefunctiontakesintwoparameters,worldofthetypeSCNPhysicsWorldthatrepresentstheentirephysicsenvironmentofasceneandtheobjectrepresentingthephysicscontactofthetypeSCNPhysicsContact.Thefunctionherechecksthebitmaskofthenodesinthecontact.Ifthefirstorsecondnodeofthecontact(nodeAornodeB)areintheflower’sspecificcategory,thenthecollectFlower()functioniscalledandthatcollectable’snodeispassedasaparameter.
Bitmaskingiswhenwedesignateasetofbitsforanothersetofbitsthatcanbecombinedtogetherusingbitwisemath.Thinkofitasusing1sand0stonotonlycategorizearangeofonesandzerosbutalsoallowustohandlesituationswheremanycategorieshappeninthesamecontext.
Forexample,wehavedifferentcategoriesofobjects/eventsinourgameandwefitthemin
theirownslotsinabyte(8bits).IntheFoxdemo,thegamecollisionsareabitshiftvalueof2,thustheyrepresent00000100inbinary.ThecategorydesignationofcollectablesintheFoxdemoabitshiftof3or00001000,theenemiesare4,00010000.
Inthedemo,weseethefollowingcodeforAAPLBitmaskSuperCollectable:
//Collisionbitmasks
typedefNS_OPTIONS(NSUInteger,AAPLBitmask){
AAPLBitmaskCollision=1UL<<2,
AAPLBitmaskCollectable=1UL<<3,
AAPLBitmaskEnemy=1UL<<4,
AAPLBitmaskSuperCollectable=1UL<<5,
AAPLBitmaskWater=1UL<<6
};
WhenthecategorybitmaskineithernodeAornodeBofthecollisionmatchtheflowercollectable(iftheslotisonpersay,orequalto1,thenweknowthecollectablewasinvolvedinthecollision).
Swiftversion1didn’treallyhaveasimilarwaytomimicbitmaskingasdoneinObjective-CwithNSOptions,butasofSwift2.0,wecanperformbitmaskinglikethedemointhefollowingway:
structAAPLBitmask:AAPLBitmaskType{
letrawValue:Int
init(rawValue:Int){self.rawValue=rawValue}
staticvarNone:AAPLBitmaskType{returnAAPLBitmask(rawValue:0)}
staticvarAAPLBitmaskCollision:AAPLBitmask{return
AAPLBitmask(rawValue:1<<2)}
staticvarAAPLBitmaskCollectable:AAPLBitmask{return
AAPLBitmask(rawValue:1<<3)}
staticvarAAPLBitmaskEnemy:AAPLBitmask{returnAAPLBitmask(rawValue:
1<<4)}
staticvarAAPLBitmaskSuperCollectable:AAPLBitmask{return
AAPLBitmask(rawValue:1<<5)}
staticvarAAPLBitmaskWater:AAPLBitmask{returnAAPLBitmask(rawValue:
1<<6)}
}
Essentially,it’sastructthatreturnsbitshiftedstaticvariablesofitself.It’snotaselegantasseeninObjective-CandinpastCimplementation,butifwewishtousebitmaskinginboilerplatecodeinSwift,thisshouldallowyoutodoso.
OnelastnoteaboutthephysicsWorld()function,inorderforthefunctiontobecalledduringthecollisionoftwophysicsbodies,weneedtosetthephysicsworlddelegate.Inmostcases,thatdelegatewouldbethecurrentgamescene.
scene.physicsWorld.contactDelegate=self
Xcodewillmostlikelytellyouthataphysicsworlddelegatewasn’tsetandifyouhaven’t,thisisthecodethatisusuallyplacedintheviewDidLoad()functionofViewController.
VisuallycomposedgamescenescgsGettingbacktothevisualaspectsoftheFoxdemo,let’slookatthegamesceneobjectscreatedintheprojectandhowwecanviewthenodesinwhat’sknownasthescenegraph.
WeseethatgameobjectsandparticleeffectsintheFoxdemocanbevisuallymanipulatedinXcodeandtogetherinoneview.Theprecedingimageshowstheflowercollectableanditscomponentsthatconsistofthe3Dmesh,lighting,boundingboxes,andtheparticleeffects.InSceneKit,wedothiswithaSceneKitscenefile(SCN).
Toviewthescene’sscenegraph,clickonthesidewindowiconfoundtowardthebottom-leftoftheXcodewindowunderthevisualeditorwindowofthescene,asseeninthefollowingscreenshot:
Thisisascreenshotofthescenegraph.Thosefamiliarwiththegameengines,suchasUnityandUnrealEngine,willbequitefamiliarwiththistypeofcomponent/gamesceneview.Thescenegraphshowsthedropdownhierarchyofalltheobjectsinthesceneincludingtheirowninternalcomponents.Theflowerpower-upconsistsofa3Dmeshmodelnamedflowerthathastwochildparticleeffectsaswellasaphysicsbody.Allthreearedenotedbythethreesymbolsseenontheright-handsideofthegraph’sobjects.
Wecanmovethemodelaroundinthescene,usingX,Y,andZmarkersseenintheprecedingimage.Wecanalsozoomin,zoomout,rotatethescene,aswellasaddmoreobjectstothescene.
Toaddmoreobjectstothescene,followthese:
1. Gotothemedialibraryfoundinthelibrarywindowsatthebottom-rightofthescreen(seeninthepriorimage).
2. Nowsearchforgrassandsimplydraganddropitintothescene.Nowthepremadegrassobjectisinthissceneasareference.
3. Thisisactuallyhowthelevel.scnfilewascomposed.4. There’salsotheoptiontoaddprimitiveobjectstothesceneandbuildthemupfrom
there,whichisagainverysimilartothedesign-centricgameengines.Simplyselectfromtheobjectlibrarytabrightnexttothemedialibraryiconandsearchgeometry.Thereareprimitiveobjects,suchasspheres,planes,andboxes.
5. Theprimitiveobjectslackthelighting,materialsandotherdetailsthatwecanseeinthegrassandotherpremadeobjectsintheprojects.Usetheevenmoredetailedinspectorwindowsfortheseobjectstoseeandeditvariousdetails,suchasthephysicsbodies,materials,bakingthelighting,andobjectnameidentificationforanyscripting/coding.
6. There’salsoactionsthatcanbeaddedtotheseobjects.Clickonthesecondaryeditoricon(theupsidedowntriangleinasquareatthebuttonrightoftheflowscene’sview).ThiswillopenthesecondaryeditorthatshowsaRotateByEuleractioniftheflowerassetisselectedinthescenegraph.
7. Whatthisactiondoesisrotatethefloweronceeverysecond.Toseethisinaction,clickonthePlaybuttonseenjustabovethesecondaryeditorwindowtimeline.Wecanseehowthisobjectwillrotatefromthisaction.
8. Theactioncanbeshortenedorextendedbyexpandingorcondensingitinthesecondaryeditortimeline.MoredetailsabouttheactioncanbeeditedintheInspectorwindow,andifwe’dlike,wecanusethelibrarytoaddmoreactionstothisobjectorremovetheoneprovidedtohaveitactinadifferentway.
Testoutafewactions,times,andpropertiesyourself.Wecanseehow,withoutanycode,wecansetupascenevisuallyanddynamicallycontrolactionsofeachobjectinthatscene.ManyofthesevisualfeaturesandactionsworkforbothSceneKitandSpriteKitscenes.
Lookatthelevel.scnfiletoseeascenewithafullycomposedlevelcameraobject(asseeninthepreviousscreenshot).Doyouwanttomakeasimilarlevelwithmaybemoreobstaclesandadifferentskybox?Copythelevelandchangethoseassetsandnameitlevel2.Thiscansaveamonumentalamountoftimeinthedesignofgamesandlevels.FromXcode7onwards,wehavetoolsdirectlyintheIDEthatoriginallywereonlyforthemultiplatformgameengines.Itreallyputsthedesignbackintogamedesign.
Muchofthemanualcodewe’vegoneovercouldgetdaunting,especiallyforthoseofuswhomaywanttogetintogamedesignbutarestillrelativelynewtocoding.
//Objective-C
SCNScene*scene=[SCNScenesceneNamed:@"game.scnassets/level.scn"];
//Swift
letscene=SCNScene(named:"game.scnassets/level.scn")!
Thisisallweneedtoreferenceascenefromthevisuallydesignedtools.Addittotheview’srootnodelikewespokeaboutintheSceneKitbasictemplateandit’sreadytogo.Usecodetoaddspawningenemies,theplayer,andthe2DSpriteKitoverlay(whichcanitselfhaveactionsandvisualdesignsinitsSKSfile),andit’safull-fledgedgame.
FormoreinformationontheSceneKitframeworkaswellasthelatestupdatesandadditionstoitslibrary,seethefulldocumentationlinkasfollows:
https://developer.apple.com/library/ios/documentation/SceneKit/Reference/SceneKit_Framework/
SummaryAtthestartofthischapter,wefirstspokebrieflyaboutthehistoryof3DgamedesigniniOSandhowSceneKitcametobefromthenecessitytohaveafirst-party,dynamicallyrobustframeworkaimedatthecomplexitiesof3Dgamedevelopment.WethenwentoverthebasicstructureofSceneKitandhowitandSpriteKitfromthepreviouschapterworkofftheconceptofnodes,startingfromtheviewandmovingontothescenenodeandchildnodesinthatscene.Next,wewentoverhowSpriteKitandSceneKitcanbeusedtogetherinthesamesceneaswethenmovedontodissectingthedefaultSceneKittemplategiventousinXcode,anditsvariousassets.Inadditiontoareviewoftemplateproject’scode,wealsoreviewedsomeofthefeatures,code,andassets,suchastheaudionodes,lendmodes,anddebugoptionsthatbecameavailableasofiOS9/Xcode7.Finally,fortheremainderofthechapter,wespokemuchabouttheFoxdemoshownduringtheWWDC15conventionandthevariousvisualgamedesignfeaturesthatbecameavailablesincetheannouncementofXcode7.
Forournextchapter,wewillgointothefeaturesoftheGameplayKitframework,whichweintroducedbrieflywhenwewentoverthebenefitsofusingcomposite-basedstructuringwhenbuildingourgames.WithGameplayKit,wecanduplicateandreusepremadegameactionsandrulesaswedidhereinthischapterwiththevisualcomponentsofourgames.
Chapter5.GameplayKitFormanyyears,videogamedevelopmenthasreliedonthetenetsofobject-orienteddesign(OOD).OfthecorefeaturesinOOD,theconceptsofinheritanceandpolymorphismhavebeenthemostusefulinthisbranchofsoftwareengineering.Itmakessensetothinkofentitiesinourgamesashomogenousgroupsofobjects;objectsthatwethenwriterulesforinhowtheyinteractwitheachother.Forexample,thankstoinheritance,allobjectsinourgamecanbegiventheclassnameofGameObject;theyhavefunctionswe’llusethroughoutthegameandthenwecanbranchthemoffintochildclasses,suchasPlayerorEnemy.Wecanthencontinuethatthoughtprocessaswecomeupwithmorespecifictypesofentities,betheyobjectssuchasPlayer,differentenemies,non-playercharacters(NPCs),orwhatevermakessenseforthegamewearemaking.Callingafunctiononthoseobjects,suchasShoot()orHealth(),couldbeuniqueforeachchildoftheparentclassandthuswemakeuseofpolymorphisminOOD.
However,asmentionedinthepreviouschapter,althoughinheritance-basedstructuringisgreatformostsoftwareapplications(includingsimplegames),theuniqueneedsandpairingsofvideogamerulesandentitiescauseinheritance-basedstructuringtobreakoneoftherulesofOOP.Thatruleisthereusabilityofourcode.Thesolutiontothatproblemistoseparatethegameobjectsandthegame’srulesintowhat’sknownascomponent-basedstructuring.Buildinggameswiththismentalitycanallowustobuilduniqueobjects,actions,andruleswiththeabilitytonotonlyshiftthemaroundthroughoutoursinglegameproject,butalsotousetheminotherprojects,cuttingtheoverlycustomizedstructuringinwhichbuildingagameviainheritance-basedstructuringcancause.
Apple’ssolutiontothisissueistheGameplayKitframework.GameplayKitisacompletelyindependentframeworkthatcanbeusedwithbothSpriteKitorSceneKitgames,aswellasgameswritteninlow-levelAPIs,suchasOpenGLandMetal.FirstannouncedforiOS9andXcode7atWWDC15,GameplayKittakesthecommonmethodologiesandconceptsusedingamedevelopmentforyearsandallowsustoworkonthoseaspectsindependentlyofwhatisbeingdrawnonthescreen.Thisframeworkdoesn’thandlewhat’sdrawnonthescreenandis,thus,madespecificallyfortheModelportionofMVC.
ThereareseveralgamedevelopmentconceptshandledbyGameplayKit,whichweshallreviewinthischapter.Theseconceptsareentitiesandcomponents,statemachines,agents,goals,behaviors,pathfinding,MinMaxAI,randomsources,andrulesystems.
EntitiesandcomponentsWecanthinkofentitiesastheobjectsinourgame.Theycanbetheplayer,anenemycharacter,anNPC,leveldecorationsandbackgrounds,oreventheUIusedtoinformtheplayeroftheirlives,power,andotherstats.Theentityisthoughtofasacontainerofcomponents.Componentsarebehaviorsthatdictatetheappearanceandactionsofanentity.Onemightask,“howisthisanydifferentfromobjectsandfunctions?”Theshortansweristhatobjectsandfunctionsininheritance-baseddesigndescribemoreofwhatourgameobjectsare,whileworkingwithcomponent-basedstructuringfocusesmoreonwhattheydo.AswedealwiththeclassesandfunctionalityoftheGameplayKitframework,wewillbeabletogetabetterhandleonthis.Inthisframework,we’llseethatentitiesandcomponentsarehandledwiththeGKEntityandGKComponentclasses,respectively.
NoteIfyouarestillabitconfusedaboutcomponent-basedstructuring,checkbackinourpreviouschapterwherewewentintothisinabitmoredetail.Youcanalsovisitthedeveloperpageaboutthisdesignmethodologyhere:https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/EntityComponent.html
UsingGKEntityandGKComponentobjectsinourgamesAnyonefamiliarwithJavaorC#willunderstandtheconceptofanabstractclass.TheGKComponentclassisessentiallyanabstractclass.QuotingfromthespeakersatWWDC:thinkofcomponentsas“littleblackboxesoffunctionality.”ObjectsoftheclassGKEntityarelikeourgenericGameObjectclassmentionedbefore.However,unliketheobjectswe’vedealtwithbefore,wetypicallydon’taddtoomuchinthewayofcustomfunctionalitytothem(otherwise,we’dbeleaningtowardsinheritance-basedstructuring).
WefirstcreateagameobjectandsubclassitasamemberoftheGKEntitytype.Forthisexample,let’sjustcallourobjectclassGameEntity.Also,don’tforgettoimporttheGameplayKitAPI:
importGameplayKit
classGameEntity:GKEntity
Again,ourgoalwithanobjectofthetypeGKEntityistouseitasacontainerforGKComponentobjects.ObjectsoftheGKEntityclassinheritthefollowingfunctions:
components:ThisisapropertythatreturnsanarrayoftheGKComponentsinourGKEntityobjectaddComponent(_:):ThisisafunctionthatweusetoaddGKComponentstoourGKEntity
removeComponentForClass(_:):ThisremovesacomponentforaclassofGKEntityobjectsupdateWithDeltaTime(_:):Thisupdateswiththerender/gameloopforeachcomponentintheentity;thisiswhat’sknownasper-entityupdating
ThelastfunctionintheGKEntityclass,updateWithDeltaTime(_:),isoneofthetwomechanismsusedbyGameplayKitwhendispatchingupdatestoourgameobject’scomponents.Theupdatemechanismsareasfollows:
Per-entity:Thisisgoodforupdatingsmallergamestructureswithoutcausinglag.TheupdateWithDeltaTime(_:)functionofGKEntitycallsalloftheentityobjects’updateWithDeltaTime(_:)functionsandthendispatchesthattoallofthecomponent’supdateWithDeltaTime(_:)functions.Per-component:Thisismadeforlargerprojectsandmorecomplexgamelogic.Whenupdatingviatheper-componentmechanism,weusetheclasstypeGKComponentSystemtocategorizegroupsofcomponentsandthencallitsupdateWithDeltaTime(_:)functiontofireoffupdatesspecifictotheclassofcomponentinstancesitmanages.GKComponentSystemdoesn’tmakenoteofyourgame’sentity/componenthierarchy,sotheycanbeusedtoefficientlyupdatemorecomplexgames.
Forexample,ifwehaveaspecificwaywewanttoupdateplayerandenemyattacks,thenwecreateanobjectoftheGKComponentSystemclass,initiateitwiththeinitWithComponentClass(_:)function,andpassthatclassofcomponents(inthiscase,
Attackcomponents)tothatsystem.It’sinthatGKComponentSystemobject’supdateWithDeltaTime(_:)functionthatwespecifytheuniqueupdatelogicforthatgroupofcomponents,independentlyoftheentityupdates.Thiscancomeinhandywhendealingwithenemy/NPCAIinmorecomplexgames.
Here’savisuallookattheseclasses:
Don’tworryifthisisstillsomewhatconfusing.ThoseofyouwhomaybemorefamiliarwiththegameengineUnitywillhaveseenhowcomponents,suchasrigidbodies,materials,andscripts,canbeaddedtoobjectsplacedinagamescene.TheconceptherewithiOScomponentsandentitiesisnotthatmuchdifferent.EachcomponentinUnityandotherenginescanbeitsownindependentlyworkingfunctionality.
Let’stakealookatthecodesnippetprovidedduringtheWWDC15conference,inbothObjective-CandSwift,showinghowwe’dcreateentities,components,andcomponentsystemsinourprojects:
//Objective-C
/*Makeourarcher*/
GKEntity*archer=[GKEntityentity];
/*Archerscanmove,shoot,betargeted*/
[archeraddComponent:[MoveComponentcomponent]];
[archeraddComponent:[ShootComponentcomponent]];
[archeraddComponent:[TargetComponentcomponent]];
/*CreateMoveComponentSystem*/
GKComponentSystem*moveSystem=
[GKComponentSystemsystemWithComponentClass:MoveComponent.class];
/*Addarcher'sMoveComponenttothesystem*/
[moveSystemaddComponent:[archercomponentForClass:MoveComponent.class]];
//Swift
/*Makeourarcher*/
letarcher=GKEntity()
/*Archerscanmove,shoot,betargeted*/
letmoveComponent=MoveComponent()
letshootComponent=ShootComponent()
lettargetComponent=TargetComponent()
archer.addComponent(moveComponent)
archer.addComponent(shootComponent)
archer.addComponent(targetComponent)
/*CreateMoveComponentSystem*/
letmoveComponentSystem=GKComponentSystem(componentClass:
MoveComponent.self)
/*Addarcher'sMoveComponenttothesystem*/
moveComponentSystem.addComponent(archer.componentForClass(MoveComponent.sel
f)!)
WhatthiscodedoesiscreateourGKEntityobjects;inthiscase,thetowergameexample’sarchercharacter.NextitaddspredefinedGKComponentobjectsviatheaddComponent(_:)function.WealsocreateaGKComponentSystemobjectnamedmoveComponentSystemthatwillbeusedtoupdateonlymovementtypecomponents.Thearcher’sownmoveComponentclassisaddedtothissystemwithmoveComponentSystem.addComponent(_:).Makeanoteofhowtheparameterspassedthroughthisobjectinadditiontoitsinitializationareclasstypesofthecomponenttypesdenotedbythe.classor.selfproperties,dependingonwhichlanguagewearewritingourcodein.
NoteAsofthispublication,thecomponentForClass()functionmightnotbefullyfunctionalfortheSwiftprogramminglanguage.SoiftheSwiftimplementationisn’tworkingasexpectedforthisandotherGameplayKitobjectinitializations,theObjective-CversionofthiscodewillneedtobeusedandlinkedtoyourprojectviaanObjective-C–Swiftbridgingfile.ThiswillmorethanlikelybeironedoutinfutureupdatestoSwiftasApplecontinuestomoveawayfromObjective-Casthemainlanguageoftheplatform.Formoreinformationonhowtomakethisbridgingfile,checkoutthislink:https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
AppleprovidesuswithaprojectnamedMazethatusestheseclassesaswellassomeotherconceptswe’llbegoingovershortly.
Here’salinktotheprojectthatcouldhelptogiveyouanevenbetterideaofentitiesandcomponents:
https://developer.apple.com/sample-code/wwdc/2015/downloads/Maze.zip
BeforewegoovermorespecificcodeuserelatedtoGKEntityandGKComponentobjects,we’lllookintoagamedevelopmentconceptthatisbestcoupledwiththeseobjects,whichistheconceptofstatemachines.
StatemachinesAvideogame,morethananyothertypeofapplication,basesmuchofitslogiconwhetherthegameorentitiesinthatgamearecurrentlyinoneofanumberofdifferentstates.
Thiscouldbecheckingwhetherthegameisintheintroscene,runninginthemaingameplaymode,theplayerhasdied,theplayerisidle,abossenemyhasappeared,thegameisover,thestageisover,thebossislowonhealth,andmuchmore.
AnexampleofstatemachinesforeitherAIorcharacteranimations
Inthepast,ithasalwaysbeencommonpracticeforgamedeveloperstowritetheirowncustomstatemachinelogicfromscratchandthenusetheupdate/rendercycletocheckonthesevariousstates.Typicallythiswouldbedoneinacustomclassorsimplyinacustom-madeenumobjectthatwillshiftthroughvariousstates,suchas.GameOver,.MainGame,.LowHealth,andsoon.Thesestatescouldalsodescribethestatusofanindividualentityinourgameanddictatewhichanimationcycletorun.Forexample,theplayercouldbechargingtheirattackandwe’dwanttousethatstateoftheplayertoanimatethecharginganimation.Objectsinthegamescenemightcheckbackonsuchstatesviaswitchstatementstomakesurethattheyarenotdoinganyactionthatwouldn’tmakesensebasedonthecontextofthestate.Itwasn’ttoolongbeforemultiplatformgameenginesmadethisapartoftheworkflow,particularlyintheanimationhandlers.Theseobjectsthatletusinformthegameandentitiesinthegameofthevariousstatesareknownasstatemachines.GameplayKitallowsustoworkwiththisconceptinconjunctionwithitscomponent/entityfunctionality.TheframeworkprovidestheabstractclassGKStateforustosubclassfromforourgame’sstates,andtheclassGKStateMachinetoutilizeforplacingthesestateobjectsintoadesignatedstatemachine.AnobjectofthetypeGKStateMachinecanonlybeinonestateatatime,soitgivesusabetterwaytouseandreusethesestates,asopposedtotheoldboilerplate/switchstatementmethodology.
ThepreviousdiagramisfromWWDC15andusesanexampleofwhataPacMan-likeghostcharacter’s,oranyothergamecharacter’s,animationandAIstatemachineswouldlooklike.Alsonotethatnotallpathscouldleadtoeachother.Forexample,theghostcan
switchbackandforthbetweenchasingandfleeing,butcanneitherbedefeatedwhilechasingnorcoulditrespawnunlessitwaspreviouslyinthedefeatedstate.Theseareknownasstatetransitionsoredgesinastatemachine.
Bydefault,alledgesarevalidandweoverridetheisValidNextState(_:)functioninourGKStateobjects/componentstotellthestatemachineifweareallowedtomovebetweencertainstates.
Here’showthisisdoneintheDemoBotssampleprogram’sTaskBotAgentControlledStateclass.DemoBotsistheiOS9SpriteKitdemomentionedinChapter3,SpriteKitand2DGameDesign:
overridefuncisValidNextState(stateClass:AnyClass)->Bool{
switchstateClass{
caseisFlyingBotPreAttackState.Type,is
GroundBotRotateToAttackState.Type,isTaskBotZappedState.Type:
returntrue
default:
returnfalse
}
}
ThistellsthestatemachinethattheTaskBotAgentControlledStatestatecantransitiontoFlyingBotPreAttackState,GroundBotRotateToAttackState,orTaskBotZappedState.ThisobjectisanotheroftheGameplayKitcomponenttypeknownasanAgent(whichwewillgoovernext),butfornownotehowwevalidatewhichtransitionscanhappenintheisValidNextState()function.
HereweseeavisualrepresentationoftheGKStateandGKStateMachineclasses.Asstatedbefore,isValidNextState()tellsuswhichstatetransitionsarevalid.ThedidEnterWithPreviousState()functioniswherewetellourcomponentswhattodowhenthestateisentered,andthewillExitWithNextState()functioniswherewetellthecomponent(s)whattodowhenthestateisexitingtothenextstate.The
updateWithDeltaTime()function,aswithpreviouslymentionedGameplayKitobjects,iswhereweputourrender/gameloopcycleupdates.Optionally,wecanalsoaddmorefunctionalitytotheseclassesthroughinheritance.Forexample,wecancreateapreviousStatepropertytocollectmoreinformationaboutthepriorstate.Wecanthenpotentiallyusethatinformationforourownhelperfunctions,suchasexitState()orexecute().
Here’sacodesnippetshowinghowtocreatestatemachinesandhowtoaddGKStateobjectstothem:
/*Makesomestates-Chase,Flee,Defeated,Respawn*/
letchase=ChaseState()
letflee=FleeState()
letdefeated=DefeatedState()
letrespawn=RespawnState()
/*Createastatemachine*/
letstateMachine=GKStateMachine(states:[chase,flee,defeated,respawn])
/*Enterourinitialstate-Chase*/
stateMachine.enterState(chase.classForCoder)
Intheprecedingcode,weseethestatescreatedfromthepremadeclassesofGKState(chase,flee,defeated,andrespawn).ThestateMachineobject,atinitialization,receivesaparameterofanarrayofGKStateobjects,asshownin:letstateMachine=GKStateMachine(states:[chase,flee,defeated,respawn]).Then,inthisexample,westartthatstatemachineatthestatechase.This,ofcourse,willbedifferentbasedonthelogicofyourowngame’scomponents.GKStateMachineobjectscanalsoreturnthecurrentState()function;thus,wecanguidevariousentitiesandcomponentsinourgamebasedonthecurrentpulseofthegame’sobjects.
FindoutmoreonGKStateandGKStateMachineinthefollowingfulldocumentation:
https://developer.apple.com/library/prerelease/ios/documentation/GameplayKit/Reference/GKState_Class/https://developer.apple.com/library/prerelease/ios/documentation/GameplayKit/Reference/GKStateMachine_Class/index.html#//apple_ref/occ/instm/GKStateMachine/canEnterState
Nextwegooveragents,goals,andbehaviors.
Agents,goals,andbehaviorsWhenwemakeentitiesinourgames,particularlythosethatarenottheplayer,wewantthemtoperformvariousactions.Theseactionsaredictatedbyartificialintelligence(AI)thatwegivethem,andarebasedonvariousstatesofthegame,theplayer,theenvironment,ortheplayerthemselves.Wecanhaveagroupofenemiesfollowacertainpath,tracktheplayer,orautomaticallymovesmoothlyaroundobstaclesusingourgame’sphysicalworld.Theframeworkallowsustomakeourgameentitiesbewhat’sknownasagents.Agentsareentitiesthatcanhavegoalsandbehaviorsattachedtothem.
AgentsinGameplayKit,whichutilizetheGKAgentclass,canhaveGKComponentobjectsthatautomaticallysetvariousbehaviorsandarebasedontheweightoftheirgoals.Theweightofagoalisusuallyafloatfrom0to1.Thehigherthegoal’sweightvalueiscomparedwithothergoals,thegreaterthechancethattheagentwillperformthosebehaviors.Forexample,ifanenemycharacterislowonhealth,we’dprobablywanttheirHealgoaltohaveahighergoalweight.Theenemywillbehaveinafashionthatshowstheurgencyofthatcurrentlowhealthsituationbyhealingmoreoftenandthusgivingtheplayeramorechallengingandintelligentopponent.Inotherwords,agents,goals,andbehaviorsareastackableandmalleableAIsystem.
Here’sanoverviewofthisfunctionalityinGameplayKit:
Abehavior,viatheGKBehaviorclass,ismadeofanarrayofGKGoalobjectsthatareeachgivenacertainweight.Forexample,wecouldhaveaGKBehaviorclassforanNPCinaracinggamenamedRacingBehavior.Thatbehaviorwouldbeacombinationoftwogoals,suchasFollowPathandAvoidAgents.TogetherthosegoalswouldmakeacharacterinourgamethatwillautomaticallymoveawayfromotherNPCswhilestayingonthecurrenttrackforthestagewearein.
Here’savisualrepresentationoftheseclasses:
AGKAgentobject,aswecanseeintheprecedingimage,hasanumberofphysics-basedproperties,suchasmass,radius,maxSpeed,andmore.LikeotherGameplayKitobjects,itutilizestheupdateWithDeltaTime()functiontosyncwiththerender/gameloopupdatesofeitherGKComponentSystemorGKEntity.Areyoustartingtoseeapatternherewiththeseobjects?Inaway,wecanalsothinkofaGKAgentobjectbeingsimilartoaSpriteKitoraSceneKitnodesincetheyworkonourgame’sphysics.However,whetherwemadeourgamewithSpriteKit,SceneKit,orourowncustomrendercomponents,suchasinOpenGLorMetal,weneedtolinkuptheseclassestowhat’sdisplayedonthescreenwiththespecialGKAgentDelegateclass.Here’sadiagramofthatclassanditsfunctions:
TheagentWillUpdate()functioniswhatweusetotelltheagentwhattodojustbeforethegame’supdate()function,andtheagentDidUpdate()functioniswhatweusetotelltheagentwhattodoonscreenaftertheupdate()function.Thiscanbe,inthecaseofaFollowGKGoalobject,havingareferencetotheplayer’spositiononthescreenbeforetheupdatetakesplace.Here’stheexampleofthisfromWWDC15,butwritteninSwiftasopposedtotheObjective-Cexamplethatwasgiven:
funcagentWillUpdate(agent:GKAgent)
{
/*Positiontheagenttomatchoursprite*/
agent.position=self.position
agent.rotation=self.zRotation
}
funcagentDidUpdate(agent:GKAgent)
{
/*Positionthespritetomatchouragent*/
self.position=agent.position
self.zRotation=agent.zRotation
}
Let’slookatanexampletoseewhataGKGoal/GKAgentinteractionlookslike.Here’sacodesnippetfoundintheDemoBotproject’sTaskBotBehavior.swiftclass,whichisachildofGKBehavior:
//(1)
letseparationGoal=GKGoal(toSeparateFromAgents:agentsToFlockWith,
maxDistance:
GameplayConfiguration.Flocking.separationRadius,maxAngle:
GameplayConfiguration.Flocking.separationAngle)
//(2)
behavior.setWeight(GameplayConfiguration.Flocking.separationWeight,
forGoal:separationGoal)
Inline(1),thetoSeparateFromAgentsparameterofGKGoalletsuspassareferencefortheGKAgentobjectswewishtokeepacertaindistancefrom.
Inline(2),thebehavior.setWeight()functionpassesthepredeterminedfloatGameplayConfiguration.Flocking.separationWeightastheweightforthisverygoal.Thehighertheweight,themorepriorityisputonthatgoal.
You’llnoticefromthefulldocumentationofGKGoallinkedtolaterthatmuchoftheGKGoalclassdealswiththeattractionorrepulsionagentshavetoeachother.CombiningdifferentcharacteristicsofthisbasicfunctionalityletsuscreateuniquegoalsthatGKAgentparametersget,asshownhere:https://developer.apple.com/library/prerelease/ios/documentation/GameplayKit/Reference/GKGoal_Class/index.html
Tobacktrackabit,hereisamorebasicwaywecancreatetheseobjects,asshownattheconferencebothinObjective-CandSwift.
//Objective-C
/*Makesomegoals,wewanttoseektheenemy,avoidobstacles,target
speed*/
GKGoal*seek=[GKGoalgoalToSeekAgent:enemyAgent];
GKGoal*avoid=[GKGoalgoalToAvoidObstacles:obstacles];
GKGoal*targetSpeed=[GKGoalgoalToReachTargetSpeed:50.0f];
/*Combinegoalsintobehavior*/
GKBehavior*behavior=[GKBehavior
behaviorWithGoals:@[seek,avoid,targetSpeed]
andWeights:@[@1.0,@5.0,@0.5]];
/*Makeanagent-addthebehaviortoit*/
GKAgent2D*agent=[[GKAgent2D*alloc]init];
agent.behavior=behavior;
//Swift
/*Makesomegoals,wewanttoseektheenemy,avoidobstacles,target
speed*/
letseek=GKGoal(toSeekAgent:enemyAgent)
letavoid=GKGoal(toAvoidObstacles:obstacles,maxPredictionTime:0.5)
lettargetSpeed=GKGoal(toReachTargetSpeed:50.0)
/*Combinegoalsintobehavior*/
letbehavior=GKBehavior(goals:[seek,avoid,targetSpeed],andWeights:
[1.0,5.0,0.5])
/*Makeanagent-addthebehaviortoit*/
letagent=GKAgent2D()
agent.behavior=behavior
Weseeintheprecedingcodethatwhenwecreategoalsweassignagentstothemthatweareeitherseekingoravoiding.Goalsonagentscanhaveatargetspeed,asseenwiththetoReachTargetSpeed:parameter,andthesecanallbebundledupintothecurrentbehaviorwithsetweightsgiventothem.
Here’smoredocumentationonGKGoal,GKAgent,GKAgentDelegate,andGKBehavior:
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/Agent.html
OneotherthingtonoteisthattheobstaclesarrayreferencepassedhereispartoftheGKObstacleclass.Thisclassreferencesobjectsonthescenethatwetellagentstousuallyavoidwhenmovingacrossthescreen,andarepartofournexttopic,Pathfinding.
PathfindingNavigationisanintegralpartofmostgames.Wecouldhaveanoverworldsceneinourgameshowingthevariouslevelstraversedoryettobevisited,withbranchingpathwaystoeachpoint,orwecouldhavea3Dactionplatformerwithaspellthatpointsoutalogicalpathtoournextquestorbattlelocation.Wecanalsoseepathfindingintop-downisometricgames.Forinstance,theplayercouldbefightingoffahoardofenemiesalllockedontheplayer’slocationonthescreen.GoodpathfindingAIwouldnotonlytelltheenemiestomovetowardtheirgoal,buttodynamicallyavoidanyimpassableobjectsintheirwayanddetourtoabetterrouteautomatically.Inourtalkonagents,goals,andbehaviors,wesomewhatcoveredthat.Behaviors,whichGKAgentobjectsadhereto,syncwithvariousgamephysicsandthuscreatesmoothAImovementstochangewithotheragents/objectsinthescene.However,itwouldbegreattoalsobeabletoinformthesecomponentswheretheycanandcan’ttraverseinascene,andthat’swherepathfindingcomesin.
TheprecedingdiagramshowswhatPathfindingisandisanin-gamevisualgiventousbyAppleduringtheWWDC15conference.Itcanbebrokendownasfollows:
Pathfindinginvolvesnodeswithtransversalpathstoandfromthosenodesinwhat’sknownasanavigationgraph.Thesenodescanbesingledirectionalorbidirectionaland,mostimportantly,therecanbeapathcalculatedwiththisgraphthatrepresentsthebestpathaGKAgentcantake.ThesquaresshownintheearlierscenerepresentGKObstacleobjectsthatareplacedinthescene(beitbycodeorvisuallyintheXcodeeditor’stools).
Here’sthefulldocumentationfortheGKObstacleclass:
https://developer.apple.com/library/prerelease/ios/documentation/GameplayKit/Reference/GKObstacle_Class/index.html
LikeotherGameplayKitfeatures,weusevariousabstractclassestochildfromforsettingupthenavigationgraphandoverallPathfindingfunctionality;thoseclassesareGKGraph,GKGridGraph,GKGridGraphNode,andGKObstacleGraph.
It’snottooforebodingwhenweseetheprecedingdiagramofclassesandgothroughthemonebyone.Themain,andmostcommon,classusedidtheGKGraphclass.Thisiswherewecanattachtoitoneoftwodifferentgraphspecificationtypes:GKGridGraphorGKObstacleGraph.GKGraphletsusaddandremovenodes,connectthem,andfindtheoptimalpathbetweennodes.Ofthetwospecificationtypes,GKGridGraphhasasimplerfunctionalitythatismeantforeasy,2D-basednavigationgraphcreation,whereasGKObstacleGraphletsussetupanavigationgraphusingGKObstacleobjects.Nodesareautomaticallycreatedaroundthoseobstaclesbasedontheirshape,andtheseclassesdomuchofthefootworkneededtocalculatethepathsouragentsneedtotakefromthestarttothefinishoftheirsetpath(s).Ifwewanttoaddevenmorefunctionalitytoournodes,sayifwewantcustomizedmovementbasedonterraintypeinadditiontoshape,thenwecouldusethenodesofGridGraphNode.
ThecostToNode()function,forexample,canbeusedtoindicatethatthoughthispathwouldbetheoptimalpathonaflat,evenandsimilartypeplane,itwouldcostmoretotraverse.Forexample,ifthere’squicksandinourgame,theplayercouldtraverseit,soitwouldn’tmakesensetomakeanimpassableGKObstacleobjectoverthequicksand.Insteadwewouldsaythatthepathacrossthatterrainbetweenthetwonodescostsmore.Thiswillmakeourgame’snavigationsmarterandwillhandlesuchcustomparameters.
NoteThecostToNode()functionisactuallyanexampleofbestpractice.Wecanchoosetonotuseit,but,ifwearenotcareful,ourgame’spathfindingAIcouldendupratherunintuitive.Thiswouldnotonlymakeapoorexperiencefortheplayer,butendupaddingmoretimefromdebuggingfaultyAIactionslateron.
Let’slookatsomecodesamplestogetabetterunderstandingoftheseclassesandhowtoworkwiththem.DonotethatthecodeasofWWDC15isinObjective-C.
/*Makeanobstacle-asimplesquare*/
vector_float2points[]={{400,400},{500,400},{500,500},{400,500}};
GKPolygonObstacle*obstacle=[[GKPolygonObstaclealloc]
initWithPoints:pointscount:4];
/*Makeanobstaclegraph*/
GKObstacleGraph*graph=[GKObstacleGraphgraphWithObstacles:@[obstacle]
bufferRadius:10.0f];
/*Makenodesforheropositionanddestination*/
GKGraphNode2D*startNode=[GKGraphNode2DnodeWithPoint:hero.position];
GKGraphNode2D*endNode=[GKGraphNode2DnodeWithPoint:goalPosition];
/*Connectstartandendnodetograph*/
[graphconnectNodeUsingObstacles:startNode];
[graphconnectNodeUsingObstacles:endNode];
/*Findpathfromstarttoend*/
NSArray*path=[graphfindPathFromNode:startNodetoNode:endNode];
ThiscodesnippetusesthefunctionalityofGKObstacleGraphbyfirstmanuallycreating2DvectorpointsinthepointsarrayandinitializingtheGKObstacleGraphobjectandgraphwiththosepoints.Next,thetwoGKGraphNode2Dobjectsarecreatedtorepresentthestartandendnodesforaherocharacterinthegame.Then,finally,theoptimalpathforthatherocharacteriscreatedandstoredintothearrayautomatically;thatis,apathusingthegraph’sfindpathFromNode:andtoNode:parametersusingthestartNodeandendNodeobjects,respectively.Thispathobjectcanthenbeusedinourhero’smovementcomponentormaybeamapvisualcomponenttomovetoorindicatetotheplayerthecorrectpathneededtotraversethegamestage’sobstacles.
ThefollowingcodesampleishowtheDemoBotsprojectworkedwiththenavigationinSwift,usingwhat’sknownasalazystoredproperty.
MoreinformationontheSwiftkeyword,lazy,canbefoundhere:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
SwiftexamplefromDemoBots:
lazyvargraph:GKObstacleGraph=GKObstacleGraph(obstacles:
self.polygonObstacles,bufferRadius:
GameplayConfiguration.TaskBot.pathfindingGraphBufferRadius)
lazyvarobstacleSpriteNodes:[SKSpriteNode]=self["world/obstacles/*"]
as![SKSpriteNode]
/*theabovelinecaststheobstaclesinourproject's"world/obstacles/"
folderpathasanimplicitlyunwrappedarrayofSKSpriteNodes
*/
lazyvarpolygonObstacles:[GKPolygonObstacle]=
SKNode.obstaclesFromNodePhysicsBodies(self.obstacleSpriteNodes)
Inshort,lazyvariablesarequickarrayinitializationsinwhichtheirvaluesarenotknownatfirstandarecontrolledbyoutsidesources.InthecaseofDemoBots,theseareobstaclesthatarecreatedautomaticallyfromtheboundsofSpriteKitnodes,whichisdonebytheSpriteKitnodefunctionobstaclesFromNodePhysicsBodies().Thisexample,justshowshowmuchtimecanbesavedwhenusingtheprovidedframeworks.Inthefirstexampleandmoresoinpastgamedevelopment,muchofthislogicwouldhavetobemanuallydoneviaterriblycomplexboilerplatecodelogic.
FormoreinformationonPathfindingwithGameplayKit,checkouttheexamplesanddocumentationfoundhere:
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/Pathfinding.html
MinMaxAISofar,we’vecreatedAIthat’sgreatforthecomponentsandobjectsthatareactiveinascenewiththeirmovement,behaviors,andnavigation,butwhataboutAIthatcanunderstandthegame’srulesliketheplayer?Agoodexampleofthisisagameofchessorvariousotherboard/tile-likegames.It’dbegreattocontrolhowmuchthecomputercanmakeprogressinthegamewithvariouslevelsofdifficultyfortheplayer.Wecanalsowanttoletthegamedecideforuswhatthenextbestmoveis.Somethinglikethisiscommoninthree-matchtypegames,suchasBejeweled®orCandyCrush®,inwhichyouarelookingatgridandthegamegivesyouahint.ThistypeoflogiciswhereMinMaxAIcomesin.
MinMaxAIworksbytakinganinventoryofalloftheavailablemovesforourgameandplacingthemintoadecisiontree.BasedontheparameterswegivetheAI,wecantellithowtochoosethesedecisionbranches,typicallyintermsofgamedifficulty.Thisisdonebytakingintheplayers,alistofalltheirpossiblemovesaswellastheirscores,andpluggingthemintoaGameModelprotocolthatthenusesMinMaxAltodeterminethebestmove.TheTic-Tac-ToeexamplefromWWDC15isshownintheprecedingdiagram.NotehowsomebrancheswouldleadtomorelossesthandrawsorwinsforthecomputerAI.Aharderdifficultylevelwouldmakethecomputerplayerchoosethepathsthatmorelikelyleadtoawinforit,or,inthecaseofthosethree-matchgames,givetheplayerasuggestionforthenextbestmove.
Ofcourse,asonemighthaveguessed,thistypeoflogicisbestforturn-basedortile-basedgames.MinMaxAIcanworkinanygame,butthatgame,oratleasttheimplementationofMinMaxAI,willonlyworkifthere’sasetbaseofmovesandfuturemovesforittotakeintoitsGameModelprotocol.Anactionplatformer,unlessgivensomechoiceoffeatures,wouldn’tbeabletouseMinMaxAI,forexample.What’sgreataboutthisfunctionalityinGameplayKitisthatitdoesn’tneedtoknowthedetailsofyourgame’srules;itjustneedstheabilitytolookintofuturepossiblemoves.
TheclassdiagramshowstheclassesandfunctionsusedwhendealingwithMinMaxAI.WeseeGKGameModel,whichisactuallyaprotocolforagamestate.TheGKStateobjectsthatadheretothisprotocolneedtoprovidealistofplayers,theactiveplayer,theplayer’sscore,andtheplayer’slistofmoves,thelatterviathegameModelUpdatesForPlayer()function.WethentelltheGKGameModelobjectwhattodoasitmovesontothenextgamemovewiththeapplyGameModelUpdate()function.GKGameModelUpdateisessentiallyanabstractofagame’smoveandisusedbytheGKMinMaxStrategistclasstobuildadecisiontree,whichisthusappliedtoGKGameModeltochangethatstateinthesetGameModel()function.
TheGKGameModelPlayerclassisaprotocolforaplayerofthegamewhomakesamove,asstatedpreviously,withGKGameModelUpdate.TheplayerIdpropertyisauniquenumberyoucanset,whichisusedtodifferentiatetheplayersinourgame’slogicanddealwiththeirownsetofmoves.Thisallowstheflexibilitytohavebothahintingstructurefortheplayer(orplayersinamultiplayergame)inadditiontoalsohavingthecomputerplayerhaveanAIforitsownmoves.TheplayerIDpropertyisrequiredtoadheretothisprotocolaswewouldn’tknowtheplayerwearereferencingwithoutit.
TheGKMinMaxStrategistclassistheactualAIitselfthatistiedtothegameModelpropertywecreatedwiththepriorprotocols.ThemaxLookAheadDepthpropertyishowmanymovesaheadtheAIwilllook,themorethebetterandthenitreturnsthebestmoveviathebestMoveForPlayer()function.WecanusetherandomMoveForPlayer()functiontoaddabitofrandomnesstothenextmovechoices;thiscouldbeusedparticularlyforthecomputer’sownAItomaybepurposelycauseittomakemistakesbychoosingalessoptimalmove.
AquickObjective-Csnippetshowinghowtodothisincodeisgiveninthefollowingcode.Don’tworryaboutthesyntaxifyouareonlyfamiliarwiththeSwiftlanguagewe’veprovidedinthisbook;justgetanideaonthebasicsforsettinguptheseobjects.
/*ChessGameModelimplementsGKGameModel*/
ChessGameModel*chessGameModel=[ChessGameModelnew];
GKMinmaxStrategist*minmax=[GKMinmaxStrategistnew];
minmax.gameModel=chessGameModel;
minmax.maxLookAheadDepth=6;
/*Findthebestmovefortheactiveplayer*/
ChessGameUpdate*chessGameUpdate=
[minmaxbestMoveForPlayer:chessGameModel.activePlayer];
/*Applyupdatetothegamemodel*/
[chessGameModelapplyGameModelUpdate:chessGameUpdate];
Thisisalso,likemanyofthecodesnippetsinthischapter,takenfromtheWWDC15conference.Itusesachessgameasanexample.Theintricatedetailsofsettingupachessgamemodelareabitcomplex,sosimplytakenoteofhowinthiscodeaChessGameModelobject(whichisachildoftheabstractGKGameModelclass)isfirstcreated.Then,wecreateanobjectoftheGKMinMaxStrategistclassnamedminmax,setitsgamemodel,setitsmaxLookAheadDepthpropertyto6,andpassthegame’smoveandthecurrentactiveplayertotheminMaxobject.Finally,weupdatethegame’smodelwiththeapplyGameModelUpdate()function.It’salsodoneinObjective-Catthetimeofthispublication,butcheckouttheFourInaRowdemofoundhere:https://developer.apple.com/library/prerelease/ios/samplecode/FourInARow/Introduction/Intro.html
ThisprojectwillletusseeamorecompleteimplementationofthisAI.
ForevenmoreonMinMaxAI,checkoutthefollowingdocumentationlink:
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/Minmax.html
NextwewilltalkaboutaddingcontrolledrandomnesstoourgameswithGameplayKit’srandomsources.
RandomsourcesRandomnessingameshasbeenastapleofAI,playermoves,leveldesign,andgamereplayabilitysincetheearlydaysofgamedevelopment.Therand()functioninvariousprogramminglanguages,inadditiontoarangeofnumberstoscalethatrandomness,hastypicallybeenusedtogiveourapplicationssomelesspredictableoutcomes.However,gamessometimesneedtohavewhatweliketocallcontrolledrandomness.Whendebuggingagame,wedon’twanttoeverrunintoaproblemwhereashippedproducthasanuntestedstate.Sometimes,whenusingpastconventionsofrandomness,wecanrunintoasituationwheretheonlytimesomerareeventshappenmaybeafteragameisoutandinthehandsofthousands,ifnotmillions,ofplayerswhoaddtothetestingpoolthatwedidn’thaveinthedevelopingphase.Therefore,wemaywanttocontrolthedistributionofrandomness.Inatypicalrandomselectionofoutcomes,wegetabellcurveofresultswheretheaverageormiddle-rangedoutcomeswilloccurmoreoftenthanfringeoutcomes.Thisisfineinsomegames,butratherundesirableinothers.Anotherbitabouttherand()functionisthatitsrandomnesscanvarybasedonotherfactors,suchasthesystemit’son,thecurrentdateandtime,andotheruncontrollablefactors.Whatweneed,then,isplatform-independentdeterminismandcustomizabledistribution.WithGameplayKit’srandomsources,wecanaccomplishthat.
Weseeanumberofthedifferentclasseswecanuseintheprecedingimage.ThebaseclassisGKRandomSource,whichactuallyusestheARC4typealgorithmbydefault(viaitsGKARC4RandomSourcesubclass).ARC4isaquick/lowoverheadandhasthetypicalrandomnessthatweuseinmanyinstances.It’sdifferentfromthearc4Random()CcallinwhichinstancesofGKARC4RandomSourceareindependentfromeachother.GKRandomSourcecanalsobecomeasubclasstoeithertheLinearCongruentialortheMersenneTwisteralgorithms.Theirbenefitsanddisadvantagesareshowninthediagram.
It’snotrecommendedthattheseobjectsareusedforcryptography,soit’sbesttouseothertheencryption/hashingframeworksthatApplerecommends(https://developer.apple.com/library/ios/documentation/Security/Conceptual/cryptoservices/GeneralPurposeCrypto/GeneralPurposeCrypto.html
Theremainingclassesgiveuscontroloftherandomnumber/outcomedistributionmethodologies.TheGKRandDistributionobjectsletususehelpermethodsthat,forexample,giveustheabilitytocreatex-sideddiepiecesinadditiontolettingussetitslowestandhighestrangevalues.TheGKGaussianDistributionandGKShuffledDistributionclassesalsoletususethosehelperfunctions,butGKGaussianDistributionisusedwhenwewanttohaveabell-curvetyperandomizationwherethemiddlevalueshappenmoreoftenthanthefringevalues.Itsmeananddeviationpropertiesgiveuscontrolsonthatbellcurveandifwemaybewantmoreoccurrencesoffringevalues.GKShuffledDistribution,aswecantellfromitsname,isgreatforcreatinganevenandcompleterangedistribution,forshufflingdecksofcards,ormakingsurethateveryvalueoccursevenly.Thisclass’suniformDistancepropertyisafloatbetweenthevaluesof0.0and1.0.At0.0,allshufflingiscompletelyrandom;at1.0,thedistributionofallvaluesiseven.
Addingrandomsourcestoourgamesisverysimple.Here’ssomecodeexamplesusingtheseclasses:
/*Createasix-sideddiewithitsownrandomsource*/
letd6=GKRandomDistribution.d6()
/*Getdievaluebetween1and6*/
letchoice=d6.nextInt()
/*Createacustom256-sideddiewithitsownrandomsource*/
letd256=GKRandomDistribution.die(lowest:1,highest:256)
/*Getdievaluebetween1and256*/
letchoice=d256.nextInt()
/*Createatwenty-sideddiewithabellcurvebias*/
letd20=GKGaussianDistribution.d20()
/*Getdievaluebetween1and20thatismostlikelytobearound11*/
letchoice=d20.nextInt()
/*Createatwenty-sideddiewithnoclusteredvalues—fairrandom*/
letd20=GKShuffledDistribution.d20()
/*Getdievaluebetween1and20*/
letchoice=d20.nextInt()
/*Getanotherdievaluethatisnotthesameas'choice'*/
letsecondChoice=d20.nextInt()
/*Makeadeckofcards*/
vardeck=[Ace,King,Queen,Jack,Ten]
/*Shufflethem*/
deck=GKRandomSource.sharedRandom().shuffle(deck)
/*possibleresult-[Jack,King,Ten,Queen,Ace]*/
/*Getarandomcardfromthedeck*/
letcard=deck[0]
Aswecansee,theseareveryquick,simplelinesofcodethatallusethevariousrandomsourceclasses.Mostaresimplepropertycalls,sothatwhenwecreateourobjectsinSwift,asseenintheprecedingcode,itjustneedsoneortwolinesofcodetoutilizetheseclasstypesandtheirvariousrandomizationfunctionalities.Combiningthistothegoalweightof,say,awanderortrackAIbehavior,andwegetsomediverseandmoderatelycontrolledrandomnessfortheobjectsandcharactersinourgames.
Toreadupmoreonrandomsources/randomizationinthisframework,seethe
documentationlinkhere:
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/RandomSources.html
RulesystemsLast,butnotleast,wecometoGameplayKit’srulesystems.Thisaspectoftheframeworkuseswhat’sknownasfuzzylogicorapproximations,mainlyinthecontextoftransitionsbetweengamestates.Thisisn’tsomethingalltoonewtogamedevelopment.Anyonefamiliarwithlinearinterpolationwillberightathomeasthisispracticallythesameconcept.Unlikethetypicaluseoflinearinterpolation,whichtendstorevolvearoundtransitionsbetweenphysicalactions,GameplayKit’srulesystemsperformtheseapproximatetransitionsbetweenvariousgamestates.Thinkoftheobjects/entitiesinourgamesasnouns,thecomponentsandactionsasverbs,andtheserulesastheinteractionsbetweentheseverbsandnouns.Aswe’veseenthroughoutthischapter,thiswouldverymuchdescribegamestates.Sowhyaddanextralayertothislogic?Well,let’slookatthisexamplefromtheGameplayKitannouncement.Thisiswheretransitionsbetweengamestatesand/orentity-componentactionscouldusethisfuzzylogic:
if(car.distance<5){
car.slowDown()
}
elseif(car.distance>=5){
car.speedUp()
}
ThispseudocodecouldrepresentacarNPCinourgame.Maybeacitybuildinggame,wheretherearevariouscarGKAgentobjectsthathavethiscodeaspartoftheirbehavior.Thisseemssounduntilwegettovaluesatornear5.WhatwemightnoticeinourgameareabunchofNPCcarsacceleratingandbrakinginajerkymotion.Tosolvethis,wemakethetransitionsbetweenbrakingandacceleratingnotbesofinite,butinsteadtransitioninapproximation.
Theprecedingimageisabetterillustrationofthis,withtheoriginallogicontheleftandfuzzylogicontheright.Thiscreatesasmoothtransitionbetweenactionsorstateswhererulesystemscomeintoplay;herearetheclassesweusetoimplementthistypeoflogic:
WeusetheGKRuleSystemandGKRuleclassinstancestoutilizerulesystems.GKRulerepresentsaspecificdecisiontobemadebasedonanexternalstate,andGKRuleSystemevaluatesasetofrulesagainststatedatatodetermineasetoffacts.Wecanassertfactsorretractthem,andwecangradethefuzzinessfactorbetweentheserules.
Let’stakealookatthisincodetogetabetterfeelforit:
/*Makearulesystem*/
GKRuleSystem*sys=[[GKRuleSystemalloc]init];
/*Gettingdistanceandassertingfacts*/
floatdistance=sys.state[@"distance"];
[sysassertFact:@"close"grade:1.0f-distance/kBrakingDistance];
[sysassertFact:@"far"grade:distance/kBrakingDistance];
/*Gradeourfacts-farnessandcloseness*/
floatfarness=[sysgradeForFact@"far"];
floatcloseness=[sysgradeForFact@"close"];
/*DeriveFuzzyacceleration*/
floatfuzzyAcceleration=farness-closeness;
[carapplyAcceleration:fuzzyAccelerationwithDeltaTime:seconds];
First,thesysobjectofGKRuleSystemiscreated,andwegrabthedistancestatevalueandsavethattothedistancevariable.Wethenassert/addarulenamedclosethathappensif1.0f-distance/kBrakingDistance.Thenextfiniteruleweaddisfar,whichisdefinedasdistance/kBrakingDistance,orbasicallyanydistancegreaterthan1-distance/kBrakingDistance.Wecreatenewfuzzyvaluesofcloseandfar,namedfarnessandcloseness,thatarebasedonthegradeForFactpropertyofGKRuleSystem.Then,fromthis,wegetourfuzzyAccelerationvaluefromthedifferencebetweenfarnessandclosenessandapplythataccelerationtoourcar.Thisischeckedduringtheupdaterendercycleautomaticallyandkeepsthelogictransitionssmooth,removingjerkymovementsbetweenthedifferentstates.
ThissimpleexamplecodefromWWDC15isinObjective-C,butwecanseemoreexamples(someinSwift)inthefulldocumentationpageasfollows:
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/GameplayKit_Guide/RuleSystems.html
Wecanalsoseesomeofthisimplementedinthedemoprojectswelinkedtopreviously.
Withtheseclasses,wecancreateanumberofcomplexrulesystemsthattransitionina
morefluidfashion.
SummaryThischapterhasgoneintoagreatdealofthisdeepandindependentgame-centricframework.Wehavefirstreviewedthebasicconceptsofentitiesandcomponents,andhowGameplayKittakesadvantageofthecomponent-basedstructuring.Wethenmovedontoastapleofgamedevelopment,theconceptofstatemachines,andhowGameplayKitutilizesthem.Then,wehavereviewedwaysbywhichwecanautomaticallycontrolcomponentsandentitiesinourgameswithagents,goals,andbehaviors,aswellasPathfinding’snavigationgraphsthataddtothisautomation.WehavelearnedthatMinMaxAIletsushintfuturemovestotheplayerorgivethecomputerasmartwayofchallengingusinvariousturn-basedgames.Finally,wehaveseenhowrandomsourcesaddcontrollablevarietytooutcomesinourgames,whereasRulesystemscankeeptransitionsofvariousstatesfrombeingtoofinite.There’smuchmoretoGameplayKitthanwecouldshowhere,soit’shighlyrecommendedthatyoureadthroughsomeofthedocumentationlinksprovidedearliertogetanevenbetterfeelforwhatthisframeworkhastooffer.Inthenextchapter,wemoveontotheMetalAPIaswellassomeothertricksandtipsthataidbestinmakingthemostoutofyourgameandkeepingyourgamesatthatalltoocrucial60fps.
Chapter6.ExhibittheMetalinYourGameUptothispoint,wehavelearnedquiteabit.WelookedintoApple’sSwiftprogramminglanguage,gotanideaofthegeneralflowofaniOSapp,andhowtocontrolthatthroughcodeand/orstoryboards.Wegotanunderstandingofhow2Dgamesand2DoverlayscanbemadewithSpriteKitandhow3DgamescanbedesignedevenintheXcodeeditorwithSceneKit.Finally,wereviewedhowtocreatereusablegamelogic,components,andAIwiththevariousaspectsofGameplayKit.
Essentially,thisisallthatisneededtogetrighttoplanning,coding,andbuildingyourowngames.Ifthere’sagameideathathascometoyourmindatthistime,gorightaheadandstartplanningitout.TheframeworksandXcodefeaturesfromthepastchapterscanhelptakeyourabstractideasandstartturningthemintowhatcouldsoonbeaplayableapplication.
However,beforemovingforward,we’dliketotakethistimetogooverafewmoretips,tricks,andtopicsthatweeitherbrieflymentionedorhaveyettogoover.ThesetopicsmainlycoverthewayswecanoptimizeourgamesandgetmoreoutoftheApplehardware.Inthischapter,weshallreviewabitontheratheradvancedtopicoftheAppleMetallow-levelgraphicsAPI.
NoteJustawarningthatthetopicoflow-levelgraphicsAPIscangetratheradvanced.Thiswon’tbeanall-encompassingtutorialonthesubject;moreofanupper-levelsummaryandawaytoappreciateallthatSpriteKitandSceneKitdointhebackgroundforus.Wehopethat,attheveryleast,itmakesyouwishtopursuehowtobuildyourowncustomrenderingobjectsthatmightpotentiallyallowthedevelopmentofextremelyperformantanddetailedgames.
TheAppleMetalAPIandthegraphicspipelineOneoftherules,ifnotthegoldenruleofmodernvideogamedevelopment,istokeepourgamesrunningconstantlyat60framespersecondorgreater.IfdevelopingforVRdevicesandapplications,thisisofevenmoreimportanceasdroppedframeratescouldleadtoasickeningandgameendingexperiencefortheplayer.
Inthepast,beingleanwasthenameofthegame;hardwarelimitationspreventedmuchfromnotonlybeingwrittentothescreenbuthowmuchmemorystorageagamecouldhold.Thislimitedthenumberofscenes,characters,effects,andlevels.Inthepast,gamedevelopmentwasbuiltmorewithanengineeringmindset,sothedevelopersmadethethingsworkwithwhatlittletheyhad.Manyofthegameson8-bitsystemsandearlierhadlevelsandcharactersthatwereonlydifferentbecauseofelaboratespriteslicingandrecoloring.
Overtime,advancesinhardware,particularlythatofGPUsallowedforrichergraphicalexperiences.Thisleadstotheadventofcomputation-heavy3Dmodels,real-timelighting,robustshaders,andothereffectsthatwecanusetomakeourgamespresentanevengreaterplayerexperience;thiswhiletryingtostuffitallinthatprecious.016666second/60Hzwindow.
Togeteverythingoutofthehardwareandcombattheclashbetweenadesigner’sneedtomakethebestlookingexperienceandtheengineeringrealityofhardwarelimitationsineventoday’sCPU/GPUs,AppledevelopedtheMetalAPI.
CPU/GPUframeworklevelsMetaliswhat’sknownasalow-levelGPUAPI.WhenwebuildourgamesontheiOSplatform,therearedifferentlevelsbetweenthemachinecodeinourGPU/CPUhardwareandwhatweusetodesignourgames.Thisgoesforanypieceofcomputerhardwareweworkwith,beitAppleorothers.Forexample,ontheCPUsideofthings,attheverybaseofitallisthemachinecode.Thenextlevelupistheassemblylanguageofthechipset.AssemblylanguagediffersbasedontheCPUchipsetandallowstheprogrammertobeasdetailedasdeterminingtheindividualregisterstoswapdatainandoutofintheprocessor.Justafewlinesofafor-loopinC/C++wouldtakeupadecentnumberoflinestocodeinassembly.Thebenefitofworkinginthelowerlevelsofcodeisthatwecouldmakeourgamesrunmuchfaster.However,mostofthemid-upperlevellanguages/APIsaremadetoworkwellenoughsothatthisisn’tanecessityanymore.
NoteGamedevelopershavecodedinassemblyevenaftertheveryearlydaysofgamedevelopment.Inthelate1990’s,thegamedeveloperChrisSawyercreatedhisgame,RollercosterTycoon™,almostentirelyinthex86assemblylanguage!Assemblycanbeagreatchallengeforanyenthusiasticdeveloperwholovestotinkerwiththeinnerworkingsofcomputerhardware.
MovingupthechainwehavewhereC/C++codewouldbeandjustabovethatiswherewe’dfindSwiftandObjective-Ccode.LanguagessuchasRubyandJavaScript,whichsomedeveloperscanuseinXcode,areyetanotherlevelup.
ThatwasabouttheCPU,nowontotheGPU.TheGraphicsProcessingUnit(GPU)isthecoprocessorthatworkswiththeCPUtomakethecalculationsforthevisualsweseeonthescreen.ThefollowingdiagramshowstheGPU,theAPIsthatworkwiththeGPU,andpossibleiOSgamesthatcanbemadebasedonwhichframework/APIischosen.
LiketheCPU,thelowestlevelistheprocessor’smachinecode.ToworkasclosetotheGPU’smachinecodeaspossible,manydeveloperswoulduseSiliconGraphics’OpenGLAPI.Formobiledevices,suchastheiPhoneandiPad,itwouldbetheOpenGLsubset,OpenGLES.Appleprovidesahelperframework/librarytoOpenGLESnamedGLKit.GLKithelpssimplifysomeoftheshaderlogicandlessenthemanualworkthatgoesintoworkingwiththeGPUatthislevel.Formanygamedevelopers,thiswaspracticallytheonlyoptiontomake3DgamesontheiOSdevicefamilyoriginally;thoughsomeuseofiOS’sCoreGraphics,CoreAnimationandUIKitframeworkswereperfectlyfineforsimplergames.
NottoolongintothelifespanoftheiOSdevicefamily,third-partyframeworkscameintoplay,whichwereaimedatgamedevelopment.UsingOpenGLESasitsbase,thussittingdirectlyonelevelaboveit,istheCocos2Dframework.ThiswasactuallytheframeworkusedintheoriginalreleaseofRovio’sAngryBirds™seriesofgamesbackin2009.Eventually,Applerealizedhowimportantgamingwasforthesuccessoftheplatformandmadetheirowngame-centricframeworks,thatis,theSpriteKitandSceneKitframeworks.Theytoo,likeCocos2D/3D,satdirectlyaboveOpenGLES.WhenwemadeSKSpritenodesorSCNNodesinourXcodeprojects,upuntiltheintroductionofMetal,OpenGLoperationswerebeingusedtodrawtheseobjectsintheupdate/rendercyclebehindthescenes.AsofiOS9,SpriteKitandSceneKituseMetal’srenderingpipelinetoprocessgraphicstothescreen.Ifthedeviceisolder,theyreverttoOpenGLESastheunderlyinggraphicsAPI.
GraphicspipelineoverviewThistopiccanbeabookallonitsown,butlet’stakealookatthegraphicspipelinetogetanidea,atleastonanupperlevel,ofwhattheGPUisdoingduringasinglerenderedframe.Wecanimaginethegraphicaldataofourgamesbeingdividedintwomaincategories:
Vertexdata:Thisisthepositioninformationofwhereonthescreenthisdatacanberendered.Vector/vertexdatacanbeexpressedaspoints,lines,ortriangles.Remembertheoldsayingaboutvideogamegraphics,“everythingisatriangle.”Allofthosepolygonsinagamearejustacollectionoftrianglesviatheirpoint/vectorpositions.TheGPU’sVertexProcessingUnit(VPU)handlesthisdata.Rendering/pixeldata:ControlledbytheGPU’sRasterizer,thisisthedatathattellstheGPUhowtheobjects,positionedbythevertexdata,willbecolored/shadedonthescreen.Forexample,thisiswherecolorchannels,suchasRGBandalpha,arehandled.Inshort,it’sthepixeldataandwhatweactuallyseeonthescreen.
Here’sadiagramshowingthegraphicspipelineoverview:
Thegraphicspipelineisthesequenceofstepsittakestohaveourdatarenderedtothescreen.Thepreviousdiagramisasimplifiedexampleofthisprocess.Herearethemainsectionsthatcanmakeupthepipeline:
Bufferobjects:TheseareknownasVertexBufferObjectsinOpenGLandareoftheclassMTLBufferintheMetalAPI.ThesearetheobjectswecreateinourcodethataresentfromtheCPUtotheGPUforprimitiveprocessing.Theseobjectscontaindata,suchasthepositions,normalvectors,alphas,colors,andmore.
Primitiveprocessing:ThesearethestepsintheGPUthattakeourBufferObjects,breakdownthevariousvertexandrenderingdatainthoseobjects,andthendrawthisinformationtotheframebuffer,whichisthescreenoutputweseeonthedevice.
BeforewegooverthestepsofprimitiveprocessingdoneinMetal,weshouldfirstunderstandthehistoryandbasicsofshaders.
Whatareshaders?GPUsfirstcameintousebecauseofnoneotherthanthevideogameindustry.Arcadecabinetsinthe1970’shadGPUchipsseparatefromthemainCPUtohandlethespecializedvisualneedsofthegamescomparedwithothercomputingapplicationsatthetime.Eventually,theneedtodraw3Dgraphicsingamesinthemid-1990’sledtothemodernGPUarchitecturewehavenow.Shaderswereactuallyfirstintroducedin1988byPixarbackwhenthecompanywasrunbyApple’scofounderSteveJobs.ShadersarelittleprogramswecanwritedirectlytotheGPUtoprocessthevertexandpixeldata.Originally,APIssuchasOpenGLES1.0didn’tmakeuseofshaderprocessingbutinsteadwerewhat’sknownasfixed-functionAPIs.Infixed-functionAPIs,programmersjustreferencedsimplesetrenderingcommandstotheGPU.AsGPUsevolvedandtookmoreworkawayfromtheCPU,theuseofshadersincreased.Althougharathermoreadvancedwaytotraversethegraphicspipelinethanthefixed-functionmethodology,shadersallowforevendeepercustomizationofwhattheGPUdisplaystothescreen.Gamedevelopersand3Dartistscontinuetopushvisualeffectsingameswiththem.
FromOpenGL2.0andonwards,shaderswerebuiltintheAPI’sC-likelanguagenamedGLSL.IntheAppleMetalAPI,webuildshaderswiththeMetalShadingLanguage,whichisasubsetofC++11ofthefiletype.metalandcanrunthepipelineineitherObjective-CorSwiftwithourviewcontrollers.
TypesofshadersShaderscomeinanumberoftypesthatcontinuetogrowas3Dgamesandartanimationcontinuestoprogress.ThemostcommonlyusedareVertexshadersandFragmentshaders.Vertexshadersareusedtotransform3Dcoordinatesinto2Dcoordinatesforthescreentodisplay,inshort,thepositioningdataofourgraphics.Fragmentshaders,alsoknownasPixelshaders,arewhatareusedtoconvertcolorsandothervisualattributesofpixelsonthescreen.TheseotherattributesofFragmentShaderscanincludebumpmapping,shadows,andspecifichighlightsaswell.Weemphasizedthewordattributesbecausethat’susuallythenamegivenforthepropertiesorinputofourshaderprograms.
HereisacodesampleofasimpleVertexandFragmentshaderwrittenintheMetalShadingLanguage.
//Shaders.metal
//(1)
#include<metal_stdlib>
usingnamespacemetal;
//(2)
vertexfloat4basic_vertex(
//(3)
constdevicepacked_float3*vertex_array[[buffer(0)]],
//(4)
unsignedintvertexID[[vertex_id]]){
//(5)
returnfloat4(vertex_array[vertexID],1.0);
}
//(6)
fragmenthalf4basic_fragment(){
returnhalf4(1.0);
Thecodehereisabitdifferentthanwhatwe’veseenthroughoutthecourseofthebook.Let’sgooveritlinebyline.
1. TheMetalShadingLanguageisaC++11-likelanguage,soweseethattheMetalStandardLibraryisimportedintotheshaderfilewiththeline#include<metal_stdlib>inadditiontousingnamespacemetal;.
2. ThenextlineisthecreationofourVertexshaderusingthekeywordvertex.Thisshaderisavertexoffourfloats.Whyfourfloatswhen3Dspaceonlydealswithx,y,andzcoordinates?Tosummarize,3Dmatrixmathinvolvesafourthcomponent,w,toaccuratelyhandlethemathcalculationsof3Dspace.Inshortifw=0,thex,y,andzcoordinatesarevectors;ifw=1,thenthosecoordinatesarepoints.Thepurposeofthisshaderwillbetodrawsimplepointstothescreen,sowwillbe1.0.
3. Here,wecreateapointertoanarrayoffloat3type(holdersforourx,y,andzcoordinates)andsetittotheveryfirstbufferwiththe[[buffer(0)]]declaration.The[[]]syntaxisusedtodeclareinputs/attributesforourshaders.
4. TheunsignedintegervertexIDiswhatwenamethevertex_idattributeofthisparticulararrayofvertices.
5. Thisiswherethefloat4typeisreturned,orinthiscase,thefinalpositionofthisvertexarray.Weseethatitreturnstwosectionsoftheoutput:thefirstbeingthe
referencetothisvertexarray,identifiedbythevertex_idattributeandthewvalueof1.0,torepresentthatthesearepointsinspace.
6. Thislineiswherewecreatethefragmentshader,usingthefragmentkeyword.Thisshaderisofthedatatypehalf4,whichisanarrayof[4,4]16-bitfloats.Thisis,inthiscase,ultimatelytocreate16-bitcoloredpixels.Thedatainthis[4,4]-componentvectortypesaves16bitstoR,G,B,andalphachannels.Thisshaderisgoingtosimplyshowpurewhitepixelshadingwithnotransparency,sowesimplywritereturnhalf4(1.0);.Thissetsallofthebitsto1,whichisequivalenttorgba(1,1,1,1).
WhenwecreateaBufferObject,whichcanjustbeaStructoffloatingpointsonthescreen,wepassthatdatathroughtheseshadersandoutwouldpopupawhitetriangleorsetoftriangleshapesonthescreen.
LookingbackattheGraphicspipelinediagram,weseethatafterthevertexshaderiscalculated,theGPUdoeswhat’sknownasPrimitiveAssembly.Thisisessentiallywherethepointsandvectorsdefinedinthevertexshaderaremappedtocoordinatesinscreenspace.TheRasterizerstep,insimpleterms,thenfiguresfromthevertexdatawhereandhowwecanandcan’tcolorthatpixeldataontothescreenusingthefragmentshaderinformation.Aftertakinginthefragmentshaderinformation,theGPUthenusesthatinformationfortheblendingofthatpixeldata.Finally,thatoutputissenttoorcommittedtotheframebufferwheretheplayerseesthatoutput.Thisallhappensinasingledrawcallintherendercycle.Havingallofyourgame’slights,pixels,effects,physics,andothergraphicscyclethroughthisin.016666secondsisthenameofthegame.
We’llgooversomemoreMetalcodelaterbutunderstandfornowthatshadersarelikelittleinstructionfactoriesfordatainputwesendtotheminourSwift/Object-Ccode.OthershadertypesthathavearisenovertheyearsareGeometryShadersandTessellationShaders.
NoteBoththeVertexandFragmentshadersarepresentinthissingle.metalfile,buttypicallyshadersarewritteninseparatefiles.XcodeandMetalwillcombineall.metalfilesinyourproject,soitdoesn’tmatteriftheshadersareinonefileornot.OpenGL’sGLSLforthemostpartforcestheseparationofshadertypes.
Foryears,OpenGLworkedwellformanydifferentGPUsbutasweallsee,AppleMetalallowsustoperformdrawcallsupto10xtimesfasterthanOpenGLES.
WhyisMetalfasterthanOpenGLES?Inlate2013,AppleannouncedtheiPhone5s.Builtintothe5swastheA7Processor,thefirst64bitGPUfortheiOSdevicefamily.ItprovidedadecentgraphicalboostcomparedwithpriordevicesandreflectedhowGPUsinmobiledeviceswerequicklycatchinguptogamingconsolesreleasedjustafewyearsprior.OpenGL,thoughastapleinlow-levelgraphicsAPIs,didn’tsqueezethemostoutoftheA7chip.
Seeninthenextdiagram,theinteractionbetweentheCPUandGPUdoesn’talwaysperformtheoptimalwaywe’dwantittoforourgames.
Beittextures,shaders,orrendertargets,drawcallsusetheirownstatevector.TheCPUviathelow-levelAPIusesmuchofthattimeverifyingthestateofthedrawcall.ThisprocessisveryexpensivefortheCPU.Whathappensisthatinmanycycles,theGPUissittingidle,waitingfortheCPUtofinishitspastinstruction.Here’swhat’stakingupallofthattimeintheAPI:
Statevalidation:ConfirmingAPIusageisvalid.ThisencodesAPIstatetothehardwarestate.Shadercompilation:Runtimegenerationoftheshadermachinecode.Thisdealswithinteractionsbetweenthestateandshaders.SendingworktotheGPU:Managingresourceresidencybatchingcommands.
WhatAppledidwiththeirMetalAPIisdothesestepsinasmarterfashion.Shadercompilationisdoneduringtheapplication’sloadtime.There’snoneedtoreloadtheshaderseverycycle;thiswassimplyarelicofolderhardwarelimitations.Thisiswhyinourpreviouscodeexample,wecanbuildmorethanoneshaderinoneMetalfile,whilethiswasprohibitedinOpenGLES.Statevalidation,thoughimportant,doesn’tneedtobecheckedeverycycle.Checkingthestatevalidationcanbesettohappenonlywhennewcontentisloaded.
NoteEvenwithMetal’sadvantages,thisiswhyit’srecommendedtostore2DanimationsinSpriteSheets.WementionedSpriteSheetsbackinourdiscussionofonSpritKit.Theyareacollectionofspritesfittedontoonetexture.Thegraphicspipelinethenonlyhastodeal
withoneversionofthatcontent.InternallyunderthehoodofSpriteKit,theGPUthendoesn’thavetodoasmanystatevectorcallscomparedtohavingeachcharacteranimationbeingplacedonitsownseparatetexture.
ThelastprocessfortheCPUiswhenitsendstheinformationouttotheGPUforprocessing.Thisisgoingtobedoneduringeachdrawcall,andineitherMetalorOpenGLES,itwillstillbethisprocessthatwillhappenthemostfrequently.Hereistheresultofthisinternal,low-levelrestructuringdoneintheMetalAPI:
AsweseeinthediagramfromWWDC14,thereareupto10extradrawcallsthatcanbeaddedduringtherendercycle!Wecanusethattimesavedforotherprocessesinsteadofextradrawcalls,suchasmorephysicsorAIinourgames.
NoteThecyclediagramsshownarefromtheoriginalMetalAPIannouncementatWWDC2014andusedaframerateof30fps.IfdevelopingforVRwhere60fpsorgreaterisnecessaryforaworkinggame,thesenumbersarehalved.EitherwaythisisratherimpressiveformobiledeviceGPUs.SearchonlineforgamesmadeinMetalandyou’dbeimpressed.Withthismuchroomtoaddmoretoourgameduringeachrendercycle,there’snoreasonnottohaveanimpressivegameatthefull60fps.Additionally,asofiOS9,theSpriteKitandSceneKitframeworksbydefaultarebackedbyMetal.EveniftheMetalAPIistoomuchtounderstand,wecanstillutilizetheserendersavingbenefitsfromwhatwealreadylearnedabouttheseframeworks.
ThebasicMetalobject/codestructureTofinishoffourtalkaboutAppleMetal,let’slookatanoverviewoftheAPI’sobjectandcodestructuring.WealreadybrieflysawsomeshadercodeintheMetalShadingLanguage,solet’sseehowwecanworkwiththisAPIinourprojects.
Objects Purpose
Device ReferencetotheGPU
Commandqueue Serialsequenceofcommandbuffers
Commandbuffer ContainsGPUhardwarecommands
Commandencoder TranslatesAPIcommandstoGPUhardwarecommands
State Framebufferconfiguration,depth,samplers,blend,andsoon
Code Shaders(vertex,fragment,geometry,andtessellation)
Resources TexturesandDataBufferObjects(vertices,constants,andsoon)
Theprecedingtablerepresentsthevarioustypesofobjectsthatwe’dworkwithifwritingagamedirectlyintheMetalAPI.TheyaretheDevice,theState,theCommandBuffer,ourShaders,Textures,andmanymore.
WecanimporttheMetalAPIintoViewController.swiftclasswiththefollowing:
importMetal
importQuartzCore
ThisimportstheMetalAPI.TheQuartzCoreAPIisneededaswellsincetheCAMetalLayerobjectwewillworkwithisamemberofthatlibrary.Also,makesurethatyousetyourtargetdevicetoanactualiOSdeviceasnewornewerthantheiPhone5S,theXcodesimulatordoesnotsupportMetal.Otherwise,XcodewillgiveyoutheCouldNotBuildObjective-CmodelMetalerror.ThisistrueasofthewritingofthisbookwiththeXcode7Beta.OvertimeandprobablyaftertheofficialpublicreleaseoftheElCapitanOS,thiswillnolongerbeneeded.Fornow,totestyourowncustomMetalcode,youwillhavetotestonanactualdevice.DoingsowillinvolvehavingtopayforyourownAppleDevelopmentaccount.Moreonthisisgiveninthenextchapter.
Here’stheorderinwhichwe’dhavetoworkwiththeobjectsinthetableshownpreviouslyaswellassomecodesamplesinSwiftthataccomplishthesesteps:
1. CreatethereferencetotheDevicewiththeMTLDeviceclassas:
letdevice:MTLDevice=MTLCreateSystemDefaultDevice()
2. CreateaCAMetalLayerobjectfortheseobjectstobeplacedonthescreenas:
letmetalLayer=CAMetalLayer()
3. CreateVertexData/BufferObject(s)(VBOs)tosenddatatoshadersasfollows:
/*SimpleVertexDataobject,anarrayoffloatsthatdrawsasimple
triangletothescreen*/
letvertexData:[Float]=[
0.0,1.0,0.0,
-1.0,-1.0,0.0,
1.0,-1.0,0.0]
4. CreateourshadersthatwillworkwiththeseVBOs.
Wedidthisinourpreviousshadercodesamples.Thevertexdatacombinedwithourpreviouslymadeshaderstogethercreateasimplewhitetriangletothescreen.
5. SetupaRenderPipelineasfollows:
//Libraryobjectsthatreferenceourshaderswecreated
letlibrary=device.newDefaultLibrary()!
//constantwherewepassthevertexshaderfunction
letvertexFunction=library.newFunctionWithName("basic_vertex")
//nowthefragmentshader
letfragmentFunction=library.newFunctionWithName("basic_fragment")
/*DescribestheRenderPipelineandsetsthevertexandfragment
shadersoftheRenderPipeine*/
letpipelineStateDescriptor=MTLRenderPipelineDescriptor()
//initiatesthedescriptor'svertexandfragmentshaderfunction
propertieswiththeconstantswecreatedprior
pipelineStateDescriptor.vertexFunction=vertexFunction
pipelineStateDescriptor.fragmentFunction=fragmentFunction
//Makesthepixelformatan8bitcolorformat
pipelineStateDescriptor.colorAttachments.objectAtIndexedSubscript(0).
pixelFormat=.BGRA8Unorm
/*ChecksifwedescribedtheRenderPipelinecorrectly,otherwise,
throwsanerror.*/
varpipelineError:NSError?
pipelineState=
device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor,
error:&pipelineError)
ifpipelineState==nil{
println("Pipelinestatenotcreated,error\(pipelineError)")
6. Createacommandqueueasfollows:
varcommandQueue=device.newCommandQueue()
Toactuallyrendertheseobjectsinourgame,we’dhavetodothefollowingprocessesinourviewcontroller:
1. Createadisplaylink.Thisisatimerthatrefresheseverytimethescreenrefreshes.It’samemberoftheclassCADisplayLinkandateveryscreenrefresh,wecallthegameRenderLoopfunction.
vartimer=CADisplayLink(target:self,selector:
Selector("gameRenderLoop"))
timer.addToRunLoop(NSRunLoop.mainRunLoop(),forMode:
NSDefaultRunLoopMode)
ThegameRenderLoopfunctioncanlooklikethefollowing.Itcallsthesoon-to-befilledinfunction,render():
funcgameRenderloop(){
autoreleasepool{
self.render()
}
2. CreateaRenderPassDescriptor.Forthisexample,amostlyredtextureistobecreatedaroundourwhitetriangleasshownhere:
letpassDescriptor=MTLRenderPassDescriptor()
passDescriptor.colorAttachments[0].texture=drawable.texture
passDescriptor.colorAttachments[0].loadAction=.Clear
passDescriptor.colorAttachments[0].storeAction=.Store
passDescriptor.colorAttachments[0].clearColor=MTLClearColorMake(0.8,
0.0,0.0,1.0)
3. CreateaCommandBufferinourrender()function:
letcommandBuffer=commandQueue.commandBuffer()
4. CreateaRenderCommandEncoder.Inotherwords,asetofcommandsforcommandQueue.Inthecodeexamplegivenlater,thistellstheGPUtodrawtriangleswiththeVBOwecreatedearlier.Thisisplaced(inthisexample)intherender()function.
letrenderEncoderOpt=
commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor)
ifletrenderEncoder=renderEncoderOpt{
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer,offset:0,atIndex:0)
renderEncoder.drawPrimitives(.Triangle,vertexStart:0,vertexCount:
3,instanceCount:1)
renderEncoder.endEncoding()
}
5. CommityourCommandBuffer.ThisessentiallytellstheGPUtodoitsdrawcallbasedonthecommandsthathavebeenpackedintothecommandBufferobject.Placethisafterthepastcode’sifstatementintherender()function.
commandBuffer.presentDrawable(drawable)
commandBuffer.commit()
Thatistheshortofit.That’sthegeneralprocessofdrawingasimpletriangletothescreenandmanuallycreatingtherenderloopontheGPU.
ShouldyouratheroptforSpriteKitandSceneKittodoallofthismanualworkforyou?Thatwouldbeunderstandable.Rememberthough,likewhenplayingagameonhard
mode,itcomeswithitsrewardstotaketheharderroute.Yes,asofiOS9,theSpriteKitandSceneKitframeworksaredefaulttoMetal.Gameengines,suchasUnityandUnrealEngine,evenimplementMetalwhenconvertingprojectstotheplatform.However,knowinghowtobuildyourgamesinalow-levelgraphicsAPI,suchasMetalorOpenGL,willgivethedevelopertheabilitytohavethepotentialformostlean/fastperforminggameforthedevicefamily.BesuretocheckoutsomeofthegamescreatedwithMetalnexttimeyousearchonline.Theycanreallygiveyourplayersagreatexperience.Atthesametime,thiscanchallengeyourskillsasagamedevelopersincebeingagamedeveloperisthecombinationofanartist,engineer,andcomputerscientist.WorkingdirectlyintheGPU’sbasicfunctionswillchallengeallofthat.
Todivemoreintotherabbitholethatislow-levelgraphicsdevelopmentwithMetal,checkouttheselinks:
https://developer.apple.com/metal/https://developer.apple.com/library/ios/documentation/Metal/Reference/MetalShadingLanguageGuide/data-types/data-types.htmlhttp://www.raywenderlich.com/77488/ios-8-metal-tutorial-swift-getting-startedhttps://realm.io/news/3d-graphics-metal-swift/
ThefirstlinkistotheofficialAppleDeveloperpageforMetal.ThenextlinkisApple’slistofdatatypesusedintheMetalAPI.ThelasttwolinksaretwoseparatetutorialstomakesimpleMetalscenesinSwift.SomeofthecodeweusedcanbefoundinthesetutorialsaswellasfullXcodeprojects.ThefirstofthesetwolinksaretotheiOStutorialsitewww.raywenderlich.com.ThelastlinkistoapagethathasagreatvideopresentationandfullinstructionsonSwiftandMetal3DgraphicsbyformerAppleEngineer,WarrenMoore.
SummaryCongratsongettingthisfar.Ifthisbookwereagame,we’dprobablyhaveearnedanachievementforthischapteralone.Aswesaw,workingwithalow-levelAPIsuchasMetalcanbeabitdaunting.Wefirstreviewedwhatitmeanswhendevelopersandengineersmentionlowerandupperlevelframeworksandcode.OntheCPUside,wesawthatthelowestlevelisthemachine’scodewithSwiftandObjective-Cinthemiddle,andaboveC/C++andAssemblycode.Next,wespokeabouttheGPUsideandwherethevisualgraphicsAPIswe’vegoneoverinthepastchaptersstandinthehierarchy.Wethengotanunderstandingofthehistoryoflower-levelgraphicsAPIssuchasOpenGLES,howthegraphicpipelinegenerallyworksunderthehood,andhowtomakebasicshaders.Finally,wereviewedwhyMetalisfasterduringtherendercyclethanOpenGL,thegeneralstructurebehindMetal,andsomeofthecode/objectsusedtomanuallysetuptherenderloop.Thischaptermerelyscratchedthesurfaceonthistopic,soifyouareuptothechallenge,it’shighlyrecommendedtocontinuereadingdocumentationonhowMetalcanmakeyourgamesstandoutfromtherest.
Atthispoint,youshouldnowhaveallthatittakestomakeagameontheiOSplatform.ThelastessentiallessonforiOSgamedevelopmentislearninghowtotest,publish,andupdateyourpublishedgameintheappstore.Inthenextchapter,let’slearnhowtogetthatgameontheAppleappstore.
Chapter7.PublishingOuriOSGameCreatingagreatgameishardwork.Ourgoalasgamedevelopersistohaveanapplicationthatcanbeplayedbythousands,ifnotmillionsofpeople.Wewantthemtoplaywhatwecraftedinthoselongweeksandmonths.Beforetheappisreleased,we’dalsoprobablywanttohaveotherstestthegametoweedoutanybugsthatmighthavebeenmissed.PublishingontheiOSplatformcanallowustodoboth.
WecanallowotherstotryoutourgamesbeforereleasethroughtheTestFlightservice,andofcourse,wecanthensubmitourgameforreleaseontheAppleAppStore.Afterrelease,wecansubmitupdates.Theseupdatesandfutureversionscouldbejusttopatchupsomeminorbugswemighthavemissedintheinitialreleases,addnewfeatures,suchaslevels,achievements,andotherAppleservices,orwemightneedtoupdateourapplaterontoadheretotheeventualupdatesthatwillhappenwiththeiOSplatform.
Inthischapter,wearegoingtocoverafewmaintopics:
SettingupourappsforeithertestingorpublishinginiTunesConnectStepstosubmitourapptobeplayedintheappstoreSummaryoftheTestFlightservicefortestingintheprereleasephaseHowtouseiTunesConnecttocreateupdatestoourapp
Wewon’ttellyouhowtomarketyourgame,asthat’sanentirebook/topiconitsownthatdependsonyourbudgetandpreferences.However,wewillsaythatifyouoryourbetatestersfoundplayingyourgamefun,thereisachancethatotherswilltoo.Submittingtotheappstoredoesnotwarrantinstantsuccess.
Evenunreleasedgamesthatgethighaccoladesattheevergrowinglistofindiegamerewardshowsorgamejamsmightnothaveseenthatpraisereflectedinsalesanddownloadsafterrelease.Wehavetorememberthatthegamedevelopmentscene,thanksinparttogaming’spositioninpopularculture,isaseaofthousandsofdevelopers,bothbigandsmall,tryingtomakethenextgreatgame.
Don’tletthatdiscourageyouthough.Ifyourgamedoeswell,whichithastheabilitytowiththeAppleAppStore,thenitcouldbealife-changingexperience.Nomattertheoutcome,lettheexperienceofbuildingyourowngameandlearningthisdevelopmentplatformhumbleyouandmakeyouwanttobeanevenbetterdeveloperforyournextproject.Eventually,thathardworkwillpayoff.
TheeverchangingprocessofappsubmissionBeforewemoveforwardinexplainingthestepsneededtopublishyourgame,wewantedtonotealittlefactaboutthissubject.ThisfactisthattheexactstepsneededtosubmitouriOSappsfortestingorpublicationisonethatchangesratheroften.Everycoupleofmonthsthisprocessmightchangefromhowwedescribeithere.
Overtheyears,sincethestartofiOSdevelopment,Applehascontinuallymadethisprocesseasierandmorestreamlined.Xcodedoesmuchmoreofthesigning/provisioningworkforusthanitdidinthepast,andtheworriesofourapptakingforevertoappearintheAppStorearehardlyanissue.
Forexample,whentheSwiftgamePikiPopwassubmittedinNovember,2014,itonlytookfivebusinessdaysbetweenthedayitwassubmittedforreviewtowhenitappearedintheAppStoreforthepublic.Thisreviewtimewillvaryforeachofus,butaslongastherearen’tanyterribleerrorsorpolicyviolationsinourapps,wecanexpectourcreationstobepublicformillionstopotentiallyplay.Tomakesurethatthepublicationofyourgamegoessmoothly,it’sbesttoreviewtheAppReviewGuidelinesfoundhere:https://developer.apple.com/app-store/review/guidelines/.
NoteWewrotethisbookinthelatesummerof2015,soifyouarereadingthisatamuchlaterdateandfeelsomeoftheseprocessesmightbeoutofdate,makesuretoseethemostrecentupdateshereonApple’sownAppsubmissiondocumentationpage:
https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
BeforesubmittingyourappThereisonemorepotentialdevelopmentsnagwemustwarnaboutbeforeyouchoosetosubmityourapptotheappstore.Asofthetimeofthispublication,ifyoubuildyourappinabetaversionofXcode,thatappwillberejectedfromreview.
Duringthecourseofthisbook,wehavebeenbuildingourappsandgoingoverfeaturesthatcurrentlyareinthebetaversionofXcode7.ThiswasbecausethereareanumberofnewfeaturesforiOS9/Xcode7thatweren’tthereiniOS8/Xcode6,namely,theGameplayKitframeworkandvisualeditortoolsforSceneKitthatmakeXcodedevelopmentashandsonasmultiplatformgameengines.
NoteBythetimeyouarereadingthis,Xcode7shouldnolongerbeinthebetaphase.Therefore,youshouldbeabletopublishaniOS9(orlater)gamewithoutworryingifthesefeaturesarebetaonly.
WhenyoubuildyourgamesforreleasingtotheAppStore,makesuretofirstbuildtheminthecurrentnon-betaversionofXcode.UsethebetaversionsofXcodetotestthenewestunreleasedfeaturesaswellasupandcomingiOSbuildsduringtheprereleasephase.
PreparingourappsforiTunesConnectSoyouhavecoded,simulated,andhopefullyenjoyedplayingabitofthatiOSgameyouworkedsomuchon.Thenextstepistobringyourappintothebeta/prereleasephase.Thegoalofthisphaseistogetyourgameintothehandsofasmallerpoolofgamers/testerstosimulatewhattheexperiencecouldbeforthethousands,ifnotmillionsofpeoplewhocouldpotentiallyplayyourgame.Theveryfirststeptothis,ifyouhaven’talready,istosignupfortheAppleDeveloperProgram:https://developer.apple.com/programs/.
Thereisacostinvolvedandthatcostwillbebasedonyourdevelopmentgoals.Forindividual,soleproprietorbusinessaccounts,itcosts$99ayeartobeaniOSdeveloper.Ifyouareworkingaspartofagroupofdevelopers,thentheEnterpriseplanof$299.99mightbeabetterchoice.
NoteInyourdeveloperpage,youwillalsohavetomakesurethatyourprovisioningprofileissetupcorrectly.ThisstepusedtobeoneofthetoughestthingstocompleteinbeinganiOSdeveloper,butXcodehasmadethisprocessmoreautomatedwitheachnewupdate.Ifyou’vebeentestingyourappinXcodewithanactualdevice,you’veobviouslyalreadydonethisstep.Ifnot,here’smoreinformationonsettingupyourprovisionprofile(s):https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ProvisioningDevelopment.html
TheportalthatwillbeyourbestfriendintheprocessofpublishingyourappisiTunesConnecthere:
https://itunesconnect.apple.com/
iTunesConnectiswhereyoucanseeyoursubmittedapps,trackvariousappanalytics,assignTestFlightbetatesters,andviewyourapp’srevenue.Wewon’tbeabletogiveafullrundownofeveryfeatureavailableiniTunesConnect;mainlywe’lllookatthestepsneededtopost,publish,andupdateyourgame.FeelfreetodiveintoallofthefeaturesandsettingsthatiTunesConnectcanprovideforyourappasthefeaturesgrowwitheverynewiOSupdate.
Submittingyourappinthetesting/betaphaseLet’sgetrightintothestepsneededtosubmityourapp.First,weshalltalkaboutthetesting/betaphaseofyourgame.
Hereisasummaryofthestepsneededforthetesting/betaphaseofyourgame:
1. CreateaniTunesConnectapprecord.2. Updatethebuildstring.3. Archiveandvalidateyourapp.4. UploadyourapptoiTunesConnect.5. BetatestyourgamewiththeTestFlightservice.6. Analyzecrashreportsandsolicitfeedbackfromtesters.
NoteSteps1-4arethesameforboththetestingandreleasephaseofyourgame.
CreatinganiTunesConnectapprecordThesearethestepsneededtoaddyourApptotheiTunesConnectapprecord:
1. Logintoyouraccountathttps://itunesconnect.apple.com/andgototheMyAppssection.
2. Nowclickonthe+iconlocatedonthetop-leftcornerofthepageandpickNewiOSAppfromthedropdown.
3. FillintheappropriatefieldsandclickonCreateifyoualreadyknowtheinformationforthisdata.Forthoseunsurewhattoplacehere,werevieweachofthesefieldsin
thefollowingscreenshot:
NotetheSKU,theVersion,andtheBundleIDfields.TheSKUmustbeuniqueandnotinusealreadyasApplewillusethisfortheidentificationofyourappinthestore.TheVersionandBundleIDfieldsmustmatchthebuildsettingsyouhavesetinyourgame’sXcodeproject.TheBundleIDfieldisadropdownthatatfirstmightonlyshowtheWildcardApp/BundleID.
TheWildcardIDisoneoftwotypesofBundleIDs,theotherbeingtheExplicitAppID.Here’salinktoApple’sdocumentation/FAQforwhichtypeofIDwouldbebestforyourgame:https://developer.apple.com/library/ios/qa/qa1713/_index.html.Inshort,ifyouaregoingtouseAppleservices,suchasNotificationsandGameCenterachievements,youwouldneedanExplicitAppID,ifnot,thentheWildcardIDisbest.
NoteIfyouwishtousetheExplicitBundleID,youwillhavetoregisteryourapp’sBundleIDintheAppleDeveloperportal.TheIDsthatareregisteredontheDeveloperportalwillpopulatethatdropdown.Here’sthelinktothatpageinthedevelopersite:https://developer.apple.com/account/ios/identifiers/bundle/bundleCreate.action.
TheBundleIDSuffixfieldisfoundinyourproject’sinfo.plistfile.It’sauniquestringthatiscreatedbyyourXcodeproject,alsoknownastheBundleSeedID.We’llshowyouwheretofindthisandotherbundle/build-basedinformationwhenwegoovertheUpdatingthebuildstringstepnext.
TheNamefieldiswhatyourgame’snamewillbeintheAppStore.Thisiswhatpeoplesearchingforandhopefullyvisitingyourgame’slandingpagewillsee.ThePrimary
Languagedropdowniswhatyourgame’sdefaultlanguagewillbeiftheappstorecan’tlocalizeyourgame’sinformationforthatterritory.
UpdatingthebuildstringThebuildstringrepresentsaniterationofyourgame’sbundle.It’satwo-period-separatedlistofpositiveintegers,asin1.2.3.Basically,thebuildstringisanotherlayerofversioningaddedforyourgame.WhenmakinganiOSapp,asinourcase,changingthisbuildinformationwillautomaticallybeseenbyiTunesConnectduringtheuploadstep.Ifwedon’tupdatethisfield,evenifwechangeourgame’scode,iTunesconnectwillstillthinkthatyouaretryingtouploadthesamebuildandwillrejectyourupload.
Here’swhereyoucanfindthisinformationinyourXcodeproject:
Thebundleidentifier,buildstring,versionnumber,andotherappidentification/globalsettingsarefoundontheGeneraltabintheInspectorwindowwhenweclickontheproject’smainfileintheNavigationpane.Wecanalsofindthisinformationrepresentedintheinfo.plistfile.MakesurethatthesefieldsmatchyouriTunesConnectrecord.
Nowlet’smoveontouploadingourgameinXcodetoiTunesConnect.
ArchiveandvalidateyourappThenextstepintheapppublishingprocessistoarchiveyourgame’sprojectbundle.Todothis,gotothetopdropdownmenusandthennavigatetoProject|Archive.ThearchiveselectionmightbeinaccessibleifyourtestdeviceisthesimulatorastotheiOSDevice.
Oncebuilt,yourarchivedappwillbeseenintheArchivesorganizerwithotherarchivesyouhavecreatedfromthisandotherapps.Thiswindowwillopenwhenyoubuildthearchive,butitcanbeaccessedatanytimebygoingtoWindow|Organizerinthetopmenu.
Thewindowcanbeseeninthefollowingscreenshot:
Next,wevalidateyourgametomakesurethatitfitstheminimumrequirementsforsubmission.Todothis,followthesesteps:
1. ClickontheValidatebuttonfoundontherightsideoftheprecedingArchivesorganizerwindow.
2. Apop-upwindowwillshowwhereyouchoosetheDevelopmentTeamthatwoulddotheprovisioningforthisapp.(ThisisassumingyourProvisioningprofilewassetupcorrectly.)ClickonChoosetomovetothenextstep.
3. Thiswillopenmoreofthepopupshowingasummaryofyourappbeforeperformingtheactualvalidation.Informationsuchasyourapp’sBundleIDalongwiththeBundleSeedIDmentionedearliercanbeseenhereaswell.
4. ClickonValidateandiftheinformationinyourappprojectiscorrectlymatchingwhatwesetuponiTunesConnect,thenyourappshouldbevalidatedandreadyforsubmissiontoiTunesConnectandeventuallytheAppStoreitself.
Ifyourappisnotvalidated,makesurethatallofyourinformationiniTunesConnectmatches,mostimportantlytheBundleID.
TipTheAppvalidationstepcouldprobablybeskippedjustbyclickingontheUploadToAppStore…button,butisagoodwaytotestearlyonifeverythingcheckoutwithourgame.
Moreonappvalidationcanbefoundhere:
https://developer.apple.com/library/mac/recipes/xcode_help-archives_organizer/articles/ValidatingYourApp.html
UploadyourapptoiTunesConnectThisstepshouldnowberathersimple.ClickontheblueUploadToAppStore…button,andyoushouldgetthesamepromptsseenfromtheappvalidationstage.You’llbeaskedtochoosetheDevelopmentTeam;showyourapp’sdetails,andyoucanchoosetouploadyourappbyclickingonSubmit.Ifyourgamevalidatedpreviously,thenitshoulduploadsmoothlytoiTunesConnect.Nowyourgameshouldbeonestepclosertobeingavailableforeitherbetatestingorpurchase/downloadontheAppStore.
BetatestyourgamewiththeTestFlightserviceEveryappshouldhaveatleastsomeformofbetatestingbeforereleasewithvideogamesusuallybeingthetypeofappsthatneeditmorethanothers,asgamestendtohavemorevariablesandchancesforcrashingthanyourrun-of-the-millmobileprogram.Also,theAppleservices,suchasGameCenterandIn-Apppurchases,can’tbetestedcorrectlywithoutmovingtothisphase.
Inthepast,theonlywaytotestiOSappsbeforereleasewasusingtheadhocdistributionmethod,validatingindividualdeviceswiththeirUDID,andgivingthetestersadownloadwithamanifestfilethatwouldallowtheapptoactuallyworkontheirdevice.ThisiswhereApplediffersgreatlyfromotherplatformssuchasAndroid.Appleisverycarefulinkeepingwhatdevelopersliketocallawalledgardenwiththeirapplicationdistribution.
Inthepast,thiswasabitofaheadacheandledtoaratherconvolutedadhocsetupascomparedwithAndroidresultinginappbugsnotbeingnoteduntilaftertherelease.TohelpkeeptheintegrityofApple’sappdistributionandgivedevelopersabetterwayofpretestingtheirappseasilyandtohavemorepeoplethantheoriginal100devicelimit,theTestFlightservicewascreated.TheTestFlighticonisseenhere:
TestFightisanappthatanybodycandownloadfromtheAppleAppstorefortheiriOSdevice.Foryou,thedeveloper,itcanbeagreattoolforearlydistributionofyourgames.TestFlighttestersaresegmentedintotwogroups:internaltestersandexternaltesters.Internaltestersaremadeupofyourownteammembers,andyoucanhaveamaximumof25internaltesters.
IniTunesConnect,youcansetrolesforyourteamintheUsersandRolesmainsection.TheserolesincludeAdmin,Technical,Marketing,andothers.Themembersthatareinthe
AdminandTechnicalcategoryarethosewhomyoucanassignasinternaltesters.MakingthoseusersinternaltestersisaseasyasturningontheInternalTestersswitchnexttotheirname.
TohavetheseuserstestyourgameinTestFlight,locateyourgameintheMyAppssectionofiTunesConnect.Ifyourgame’sbuildwassuccessfullyaddedtoiTunesConnectfromthestepsprovidedintheprevioussection,thenyoushouldseeitlistedinthePrereleasetab.
NoteWhenyouuploadyourapptoiTunesConnecttheycanbedividedintoversions,whicharethensubdividedintotheirownbuilds.Forexample,version1.0(1)isversion1.0,build1ofyourgame,whereas1.0(1.2)wouldbeversion1.0/build1.2ofyourgame.ChangingthebuildstringinyourprojectandthenuploadingthatnewbuildishowyoucandivideyourappforthispageiniTunesConnect.Wewilldiscussmoreonthiswhilecreatinganewupdatetoyourgame,butthisistheprocessforversioningtheprereleasebuilds.
Thenextstepistoclickonthebuildorversion,whichshouldopenthebuild’sownmetadatapage.Fillinthisinformationtobetterhelpyourtestersknowwhomtocontact
andwhattotest.Thisinformationiswhatyourtesterswillseewhentheydownloadyourgame’sbetaversion.
Toallowthisbuild/versionforTestFlighttesting,simplyswitchontheTestFlightBetaTestingswitchfoundontheupper-rightsideoftheversion’slistinginthePrereleasetab.
NowtohaveyourtesterstestthisbuildintheirTestFlightapp,simplyclickontheInternalTesterstabnexttotheBuildstaboninthegame’sprereleasepage,clickonthecheckmarknexttotheirnameandthenclickonInvite.
Theyshouldgetane-mailtoacceptthatinvite,andyouwillseewhichbuildtheyaretestingoncetheyinstalleditinTestFlight.
Externaltesterinvites
TogetexternaltestersforyourgamewithTestFlightisalsoarathersimpleprocesswithonecaveat;yourappneedstobesubmittedforbetareview.DoingsoissimplethoughallyouneedtodoistoclickontheSubmitForBetaAppReviewlinkattherightsideofyourapp’sbuild;againintheBuildstabofthePrereleasesection.
AswiththeactualAppStoresubmission,itmightinvolvewaitingbeforemovingtothenextstep.Thewaitisnotaslongasthefullappsubmissionandisaverygoodsignthatallwillgowellwhenyoudothepublicrelease.Unlikeinternaltesters,allofthemetadatamustbecompletedbutyoucanhaveupto1000testers!YoucanstarttoinvitetestersoncetheSubmitForBetaAppReviewlinkistappedandyourappiswaitingforbetatestreview.
NowgototheExternalTesterstabinthePrereleasepageandthenclickonthe+buttontoaddthemwiththeire-mailaddressand(optionally)theirfirstandlastname.ClickonNexttoaddthatpersontoyourinvitelist.Notethatyouonlyhave30daysforexternaltesterstoreviewthatbuildofyourgame.
Onceyourapppassesbetareview,yourexternaltesterscantestyourappjustlikeyourinternaltesterscan.
AnalyzingcrashreportsandfeedbackfromtestersNowthatyouhavepeopletestingyourgame,takenotesfromtheire-mailsonwhatissuestheremightbeinyourgameandthengobacktoyourgame’sprojecttodotheneededfixes.UpdatethebuildnumberinthebuildstringofyourXcodeprojectandreuploadthebuildtoeasilyallowyourtesterstokeepuptodatewitheachnewprereleaseupdate.
AppcrashreportscanbeviewedintheAppAnalyticsmainsectionofiTunesConnect,asseenintheimagefromPikiPop’sinformationasfollows.However,itseemsthatthesedetailedcrashanalyticsareforafterreleaseandnotduringprerelease.
MoreonTestFlightandevenavideoexplanationcanbefoundonApple’sofficialpageonthesubjectasfollows:
https://developer.apple.com/testflight/
SubmittingyourgameforreviewThisisthepointinyourgame’sdevelopmentyou’veworkedsohardfor,submittingittotheAppleAppStore.Thegoodnewsisthatmostoftheworkhasalreadybeendone!Tosubmityourgameforreview,atthispoint,allyouhavetodoisgotoyourapp’sVersionstabandclickontheSubmitForReviewtab.
YoucanseethisinthefollowingAdventureappexampleimage:
Youwillbeaskedafewquestionsbeforetheactualsubmission,suchasaboutExportCompliance,Contentrights,andAdvertisingIdentifier(IDFA)information.MoreinformationonIDFAcanbefoundhere:
https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html#//apple_ref/doc/uid/TP40011225-CH33-SW8
NowthewaitinggamebeginswithyourgameintheWaitingForReviewstatus.Again,thewaittimeforappapprovalwillvarybutisusuallymuchlessthanitusedtobeinthepast.Hopefully,allgoeswellandyouwillseeagreenmarkstatingyourgame’ssubmittedversionnumberwiththewordsReadyforSale;asfollows:
Congrats!YourgameshouldbenowlocatedintheAppStoreforeveryonewithaniOSdevicetodownloadandplay!MakesuretocheckoutthevariousanalyticstoolsiTunesConnectprovidestokeepapulseonyourgame.
UpdatingyourgameGamestodayarealmostneveraone-shotdeal.Theytendtobealivingapplicationthroughupdates,add-ons,andfixesevenafterrelease.UpdatestoyourappwillbeworkingtoanyfutureiOSupdates,addingnewgamecontent,oracombinationofboth.Todothis,simplyrepeatmuchoftheprocessesfromthebuildstringphasestartingwiththecreationofanewversionnumber.DoingsowillcreateanewsectioninyourBuildstabunderPrereleaseasseenwithPikiPop’sownpageasfollows:
YoucanuseTestFlightbetatesttotestthenewbuildwithinternalandexternaltestersjustasyoudidbefore.Tomakereadythenewestversionforrelease,clickontheNewVersionbuttonontheVersionsmaintabandsubmitthenewestversion’snumberinthepopup.
TheVersionstabwillnowbedividedintothecurrentversionandthenewversionviaeasy-to-navigatetabs.Likeintheoriginalrelease,youcanfillinvariousmetadataaswellasinformationfortheplayersforwhatthisnewupdatewillentail.Submittheversionforreviewandonceapproved,thenewestgameupdatewillbeintheappstorethatplayerswilleitherautomaticallydownloadorbenotifiedabout,basedontheirdevice’sAppStoreupdatesettings.
Thataboutdoesit!IfmoregameframeworksandtoolscomeoutinfutureversionsofiOS,youcanhavethemaspartofyourgamewithyourupdates.Makingagreatgameevenbetterisalwaystherightchoiceforeverygamedeveloper.
SummaryThroughoutthischapter,wesawwhatwasneededtofinallytakeyourgamefromanXcodeprojecttoaplayablegameforeveryonewithaniOSdevice.WelearnedaboutthestepsneededtosetupourXcodeprojectbeforesubmissiontoiTunesConnect.Then,wegotanintroductiontoTestFlight,whichisagreatserviceforustobetatestourgamesbeforerelease.Wesawthatthosestepsalreadypreparedusformostofwhatwasneededtosubmitourgamesforreview.Finally,wesawhoweasyitwastocreateappupdatesiniTunesConnect.
Younowhaveagameappthathopefullythousandsofplayerscanenjoy.Nomatterifthegamewasbigorsmall,beproudofthefactthatyoucreatedsomethingforplayerstoenjoy.Eventhesimplestofgamescanbeaworthyaccomplishment.We’veseenthroughoutthisbookthattheprocessofmakinganiOSgame,thougheasierthanevenafewyearsago,takessomeeffortanddiligencetogetright.Thisconcludesallofthetechnicalaspectsweshalldiscuss.Next,weconcludewithaquickdiscussiononthefutureofiOSandgamedevelopmentasawhole.
Chapter8.TheFutureofiOSGameDevelopmentFortheentiretyofthisbookwehavegoneoverthefeaturesfromiOS8andtherecentannouncementsofiOS9.So,whatofthefutureofiOSgamedevelopment?Obviously,nobodycantellwhatthefuturewillbring,butwecanguessafewpossibilitiesbasedontherecentevolutionoftheiOS/Xcodeplatform,programming,andhowgamedevelopmentasawholewillchange.
AgreaterfocusonfunctionalprogrammingAsofthispublication,theSwiftprogramminglanguageisonlyayearoldbutitrepresentsarecentparadigmshiftinmuchofprogramming.Object-orientedprogramminganddesignstillholdstrue,butashifttofunctionalprogrammingiswherelanguagessuchasScalaandSwiftplacetheirfocus.Functionalprogramming,insummary,isafocusonfunctionsbeingpuremathematicalcalculationsofobjectswithanavoidanceofthestatechangesandmutabledatathatwe’veseenwithpastlanguages,suchasC++/JavaandevenApple’sownObjective-C.Insteadofdealingwithsubroutines,afunctiononlyworksontheparametersit’sgiven.Swiftdoesthiswellwithitsclosures,whichwe’veseenafewtimesinthisbook,andareoftenusedtocompactmuchofthelogicingameprogrammingforiOS.
SinceWWDC14,ApplehastolddevelopersthatSwiftisthesuccessortobothCandObjective-C.It’sacompletelyrebuiltlanguagefromthegroundupwithspeedandefficiencyasitsfocus,somethinggamedevelopersmustalwaystakeadvantageofandwhySwiftisnowthelanguageofchoicegoingforwardforiOSgamedevelopment.Objective-Cisnotgoingaway,andSwiftstillhassomegrowinguptodobeforeitcaneventakeoverallofwhatObjective-CcandoforiOSdevelopment.Swift’s2.0updatefromiOS9addedmoreforerrorcatchinganddebuggingwithkeywords,suchasthrow,catch,andguard.Thankfully,wedon’thavetoabandonanypastObjective-CprojectssincetheabilitytousebothSwiftandObjective-CinthesameprojectisverysimplethroughtheuseofanObjective-C/SwiftBridgingfile.
AswemoveontofutureversionsofiOS,expectmoredebuggingcontrolsandimprovementstoSwift’sabilitytocompactdetailedlogicintoafewlines.SomedatasortingfunctionalitiesofSwiftarefasterthanObjective-C,andintheworldofgamedevelopment,anygainsinframerateisalwaysagoodthing.DespitetheadvancesinCPU/GPUpowerandtheever-growingeaseofgamedevelopmentframeworks,suchasSpriteKitandSceneKit,thedesignaspectofgamedesigncanbeadouble-edgedswordifweforgettheengineeringaspectofthiscraft.Someofthetoolsgiventousintherecentgamedevelopmentscenecanmakealmostanybodyagamedeveloper.Thisisagoodthing.Wewantpeoplefromallwalksoflifetobegamedevelopers,butwemustnotletsomeoftherecentandfuturetoolsthatallowgamesoniOSorotherplatformsmakinguslazyandforgetthatthereisandalwayswillbeaprogramming/engineeringsidetothisindustry.TheframeworksandvisualtoolsofiOSandothergameenginesshouldalwaysbetreatedastoolsandneverasacrutch.Thebestgamesofthepast,present,andinthefuturewillbegamesthattapintoeveryaspectofgamedevelopment,particularly,thehardestaspectsofgamedevelopment,suchasthegraphicspipeline.That’swhyAppletappedintothedetailsoftheGPUwiththeMetalAPI.
TheAppleWatchAsofiOS9,theAppleWatch’splatformalreadygotanupgradewithwatchOS2.TheAppleWatchisgenerally,atthemoment,notavideogameplatform.Withouttakingthesmallscreensizeintoplay,appsmadefortheAppleWatchdon’thavemuchinthewayofmakinggame-likegraphicsupdates.Inthefuture,thismightchange.Atthemoment,appsmadeinwatchOSarelikechildappsofmainiOSapps.Eventually,wecanmakewatchOSappsseparatelywithouthavingthemattachedtoaparentiOSproject.However,somedevelopershavemadesimpletext-basedgamesfortheApplewatch.It’spossiblethatinthefuture,wecouldmakemoreaction-orientedgamesfortheAppleWatch.
Currently,it’sverymuchpossibletomakeagamethatusesthewatchforaccessoriesdata,suchasinventory,maps,andmorewithalittlebitofingenuity.OnefeatureoftheAppleWatchthatwecoulddesigngamesorgamecontrolswithisForceTouch.ForceTouchsenseshowfirmthepressgestureis.Thisisn’tsomethingnewtogamedesignasawholebutnewtoiOSwiththenextlineofiPhonesandiPadsmostlikelyhavingthisfeatureaswell.Gettingthestrengthofaplayer’stouchandtapscouldallowsomeintuitivegameplaymechanicsforthenextlineofmobilegames.
FormoreonthewatchOStopossiblyinspiresomegamedevelopmentideasforthedevice,checkoutthewatchOS2previewpageathttps://www.apple.com/watchos-2-preview/.
Component-basedstructuringWesawinthepreviouschaptersthatiOShasimpartedmuchofthecomponent-basedstructuringparadigmtotackletheuniquesoftware/programmingrequirementsthatcomefromgamedevelopment.Insteadofbuildingatallparent-childstructurethatweseefromanobject-orienteddesign,itbuildsastructurethatdoesbestbygrowingit’sstructureinwidth.Classes,suchasGKEntity,GKComponent,andotheraspectsofGameplayKit,arewhatletustakeadvantageofthesefeatures.Thistypeofstructuringisn’talltoonewingamedevelopment.Component-basedstructuringhasbeenusedbymultiplatformgameengines,suchasUnityandUnrealEngine,andcontinuestobethewaygamedeveloperslikeusmakeandreusepartsofourgames.ExpectthefutureupdatestoXcodeandiOStoutilizethesefeaturesevenmore.Inthenearfuture,wewillprobablyseeXcodelookandactevenmorelikethesegameenginesbutwiththebenefitofbeingspecificallymadefortheiOSdevices,allowingevendeeper,customintegration.DoingallofthisdirectlyinXcodeandwithiOSframeworksallowsinstantaccesstoApplefeatureswithouttheuseofpaidassettoolsorwaitingforpluginupdates.DevelopersforthenextgenerationofiOSgameswillbeabletotakeAIactions,characterabilities,HUDanimations,andotherfeaturesmadeinonegameandreusethemalmostinstantlyforacompletelydifferentgame.Component-basedstructuringmakesitwherethedevelopercanbuildalibraryofreusablefeaturesbyplacingthedesignofourgamesaheadoftypicaldevelopmenthurdles.
TheriseofVRThishasbeenatopicthatApplehasbeenratherquietaboutwhereotherplatformshavebeenrathervocal.EventhemanufacturersofApple’sA7/A8chips,Samsung,havejoinedtheVRdevelopmentenvironmentwiththeirGearVRdeviceandtheirpartnershipwithOculusontheproject.GooglehascreatedthesimpleCardboardsetup,akintotheGearVR,whichsimplyletstheuserplacetheirsmartphoneintothedeviceorboxtoexperienceVRexperiencesandgames.Mostfamously,thereistheOculusRift,whichwillhaveitsconsumermodelavailableinthefirstquarterof2016,andwilllikelybethefrontrunnerinthisnewergameenvironment.
Virtualrealityisatopicthathascomeupanumberoftimesinthepastfewdecades.Ithascomeandgone,butforthetimebeing,itseemstobegettingitsoverduefootholdintechnology.TheUnitygameengine,forexample,justrecentlyallowedfornativesupportofVR.Thethoughtprocessinmakingthesegamesisabitdifferentandhasyettobefullyfleshedout.ItispossiblethatsoonApplewillthrowtheirhatintothisarena.Ifyouhaven’talready,learninghowtomakefungamesintheVRspacemightbeaworthyactofforesight.
SummaryWehopethatthediscussionsandtutorialsseenthroughoutthisbookhelpedyoueitherlearntheplatformforthefirsttimeorenrichedwhatyoumighthavealreadyknownaboutiOS.WehopethatyoutakethisknowledgeandmakesomeamazinggamesfortheiOSfamilyofdevicesandcontinuetolearnmoreabouttheplatformandgamedevelopmentasawhole.
Intheend,alwaysrememberthatgamedesignandprogrammingisacombinationofcomputerscience,engineering,art,andlotsofhardwork.It’sacomplexcreativefieldthatisacombinationofeveryothercreativeprofessionfrommusictomoviemaking,to2Danimation,3Dsculpting,andmore.Asthecasewithothercreativefields,developersshouldneverfeeldoneandalwaysbehumbleinwhattheyknowandacknowledgethatthere’salwaysmoretolearninthisever-growingfield.Likeavideogame,beupforthechallenge.Realizewhenyoulookbackthatyouessentiallyleveledupfromwhatyouusedtoknowandtherearealwaysmoreabilitiestogain.
IndexA
A7Processor/WhyisMetalfasterthanOpenGLES?AdvertisingIdentifier(IDFA)/Submittingyourgameforreviewaffectedbygravitytoggle/Particlesystemsagents
about/Statemachines,Agents,goals,andbehaviorsAPI
statevalidation/WhyisMetalfasterthanOpenGLES?shadercompilation/WhyisMetalfasterthanOpenGLES?work,sendingtoGPU/WhyisMetalfasterthanOpenGLES?
AppleMetalAPIabout/TheAppleMetalAPIandthegraphicspipelineCPUframeworklevel/CPU/GPUframeworklevelsGPUframeworklevel/CPU/GPUframeworklevelsgraphicspipelineoverview/Graphicspipelineoverview
AppleWatchabout/TheAppleWatch
appslicingabout/Assets,sprites,andiconsURL/Assets,sprites,andicons
appsubmissionprocessabout/Theeverchangingprocessofappsubmissionprerequisites/Beforesubmittingyourappapps,preparingforiTunesConnect/PreparingourappsforiTunesConnecttesting/betaphase/Submittingyourappinthetesting/betaphase
appthinningabout/Assets,sprites,andiconsURL/Assets,sprites,andicons
arraysabout/Arrays
artificialintelligence(AI)about/Agents,goals,andbehaviors
assemblylanguage/CPU/GPUframeworklevels
Bblocks
URL/ThegameloopBoolean
about/Booleanbridgingfile
URL/UsingGKEntityandGKComponentobjectsinourgamesBufferobjects/Graphicspipelineoverview
Ccharacters
about/Charactersandstringsclasses
about/Classesclosures
URL/ThegameloopCocos2Dframework/CPU/GPUframeworklevelscollectiondata
editing/Editing/accessingcollectiondataaccessing/Editing/accessingcollectiondata
collectiontypesiteratingthrough/Iteratingthroughcollectiontypes
commentingabout/CommentinginSwift
component-basedstructuringabout/Component-basedstructuring
componentsabout/EntitiesandcomponentsURL/UsingGKEntityandGKComponentobjectsinourgames
constantsabout/Constants,Moreaboutconstants…
controlflowabout/ControlflowinSwiftif-statement/Ifstatementsfor-loops/Forloopsdo-whileloop/Do-whileloopsswitchstatements/Switchstatements
D2Darrays
about/2Darrays/matricesDemoBots
about/DemoBotsURL/DemoBots
designmethodologyURL/Entitiesandcomponents
dictionariesabout/Dictionaries
do-whileloopsabout/Do-whileloops
doublesabout/Floatsanddoubles
Eemoji
URL/Creatingourgamelogicentities
about/EntitiesandcomponentsURL/UsingGKEntityandGKComponentobjectsinourgames
Ffiledecodingclass
URL/SpriteKitfloats
about/Floatsanddoublesfor-loops
about/ForloopsForceTouch
about/TheAppleWatchFoxdemo
about/FoxdemoURL/Foxdemo
framespersecond(fps)/SceneKitprojectflowandstructurefunctionalprogramming
about/Agreaterfocusonfunctionalprogrammingfunctions
about/Functionsfuzzylogic
about/Rulesystems
Ggame
submitting,forreview/Submittingyourgameforreviewupdating/Updatingyourgame
GameBoardabout/GameBoard
gamelogiccreating/CreatingourgamelogicGameBoard/GameBoardGameScene.swift/PuttingitalltogetherinGameScene.swift
gameloopabout/ThegameloopURL/Thegameloop
GameplayKitoverview/Agents,goals,andbehaviorsguide,URL/Agents,goals,andbehaviorsrulesystems/Rulesystemsrulesystems,URL/Rulesystems
GameplayKitframeworkabout/Boolean
gamestateabout/PuttingitalltogetherinGameScene.swift
Genericsabout/2Darrays/matrices
GKAgentparametersURL/Agents,goals,andbehaviors
GKComponentobjectsusing,ingames/UsingGKEntityandGKComponentobjectsinourgames
GKEntityobjectsusing,ingames/UsingGKEntityandGKComponentobjectsinourgames
GKObstacleclassURL/Pathfinding
GKStateURL/Statemachines
GKStateMachineURL/Statemachines
GLKit/CPU/GPUframeworklevelsgraphicspipelineoverview
about/Graphicspipelineoverviewvertexdata/Graphicspipelineoverviewrendering/pixeldata/Graphicspipelineoverviewexample/GraphicspipelineoverviewBufferobjects/Graphicspipelineoverview
primitiveprocessing/GraphicspipelineoverviewGraphicsProcessingUnit(GPU)/CPU/GPUframeworklevels
HHeads-UpDisplay(HUD)
about/ConstantsHelloWorldprogram
about/HelloWorld!
Iif-statement
about/Ifstatementsifandelsestatements
about/Moreaboutconstants…imageassets
URL/Creatingourgamelogicimmutablecollections
about/Mutable/immutablecollectionsinheritance-basedstructuring
issues/Theissuewithinheritance-basedstructuringandgamedesignintegers
about/IntegersandunsignedintegersiOS9/Xcode7
SceneKit,features/SceneKitfeaturesintroducediniOS9/Xcode7iOSapplifecycle
about/AniOSapp’slifecyclemain()function/Themain()functionUIApplicationclass/TheUIApplicationclass/objectAppDelegateclass/TheAppDelegateclassviewcontrollers/ViewcontrollersViewControllertypes/Viewcontrollertypes
iOSgamedevelopmentfunctionalprogramming/AgreaterfocusonfunctionalprogrammingAppleWatch/TheAppleWatchcomponent-basedstructuring/Component-basedstructuringVRdevelopmentenvironment/TheriseofVR
iOSgamedevelopmentenginesabout/AbriefhistoryofiOSgamedevelopmentengines
iPhone5s/WhyisMetalfasterthanOpenGLES?iTunesConnect
URL/CreatinganiTunesConnectapprecord
Mmachinecode/CPU/GPUframeworklevelsmatrices
about/2Darrays/matricesMetalAPI
versusOpenGLES/WhyisMetalfasterthanOpenGLES?object/codestructure/ThebasicMetalobject/codestructure
MinMaxAIabout/MinMaxAIURL/MinMaxAI
Model-View-Controller(MVC)about/Model-View-Controller
mutablecollectionabout/Mutable/immutablecollections
Nnavigationgraph
about/Pathfinding
Oobject-orientedprogramming(OOP)
about/ObjectsinSwiftObjective-C
versusSwiftcode/Objective-CandSwiftcomparison,Swiftabout/Objective-C,UsingGKEntityandGKComponentobjectsinourgames
objectsabout/ObjectsinSwifttype-safe/Typesafetyandtypeinferencetypeinference/Typesafetyandtypeinference
OpenGLAPI/CPU/GPUframeworklevelsOpenGLES/CPU/GPUframeworklevelsoptionals
about/Optionalsunwrapping/Unwrappingoptionalsoptionalbinding/Optionalbindingandchainingoptionalchaining/Optionalbindingandchaining
Pparticles
placing,inpioscene/PlacingparticlesintoourpiosceneSpriteKitparticles/SpriteKitSceneKitparticles/SceneKit
particleseffectsabout/Particlesystemscreating,onSceneKit/Particlesystems
Pathfindingabout/PathfindingURL/Pathfinding
PrimitiveAssembly/Typesofshadersprimitiveprocessing/Graphicspipelineoverview
Rrandomsources
about/Randomsourcesreal-timestrategy(RTS)game/HandlinguserinputinSceneKitRight-HandedCoordinateSystem/SceneKitprojectflowandstructureRollercosterTycoon™/CPU/GPUframeworklevels
Sscenegraph
about/Visuallycomposedgamescenescgsobjects,adding/Visuallycomposedgamescenescgs
SceneKitbasics/SceneKitbasicsandworkingwithnodesnodes,workingwith/SceneKitbasicsandworkingwithnodesandSpriteKit,interactivity/SpriteKit/SceneKitinteractivitycoordinatediagram,URL/SceneKitprojectflowandstructureframework,URL/Visuallycomposedgamescenescgs
SceneKitfeatures,iniOS9/Xcode7about/SceneKitfeaturesintroducediniOS9/Xcode7audionodesand3Dsound/Audionodesand3Dsoundambienceandmusic/AmbienceandmusicSpriteKitscenetransitions/SpriteKitscenetransitionsinSceneKit
SceneKitparticlesabout/SceneKit
SceneKitphysicsabout/IntroducingSceneKitandSpriteKitphysics
SceneKitsceneabout/OurfirstSceneKitscene–theXcodetemplateproject,flow/SceneKitprojectflowandstructureproject,structure/SceneKitprojectflowandstructuredebugging,options/SceneKitDebuggingOptionsuserinput,handling/HandlinguserinputinSceneKit
SCNActionURL/SceneKitprojectflowandstructure
SCNBoundingVolumeclassURL/IntroducingSceneKitandSpriteKitphysics
SCNLightsURL/SceneKitprojectflowandstructure
seguesabout/SeguesShow/SeguesShowDetail/SeguesPresentmodally/SeguesPopover/SeguesCustom/Seguescreating/Seguescustomsegueclasses,creating/Segues
setsabout/Sets
shaders
about/Whatareshaders?types/TypesofshadersVertexshaders/TypesofshadersFragmentshaders/TypesofshadersPixelshaders/Typesofshaderscodesample/Typesofshaders
SKActionURL/SceneKitprojectflowandstructure
SKConstraints/ThegameloopSKNode
URL/AnoverviewoftheSpriteKitstructureandobjectsSKScenetransitions
withSKSfiles/SKScenetransitionswithSKSfilesSKTransition
URL/AnSKTransitionexample,SpriteKitscenetransitionsinSceneKitSpriteKit
gameloop/Thegameloopkeynote,URL/DemoBots
SpriteKitgamecreating/CreatingourSpriteKitgamestructure/AnoverviewoftheSpriteKitstructureandobjectsobjects/AnoverviewoftheSpriteKitstructureandobjectsscenetransitions/Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfilescode,choice/Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfilesstoryboards/Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfilesSKSfiles/Scenetransitionsandthechoiceofcode,storyboards,and/orSKSfilesSKTransitionexample/AnSKTransitionexampleSKScene/storyboardexample/ASKScene/storyboardexampleSKScenetransitions,withSKSfiles/SKScenetransitionswithSKSfilesassets/Assets,sprites,andiconssprites/Assets,sprites,andiconsicons/Assets,sprites,andiconsspriteatlases/Spriteatlasesandanimatingspritessprites,animating/SpriteatlasesandanimatingspritesandSceneKit,interactivity/SpriteKit/SceneKitinteractivity
SpriteKitparticlesabout/SpriteKit
SpriteKitphysicsabout/IntroducingSceneKitandSpriteKitphysics
spritesheet/Spriteatlasesandanimatingsprites
SpriteSheets/WhyisMetalfasterthanOpenGLES?statemachine
about/PuttingitalltogetherinGameScene.swiftstatemachines
about/Statemachinesstoryboards
about/Storyboardsandseguesusing/Storyboardsandseguesversuscoding/Storyboardsversuscoding
StringInterpolationabout/StringInterpolationstrings,mutating/Mutatingstringsstringindices/Stringindices
stringsabout/Charactersandstrings
Swiftcommenting/CommentinginSwift
Swiftlanguagevariables/Variables,constants,andprimitivedatatypesconstants/Constants
SwiftSweeperabout/Tilegame–SwiftSweeper,WhatisSwiftSweeper?URL/WhatisSwiftSweeper?,PuttingitalltogetherinGameScene.swift
switchstatementsabout/Switchstatements
Ttesting/betaphase,appsubmissionprocess
about/Submittingyourappinthetesting/betaphaseiTunesConnectapprecord,creating/CreatinganiTunesConnectapprecordbuildstring,updating/Updatingthebuildstringapp,archiving/Archiveandvalidateyourappapp,validating/Archiveandvalidateyourappapp,uploadingtoiTunesConnect/UploadyourapptoiTunesConnectapp,testingwithTestFlightservice/BetatestyourgamewiththeTestFlightserviceexternaltesterinvites/Externaltesterinvitescrashreports,analyzing/Analyzingcrashreportsandfeedbackfromtestersfeedback,analyzing/Analyzingcrashreportsandfeedbackfromtesters
textureatlas/Spriteatlasesandanimatingspritestuples
about/Tuples
UUITapGestureRecognizerclass
URL/HandlinguserinputinSceneKitunsignedintegers
about/Integersandunsignedintegers
Vvariables
about/VariablesVertexBufferObjects/GraphicspipelineoverviewVertexProcessingUnit(VPU)/GraphicspipelineoverviewVRdevelopmentenvironment
about/TheriseofVR
WwatchOS2previewpage
URL/TheAppleWatchWWDC15
URL/DemoBots
XXCodetemplate
about/OurfirstSceneKitscene–theXcodetemplate