Chapter 23: Game Center Achievements... 4!

Size: px
Start display at page:

Download "Chapter 23: Game Center Achievements... 4!"

Transcription

1

2 Bonus Chapters Chapter 23: Game Center Achievements... 4! Introduction to Game Center... 4! Getting started... 7! Authenticating local players... 13! Adding achievements... 19! Initializing the built-in user interface... 25! Challenges... 32! Chapter 24: Game Center Leaderboards... 33! Supporting leaderboards... 33! Leaderboard sets... 43! Security in Game Center... 47! Challenges... 48! Chapter 25: Game Center Multiplayer... 50! Choosing a multiplayer strategy... 51! Player matchmaking... 52! Testing your matchmaking code... 58! Network coding strategy... 62! Multiplayer game scene... 65! Adding networking code... 67! Displaying player aliases... 84! Challenges... 89! Chapter 26: Performance: Texture Atlases... 94! Advantage 1: Use less memory... 95!

3 Bonus Chapters Advantage 2: Run faster... 97! Using texture atlases ! Pixel formats ! Manual texture atlas generation ! Texture atlas pitfalls ! Challenge: Preloading power ! Chapter 27: Performance: Tips and Tricks ! Getting started ! Introducing Instruments ! Touring the code ! Reducing sprites ! Reducing particle systems ! Limiting profiler results to your code ! Object pooling ! Avoid enumerating by name ! Other performance tips and tricks ! Chapter 28: Making Art for Programmers ! Choose your path: Hire or DIY? ! How to find and hire an artist ! Paying your artist ! Getting started ! Start with a sketch ! Getting the sketch into Illustrator ! Tracing the sketch with vector lines ! Custom stroke widths ! Coloring your artwork ! A bit about shadow and light ! Exporting PNG files ! Challenges ! 3

4 Chapter 23: Game Center Achievements By Kauserali Hafizji So far, all the minigames you ve created for this book have been single-player only. But players also want to be able to enjoy your game with friends. It s more fun to play with others, whether on the same or opposite sides, and it s also fun to boast about your achievements and high scores. Luckily in ios, adding social features like these is easy, thanks to Apple s Game Center app and APIs. Over the next three chapters, you re going to learn all about Game Center and see how to put it to work in Circuit Racer. Specifically: In this chapter, you ll learn what Game Center is and take a quick tour of its features. Along the way, you ll learn how to authenticate the local player, enable achievements and add support for them to your game. In the next two chapters, you ll continue your exploration of Game Center by adding leaderboards and multiplayer support to Circuit Racer. If you re ready to learn how to add a new level of interactivity to your game, pull your racing gloves back on and rev your coding engine! Note: This chapter begins where Chapter 21 left off. If you were unable to complete the chapter or skipped ahead from a previous chapter, don t worry you can simply open CircuitRacer-Starter from this chapter s resources and you ll be ready to go. Introduction to Game Center If you re an ios gamer, you ve no doubt come across Game Center already. On the user side, Game Center consists of an app that acts as a central hub to browse your friends, leaderboards, achievements and challenges:

5 Chapter 23: Game Center Achievements On the developer side, Game Center provides a set of APIs that you can integrate into your game to support achievements, leaderboards, challenges, turn-based gaming, real-time multiplayer gaming and more. Why should you add Game Center support to your game? Well, not only can it significantly up your game s fun quotient it can also increase sales. If your game supports Game Center, you can benefit from a number of extra ways players can discover your app: Listed on profile. Your app will be listed on each player s game list, leaderboards and achievements. When a friend checks out a player s profile, he or she might see your game! Friend challenges and requests. A player can challenge her friends to beat her high scores in your game, or if your game has multiplayer support, invite them to 5

6 Chapter 23: Game Center Achievements play. In either of these cases, the new player will be presented with the option to download or buy your app, providing a viral growth mechanism. More ratings. Game Center makes it easier for players to rate your game or like it on Facebook by presenting those options right on the selection screen. Anything that encourages ratings is a good thing, as games with more and higher ratings are more likely to get downloads. App Recommendations. Games that support Game Center can be autorecommended to players within the Game Center app based on other games they like or what their friends play. Game Center games are also more likely to be featured! 6

7 Chapter 23: Game Center Achievements I m sure you don t need much more persuasion that adding Game Center support is a great idea, and Apple makes it nice and easy with its Game Center APIs. As you explore Game Center over the next three chapters, you ll focus on its three most popular features: achievements, leaderboards and real-time multiplayer gaming, as well as how to integrate them into a Sprite Kit game. Note: To learn more about turn-based gaming and challenges in Game Center, check out the Game Center chapters in ios 5 and ios 6 by Tutorials. Getting started To do anything with Game Center, you first have to configure your app to use Game Center. This involves three steps: 1. Turn on your app s Game Center capability. 2. Register your app on itunes Connect. 3. Enable Game Center features such as achievements and leaderboards. Let s go through these steps one at a time. If you ve used Game Center before, this may be routine for you by now. In that case, feel free to perform these steps yourself and skip to the Authenticating local players section. Turning on Game Center Open Circuit Racer in Xcode if you haven t already and select the CircuitRacer target. In the General tab, you ll see the Bundle Identifier property in the Identity section. 7

8 Chapter 23: Game Center Achievements Change the bundle identifier to a unique name. Each bundle identifier must be globally unique. Since we ve already used com.razeware.circuitracer, com.razeware.circuitracer-swift, and a selection of other variations, you ll need to think of another unique ID. Set the Team dropdown to your developer profile. Next, select the Capabilities tab and in the Game Center section, click the On button. This feature, introduced in Xcode 5, makes it very easy to enable Game Center in your apps. And that s it with the click of a button, you re set up. Thanks, Xcode! Registering your app on itunes Connect Before you can use Game Center, you must register your app on Apple s itunes Connect site and enter some metadata. Log in to itunes Connect, go to My Apps and click the + button in the top-left corner to add a new app. If you have both Mac and ios developer accounts, you will be presented with a choice of app type choose ios App. On the first screen, you need to provide a unique value in the App Name field, because it s the name that will appear in the App Store if you submit your app and it s accepted. I used CircuitRacer-Swift, so you won t be able to use that name. Because this is only a test app, you could do something like Your Name Circuit Racer, using your actual name in place of the words Your Name. Select English as the Primary Language and 1.0 as the Version number (The version number you enter here should match with the one used in Xcode). Next, enter 100 as the SKU number (this can be any number/word, so if you want, you can set it to something else) and select the Bundle ID you created in the previous step. Note: SKU is short for stock-keeping unit, which is a unit that you personally make up to keep track of your applications. 8

9 Chapter 23: Game Center Achievements When you re done entering all the values, click Create. On the next screen enter all the required details. Since you just need to get through these steps for the purposes of this chapter, fill in only the necessary values and be as brief as you want to be. To get started with Game Center even though you don t have to upload screenshots of the game I ve included them anyway in the itunes folder present in this chapter s resources. Upload the screenshots for each size class by dragging and dropping them in the relevant space. Along with the screenshots you ll also find a large app icon, which you can use as the icon for the app store. When you re done, click Save. If everything went well, you ll be presented with the following screen: Hooray! You ve registered your app with itunes Connect and there are only a few more steps remaining to activate Game Center. 9

10 Chapter 23: Game Center Achievements Enabling Game Center achievements Still in itunes Connect, select the blue Game Center button. itunes will present you with the option to register this app as an individual app or as one that shares achievements and leaderboards with other apps you ve created. Choose Enable for Single Game, and itunes will present you with the main Game Center management screen. 10

11 Chapter 23: Game Center Achievements This chapter of the book covers Game Center achievements, so let s take a look at the achievements you ll be implementing. Circuit Racer will have four achievements that players can unlock: 1. Destruction Hero: A player will earn this achievement whenever he hits the crates more than 20 times during a single race. You ll hide the achievement initially, meaning the player won t see this achievement in Game Center until he s earned it. It will be achievable multiple times. 2. Amateur Racer: A player will earn this when he successfully completes the first level that is, the easy level. 3. Intermediate Racer: A player will earn this when he completes the second level that is, the medium difficulty level. 4. Professional Racer: A player will earn this when he completes the third level that is, the hard level. Let s create the first achievement. Click the Add Achievement button. itunes Connect will present you with a screen to enter some details about the achievement you want to create. In the Achievement subsection, enter the following: Enter DestructionHero for the Achievement Reference Name. This is an internal name that you can use to search for the achievement on itunes Connect. Enter [Your app Bundle ID].destructionhero for the Achievement ID. This is a string that uniquely identifies each achievement. It s generally good practice to use the Bundle ID as a prefix while setting up the achievement ID, as it ensures the ID will be globally unique. Enter 100 for the Point Value. This refers to the number of points the achievement is worth. Each achievement can have a maximum of 100 points, and all achievements combined can be worth a maximum of 1000 points. Select Yes for Hidden. This property will keep the achievement hidden until the player achieves it for the first time. Select Yes for Achievable More Than Once. As the name suggests, this property will ensure that the player can earn this achievement more than once. Moreover, when you set this property to Yes, players will be able to receive achievement challenges from their friends even for achievements they ve already earned. 11

12 Chapter 23: Game Center Achievements Next, select Add Language in the Achievement Localization section and enter the following details: Enter English for the Language. Right now, you re only adding a single localization (English), but if you re localizing your app, you can repeat this process for other languages and select the appropriate language here. Enter Destruction Hero for the Title. This is the achievement s title as it will appear in Game Center. Enter Bang into an obstacle more than 20 times for the Pre-earned Description. This is the description you show before the player has earned the achievement. This particular achievement is actually hidden from the player until he achieves it, meaning this description will never be seen. However, it s good practice to always enter a description, in case you decide to make the achievement visible later. Enter Banged into an obstacle more than 20 times for the Earned Description. This is the description you show after the player has earned the achievement. Enter achievement-destruction.png for the Image. You need to add an image for each language, which in this case is just one. The reason the image is in the language-specific section is because there is a chance your image might include text (in this case it doesn t so you could reuse it for multiple languages). You can find this image file in the itunes folder in the resources for this chapter. Click Save to save the localization. Then, click Save on the achievement screen, and this will take you back to the main Game Center screen. There, you ll see your new achievement in the Achievements section Destruction Hero! 12

13 Chapter 23: Game Center Achievements Now that you know what you re doing, create the other three achievements yourself by following the same process. Here s a small table showing the values you should use, but be sure to replace [Your app Bundle ID] with your own Bundle ID: Achievement reference name Achievement ID AmateurRacer [Your app Bundle ID].amateurracer IntermediateRacer [Your app Bundle ID].intermediateracer ProfessionalRacer [Your app Bundle ID].professionalracer For each achievement, set Point Value to 100, Hidden to No and Achievable More Than Once to Yes. Also add English localizations for all three. You can enter whatever you like for the title and descriptions. The itunes directory contains an appropriate icon for each of the three achievements. Once you ve added all the achievements, click Done on the main Game Center screen. Sweet! You have everything set up, so it s time to crack your knuckles and write some code. Authenticating local players You re going to write some code to authenticate the player. Without an authenticated player, you can t use any features provided by Game Center. In Xcode, right-click on the CircuitRacer project file and select New Group. Name the group GameKit. Next, right-click on the newly created group and select New File, choose the Cocoa Class template and click Next. Name the class GameKitHelper and make it a subclass of NSObject. Be sure the Language is set to Swift, and click Next again. Save the new class file. 13

14 Chapter 23: Game Center Achievements This GameKitHelper class is where you re going to put all your Game Center code. An added benefit to you is that you ll be able to use this same class in your own games without having to rewrite anything. I hope you agree that s pretty awesome! Open GameKitHelper.swift and replace the contents of the file with the following: import GameKit import Foundation let singleton = GameKitHelper() class GameKitHelper: NSObject { var authenticationviewcontroller: UIViewController? var lasterror: NSError? var gamecenterenabled: Bool class var sharedinstance: GameKitHelper { return singleton GameKit is the name of the framework that includes all of the classes you need to access Game Center, so you start by importing that and Foundation. The GameKitHelper class will behave as a singleton so that a shared instance of GameKitHelper can be accessed from anywhere in the app. The sharedinstance class variable returns this singleton instance of GameKitHelper. Within the class, you ve declared two optional properties. You ll use the first to keep track of a view controller (more on this in a bit) and the second to keep track of the last error that occurred while interacting with the Game Center APIs. The gamecenterenabled property stores a Bool that, as the name suggests, tells you whether or not Game Center is enabled. Xcode will already be showing you a compile error. The class has no initializers. To fix this, add an init() method as follows: override init() { gamecenterenabled = true super.init() By default, you re going to assume that Game Center is enabled on the user s device. 14

15 Chapter 23: Game Center Achievements Authentication callbacks Since Game Center authentication happens asynchronously, the callback can be triggered at any point, even when the player is racing around your Circuit Racer tracks! To handle this, you re going to use NSNotificationCenter. Therefore, the first thing you need to define is a static name for the notification. Add this declaration to the top of GameKitHelper.swift: let PresentAuthenticationViewController = "PresentAuthenticationViewController" Now add the authentication code to the class by adding the following method: func authenticatelocalplayer () { //1 let localplayer = GKLocalPlayer.localPlayer() localplayer.authenticatehandler = {(viewcontroller, error) in //2 self.lasterror = error if viewcontroller!= nil { //3 self.authenticationviewcontroller = viewcontroller NSNotificationCenter.defaultCenter(). postnotificationname(presentauthenticationviewcontroller, object: self) else if localplayer.authenticated { //4 self.gamecenterenabled = true else { //5 self.gamecenterenabled = false This method will authenticate the player with Game Center. It s a lot of code, so let s go through it step by step: 1. First, you set the authenticatehandler of the GKLocalPlayer object. The Game Kit framework may call this handler multiple times. 2. Here, you store any error the callback may have received in the optional property you defined earlier. 15

16 Chapter 23: Game Center Achievements 3. If the player has not logged into Game Center either using the Game Center app or while playing another game, the Game Kit framework will pass a view controller to the authenticatehandler closure. It s your duty as the game s developer to present this view controller to the user when you think it s appropriate. Ideally, you should do this as soon as possible. You will store this view controller in the authenticationviewcontroller variable. The code also raises your notification. 4. If the player has already logged into Game Center, the authenticated property of the GKLocalPlayer object is true. When this occurs, you enable all Game Center features by setting the enablegamecenter Boolean variable to true. 5. If the user did not sign in perhaps they pressed the Cancel button or login was unsuccessful you need to turn off all Game Center features. As previously stated, Game Center features are only available if the local player has logged in. Build the project. The code is now in place to authenticate with Game Center. All you need to do is call it in relevant places in Circuit Racer. Integrating with Circuit Racer Pause for a moment to think about the architecture of the game. Each screen in the game is a separate view controller and is controlled by a navigation view controller. Therefore, you re going to implement authentication by creating a subclass of UINavigationViewController. Right-click on the CircuitRacer group, select New\File and choose the Cocoa Touch Class template. Name your class CircuitRacerNavigationController, make it a subclass of UINavigationController, make sure the Language selected is Swift and click Next. Save the new class file. Now you need to set the class of your game s navigation controller to be your new class. Open Main.storyboard and select the navigation controller. 16

17 Chapter 23: Game Center Achievements In the Identity Inspector, inside Custom Class, set the Class property to CircuitRacerNavigationController. Great! Just a few more steps. Open CircuitRacerNavigationController.swift and override viewdidload(), as follows: override func viewdidload() { NSNotificationCenter.defaultCenter(). addobserver(self, selector: Selector("showAuthenticationViewController"), name: PresentAuthenticationViewController, object: nil) GameKitHelper.sharedInstance.authenticateLocalPlayer() super.viewdidload() Here you simply register for the PresentAuthenticationViewController notification and make a call to authenticatelocalplayer() of the GameKitHelper class. Note: As a general rule, you should always authenticate the local player as soon as the game starts. Next, add the following methods: func showauthenticationviewcontroller() { let gamekithelper = GameKitHelper.sharedInstance if let authenticationviewcontroller = gamekithelper.authenticationviewcontroller { topviewcontroller. presentviewcontroller(authenticationviewcontroller, animated: true, completion: nil) deinit { NSNotificationCenter.defaultCenter().removeObserver(self) 17

18 Chapter 23: Game Center Achievements In these methods, you simply present the authentication view controller over the top view in the navigation stack and deregister for notifications when the object is deallocated. It s time to build and run! If everything goes well, when the game launches you ll be presented with the Game Center authentication view: Enter your credentials and press Go. With Game Center, you only need to authenticate once. The next time you launch the game, Game Center will present a banner similar to the one shown below: In case you don t see the authentication view or the banner, it s probably because sandbox mode is not enabled on your device. This is a new feature introduced in ios 8. To enable it, open the Settings app, select Game Center and turn on the Sandbox switch in the Developer section. 18

19 Chapter 23: Game Center Achievements Note: Think of Sandbox as a close duplicate of the live Game Center servers. These servers are used to test Game Center functionality with games that have not been launched yet. Once the app is on the app store it automatically shifts to the production servers. By selecting the Sandbox mode all the api requests are sent to the Sandbox servers and not production. You can create sandbox users using itunes connect. To do this login to itunes connect and select Users and Roles. On the next screen select Sandbox Testers on the top left and click the + button to add a new user. Fill in all the details on the next screen and select Save. The users you enter here can act as your beta testers; these users can test features such as In-App Purchases, Game center and app localization. Doing the above ensures that Game Center only talks to the sandbox servers and not the ones in production. Adding achievements In the previous section, you added four achievements to the game in itunes Connect, so now you can go straight to writing code to award the achievements at the appropriate times within Circuit Racer. Open GameKitHelper.swift and add the following method: func reportachievements(achievements: [GKAchievement]) { 19

20 Chapter 23: Game Center Achievements if!gamecenterenabled { println("local player is not authenticated") return GKAchievement.reportAchievements(achievements) {(error) in self.lasterror = error To report an achievement, you first need to create a GKAchievement object that stores the achievement s identifier and its completion percentage. You ll create those later, but for now, assume you have an array of these you wish to report to Game Center. The good news is, once you have this array, it s literally just one line of code to send it: simply call GKAchievement s reportachievements(). This method automatically handles network errors for you and resends the data to Game Center until it arrives successfully. Now, let s integrate this into Circuit Racer for its specific achievements. Integrating with Circuit Racer To handle the Circuit Racer achievements, you re going to create a helper class. Right-click on the GameKit group, select New File, choose the Swift File template and click Next. Name the file AchievementsHelper and click Create. Open AchievementsHelper.swift and replace its contents with the following: import Foundation import GameKit class AchievementsHelper { struct Constants { static let MaxCollisions = 20 static let DestructionHeroAchievementId = "com.razeware.circuitracer.destructionhero" static let AmateurAchievementId = "com.razeware.circuitracer.amateurracer" static let IntermediateAchievementId = "com.razeware.circuitracer.intermediateracer" static let ProfessionalAchievementId = "com.razeware.circuitracer.professionalracer" 20

21 Chapter 23: Game Center Achievements Note: Change the achievement IDs to exactly match the ones you ve created in itunes Connect. Remember capitalization matters! Next, add the following class method: class func collisionachievement(noofcollisions: Int) -> GKAchievement { //1 let percent = (noofcollisions/constants.maxcollisions) * 100 //2 let collisionachievement = GKAchievement(identifier: Constants.DestructionHeroAchievementId) //3 collisionachievement.percentcomplete = Double(percent) collisionachievement.showscompletionbanner = true return collisionachievement This is a static helper method that makes it easy for you to report progress for the Destruction Hero achievement. Remember, this achievement is granted once a user has collided with 20 crates in a single race. collisionachievement() takes a parameter specifying the number of collisions that have occurred so far. It returns a GKAchievement that you can send to the reportachievements(achievements) method you just wrote. Here s quick breakdown of the method: 1. You can report achievement progress even if it s only partially complete. This calculates the percent complete based on the number of boxes the player has hit so far, compared to the maximum boxes set in the MaxCollisions static variable (20). 2. You create a GKAchievement object using the Destruction Hero achievement identifier. 3. You set the percentcomplete property of the GKAchievement object to the one calculated in the first step. Now that you have a helper method to create the Destruction Hero achievement, let s do the same for the achievements corresponding to each difficulty level. Remember, you ll unlock these achievements only when the player has completed the corresponding levels. Add the following method: 21

22 Chapter 23: Game Center Achievements class func achievementforlevel(leveltype: LevelType) -> GKAchievement { var achievementid = Constants.AmateurAchievementId if leveltype == LevelType.Medium { achievementid = Constants.IntermediateAchievementId else if leveltype == LevelType.Hard { achievementid = Constants.ProfessionalAchievementId let levelachievement = GKAchievement(identifier: achievementid) levelachievement.percentcomplete = 100 levelachievement.showscompletionbanner = true return levelachievement This method is similar to collisionachievement(). It creates an achievement depending upon the game level. Now that you have these helper methods in place, you need to make use of them in the game. Open GameScene.swift and import the GameKit framework. import GameKit Next, add a variable to the class that will track the number of collisions between the car and the boxes: var noofcollisionswithboxes = 0 Next, you need to tell the scene about physics collisions. For this, mark the class declaration of GameScene to implement the SKPhysicsContactDelegate protocol. class GameScene: SKScene, AnalogControlPositionChange, SKPhysicsContactDelegate Add this line at the end of initializegame() to set the GameScene class as the physics contact delegate: physicsworld.contactdelegate = self You also need to define constants for the physics categories that the car and box belong to. You ll use the physics categories to determine whether the objects colliding are the car and a box. You aren t interested in boxes colliding with other boxes! You ve dealt with collision categories during earlier chapters of the book, and so probably what you re about to do will feel very familiar. That s a good sign! 22

23 Chapter 23: Game Center Achievements For these categories, it s best if you use a structure, as the categories need to coincide with the category bitmasks you set up in GameScene.sks when you first built Circuit Racer. Add the following structure to the GameScene class: struct PhysicsCategories { static let CarCategoryMask: UInt32 = 1 static let BoxCategoryMask: UInt32 = 2 Next, you need to implement didbegincontact() to test for collisions between the car and a box: func didbegincontact(contact: SKPhysicsContact!) { if contact.bodya.categorybitmask!= UInt32.max && contact.bodyb.categorybitmask!= UInt32.max && (contact.bodya.categorybitmask + contact.bodyb.categorybitmask == PhysicsCategories.CarCategoryMask + PhysicsCategories.BoxCategoryMask) { noofcollisionswithboxes += 1 runaction(boxsoundaction) This method is called every time two physics bodies in the game collide. If the collision is between the car and a box, you increment the collision counter you declared earlier by 1 and, just for fun, play a collision sound, too. To set the car s physics body collision category, modify loadcartexture() as shown: func loadcartexture() { let car = self.childnodewithname("car") as SKSpriteNode car.texture = SKTexture(imageNamed:"car_\(carType.rawValue + 1)") /** Defines what logical 'categories' of bodies this body generates intersection notifications with. */ car.physicsbody?.contacttestbitmask = PhysicsCategories.BoxCategoryMask Okay! Now all that s left to do is report achievements. To do this, add the following new method: func reportachievementsforgamestate(haswon: Bool) { 23

24 Chapter 23: Game Center Achievements //1 var achievements = [GKAchievement]() //2 achievements. append(achievementshelper. collisionachievement(noofcollisionswithboxes)) //3 if haswon { achievements. append(achievementshelper.achievementforlevel(leveltype)) //4 GameKitHelper.sharedInstance.reportAchievements(achievements) This method is easy to understand. You pass in a Boolean variable that describes if the player has won or lost a game. If the player has won, you create an achievement for that level and report it to Game Center. Either way, you always report the collision achievement. Inside update(), add the following line just before the call to GameOverBlock: reportachievementsforgamestate(numberoflaps == 0) Build and run again it s time to run some tests! Try to win on each of the tracks. I know it might be difficult to finish all three levels, but if you want those achievements, you better complete those laps before the time s up! Every time you earn an achievement, Game Center will show a banner like this: 24

25 Chapter 23: Game Center Achievements Achievement unlocked: You have successfully added achievements into your game! Initializing the built-in user interface You now have achievements in your game, but what if your player wants to see what achievements he or she has unlocked so far? At this point, their only option is to check their achievements in the built-in Game Center app. But of course, this requires the player to leave your app, which is never a good thing. It would be much better if there were a way for a player to see their progress right from within your app and luckily, there is! The Game Kit framework provides a class called GKGameCenterViewController that allows your players to view their achievements, leaderboards and challenges right from within your app. Let s add this to Circuit Racer. To present the Game Center view controller, your GameKitHelper class will need to conform to the GKGameCenterControllerDelegate protocol. Open GameKitHelper.swift and add the protocol to the class definition: class GameKitHelper: NSObject, GKGameCenterControllerDelegate { To show the Game Center view controller, add the following method: func showgkgamecenterviewcontroller(viewcontroller: UIViewController!) { if!gamecenterenabled { println("local player is not authenticated") return 25

26 Chapter 23: Game Center Achievements //1 let gamecenterviewcontroller = GKGameCenterViewController() //2 gamecenterviewcontroller.gamecenterdelegate = self //3 gamecenterviewcontroller.viewstate =.Achievements //4 viewcontroller. presentviewcontroller(gamecenterviewcontroller, animated: true, completion: nil) The above method is responsible for creating and displaying the GKGameCenterViewController. The steps involved in doing so are these: 1. First, initialize an object of GKGameCenterViewController. 2. Set the delegate of the GKGameCenterViewController. Game Center informs the delegate when the user finishes interacting with the GKGameCenterViewController. You ll look into that in just a moment. 3. Set the default view state to Achievements. This means that when you display the Game Center view controller, the achievements view is the first view the player sees. 4. Finally, present the view controller. Now add the required GKGameCenterControllerDelegate delegate method to dismiss the Game Center view controller: func gamecenterviewcontrollerdidfinish(gamecenterviewcontroller: GKGameCenterViewController!) { gamecenterviewcontroller. dismissviewcontrolleranimated(true, completion: nil) That s all it takes to present the GKGameCenterViewController now you just need to integrate it into Circuit Racer. Integrating with Circuit Racer There s currently no logical place in the Circuit Racer menu system to launch the Game Center view controller. So, you ll create a new home screen as an initial 26

27 Chapter 23: Game Center Achievements screen for the game, where the player can choose to either play the game or view his progress in Game Center. To do this, open Main.storyboard and drag a new view controller onto the storyboard. Drag an image view into the view controller and make it fill the entire view controller. Set the image to bg-main-menu.png. Then, drag two buttons into the view controller, set their Type to Custom, delete their titles and set the images to btn-play.png and btn-gamecenter.png. At this point, your view controller should look like this: 27

28 Chapter 23: Game Center Achievements For the background and buttons to appear correctly on all devices, you ll need to apply some constraints in the storyboard. Select the image view and click the second button in the bottom right bar to bring up the Pin menu. Select all four brackets to pin the image view to each edge, and click Add 4 Constriants: To ensure the buttons show up on all devices, apply the following constraints to the Play button, by control-dragging in the view according to the table below: Ctrl+drag from Ctrl+drag to Menu option Constraint 1 Play button Play button Aspect Ratio Constraint 2 Play button Background image Center X Constraint 3 Play button Background image Center Y Constraint 4 Play button Background image Equal Widths Select the Play button and look at the constraints in the Size Inspector. Double-click on the Align Center Y constraint. Reverse the items so that Button.Center.Y is on top, and set the Constant to 0 and the Multiplier to

29 Chapter 23: Game Center Achievements Similarly double click the Equal Width constraint and change the multiplier to 0.6. For the Game Center buttons, apply the following constraints by control-dragging in the view according to the table below: Ctrl+drag from Ctrl+drag to Menu option Constraint 1 Game Center button Game Center button Aspect Ratio Constraint 2 Game Center button Play button Center X Constraint 3 Game Center button Background image Center Y Constraint 4 Game Center button Play button Equal Widths Again, select the Game Center button and look at the Size Inspector to see the button s constraints. Double-click on the Align Center Y constraint. Make sure Button.Center.Y is the first item, set the Constant to 0 and the Multiplier to Your buttons will now appear centered and in a suitable position on all devices. For your new custom class for this view controller, create a new file in the CircuitRacer group with the Cocoa Touch Class template. Enter HomeScreenViewController for the name and UIViewController for Subclass. Ensure the Language is set to Swift, then click Next and finally Create. Back in Main.storyboard, select your new view controller and in the Identity Inspector, set the Class and Storyboard ID to HomeScreenViewController: 29

30 Chapter 23: Game Center Achievements Next, control-drag from the Circuit Racer Navigation Controller to the Home Screen View Controller. In the popup that appears, select root view controller: This displays the Home Screen View Controller when the app is launched, instead of the Select Car View controller. Finally, you need to add action methods for both buttons. Still in Main.storyboard, select HomeScreenViewController and make sure the Assistant Editor is open with HomeScreenViewController.swift. Control-drag from the play button to HomeScreenViewController.swift and enter playgame for the name. For Connection, select Action. For Type, select UIButton. Click Connect. 30

31 Chapter 23: Game Center Achievements Repeat these steps for the Game Center button, entering gamecenter for the action name. Now open HomeScreenViewController.swift and implement the playgame(sender) and gamecenter(sender) methods as func playgame(sender: UIButton) { SKTAudio.sharedInstance().playSoundEffect("button_press.wav") if let storyboard = storyboard { let carviewcontroller = storyboard. instantiateviewcontrollerwithidentifier ("SelectCarViewController") as SelectCarViewController navigationcontroller?.pushviewcontroller(carviewcontroller, animated: func gamecenter(sender: UIButton) { SKTAudio.sharedInstance().playSoundEffect("button_press.wav") GameKitHelper.sharedInstance.showGKGameCenterViewController( self) Build and run. Wait for Game Center to authenticate the player and then press the Game Center button. This will open the GKGameCenterViewController, which looks like this: 31

32 Chapter 23: Game Center Achievements And that s it you ve added Game Center, achievements and a Game Center view controller to Circuit Racer! In the coming two chapters, you ll learn all about the latest Game Center leaderboard features and multiplayer APIs. But first, a quick challenge for you! Challenges Before you proceed to the next chapter, here is a challenge that will ensure you ve understood the majority of the chapter. If you get stuck, you can find the solutions in the resources for this chapter, but give it your best shot first! Challenge 1: Racing addict achievement Create a new achievement in itunes Connect called Racing addict. Hide it initially and award it if the player plays the game at least 10 times in a row. Here are a few hints for how to do this: Create a variable to track the number of times the local player has played the game. Every time the game completes, check to see if that variable has crossed 10. You can use one of the images for the other achievements to create this achievement. 32

33 Chapter 24: Game Center Leaderboards By Kauserali Hafizji In the previous chapter, you learned the steps required to set up your game to use Game Center, authenticate the local player and enhance the player s experience with achievements. You also implemented Game Center s built-in user interface. In this chapter, you ll focus on another awesome Game Center feature: leaderboards. Think of a leaderboard as a database of the scores of all the players playing your game. A leaderboard allows your players to compare their scores with those of other players. It s a great way to get players to play your game just one more time to see if they can increase their score and move up the leaderboard. Traditionally, implementing leaderboards meant developing performant server-side components and configuring load-balanced, server-side infrastructure. Game Center provides all of this for you, making it easy to add leaderboards to your game just add a few lines of code, and you re set. Let s try it out and add some leaderboards to Circuit Racer! Note: This chapter begins where the previous chapter s challenge left off. If you were unable to complete the challenge or skipped ahead from an earlier chapter, don t worry you can simply open CircuitRacer-Starter from this chapter s resources to begin in the right place. However, if you skipped the last chapter, to follow along with this chapter you will need to set up Circuit Racer in itunes Connect and register a number of achievements. For instructions on how to perform these steps, see the previous chapter. Supporting leaderboards There are five steps to add leaderboards to your game. Before you get started with Circuit Racer, here s a quick overview.

34 Chapter 24: Game Center Leaderboards 1. Authenticate the local player. Remember that to use any Game Center feature, you first need to authenticate the local player. 2. Create a strategy for using leaderboards in the game. Decide how many leaderboards the game is going to have and what scores will drive each leaderboard. 3. Configure leaderboards in itunes Connect. Add each leaderboard and set its name and formatting, such as the score range. Optionally, add an image for each leaderboard. 4. Add code to report scores to Game Center. In the same way you added code to send an array of GKAchievements to Game Center in the previous chapter, you need to add similar code to send an array of GKScores. 5. Add code to display the leaderboards to the player. You ll use the GKGameCenterViewController as you did for achievements. Optionally, you can also retrieve the score data and display the leaderboard in a custom user interface. The rest of this section will take you through these steps, one by one. Step 1: Authenticating the local player You added support for player authentication to Circuit Racer in the previous chapter, so you can skip this step. If you d like to go over the process one more time, have a look at authenticatelocalplayer() in the GameKitHelper class. Step 2: Creating a leaderboard strategy To support leaderboards, your game needs a scoring mechanism that allows it to calculate a fixed score every time the player finishes a race. The only restriction Game Center puts on this score is that it must be a 64-bit integer. Since Circuit Racer is a race against time, let s keep the scoring mechanism as the time required to complete a track. This means the game will report the amount of time it took for the local player to complete each track. The players who finish the track in the shortest amount of time will be at the top of the leaderboard. Before moving on to the next step, you also need to decide on the number of leaderboards your game will support. It s not mandatory to have multiple 34

35 Chapter 24: Game Center Leaderboards leaderboards, but it s usually a good idea, as it gives the player a fine-grained look at the position they hold among all the game s players. Having multiple leaderboards also gives the player opportunity to do well on certain leaderboards when they might not be able to push for the top on others. In Circuit Racer, the player chooses a car and then selects a difficulty level. You re going to create a leaderboard for each of the possible car/difficulty combinations, so that players have the chance to climb the leaderboard with their favorite car and track. To account for all the possible car/difficulty combinations, you re going to create a total of nine leaderboards. For example, the first leaderboard will be Car_1_level_easy_fastest_time. Your other leaderboards will similarly cover all the other car and difficulty level combinations. Later in the chapter, you ll use these nine leaderboards to create leaderboard sets. Keep reading to learn more. Step 3: Configuring leaderboards in itunes Connect Login to itunes Connect using your credentials and select the My Apps option, then choose your Circuit Racer app its exact name is unique and will be whatever you called it when going through the last chapter. Select the Game Center option. Select the Add Leaderboard button under the Leaderboards section. Then, select the Single Leaderboard option to create a new leaderboard. Note: The other option, Combined Leaderboard, allows you to create a new virtual leaderboard that combines the results of several leaderboards. For example, you could create a leaderboard for Circuit Racer called Any car level easy fastest time that combines the results of Car 1 level easy fastest time, Car 2 level easy fastest time and Car 3 level easy fastest time. You would still report your scores to each individual leaderboard this just provides an easy way to aggregate the results for players across several leaderboards that share the same score format type and sort order. 35

36 Chapter 24: Game Center Leaderboards If you re wondering what each of those fields are on the next page, don t fret! Enter the following values: Car 1 level easy fastest time for the Leaderboard Reference Name. This is a string that represents the internal name for the leaderboard. You can use this string to search for leaderboards within itunes Connect. [Your app Bundle ID].car1_level_easy_fastest_time for the Leaderboard ID. This string uniquely identifies the leaderboard. You ll use this identifier to report scores to Game Center. It s usually best practice to use your app s Bundle ID as a prefix. Elapsed Time To the Second as the Score Format Type. This field specifies the format of the scores you ll send for this particular leaderboard. It also tells Game Center how to interpret the scores. Your game s scoring mechanism is based on time, so you choose the appropriate format. Best Score as the Score Submission Type. This field specifies which score the leaderboard will display first, the best or the most recent one. Low to High as the Sort Order. This field dictates how the scores are arranged in the leaderboard. In the case of Circuit Racer, the lowest time refers to the highest score, hence your selection here. 1 to 60 for the Score Range. Even though this field is optional, I recommend you add a value here. You will learn more about this field in the sections to come, so stay tuned. For now, you enter 1 and 60, which are the lowest and highest values from the game s LevelDetails.plist file. 36

37 Chapter 24: Game Center Leaderboards The next step is to add a language. Select the Add Language button in the Leaderboard Localization section. The important thing to keep in mind is that these are the settings that affect what your users will see, so choose wisely. Fill in the following details: English for the Language. Yellow Car Easy Level Fastest Time for the Name. This is the name of the leaderboard that players will see. Elapsed Time (hours, minutes, seconds, ex. 5:01:18) for the Score Format. This is the format Game Center will use to display the scores in the leaderboard. seconds for the Score Format Suffix. This is the suffix that Game Center adds to the score submitted by the device. Circuit Racer measures a player s score in seconds. leaderboard-yellow-easy.png for the Image. Adding an image for the leaderboard is optional, but it s always a good idea, as it will enhance the leaderboard s appearance in Game Center. You ll find the leaderboard images in the LeaderboardIcons directory in the resources for this chapter. Note: You can add multiple languages to each leaderboard and Game Center will display the correct language, according to the locale set on the phone. Click the Save button when you re done entering all the details. This will create a new leaderboard, as shown below. 37

38 Chapter 24: Game Center Leaderboards Now you need to create the other eight leaderboards for the game. Since you already know how to create a single leaderboard, the rest should be easy. Make sure you follow the table below while entering the data required for each leaderboard. Yes, this is a bit repetitive, but think of it this way by the time you re done, you ll be an old hand at setting up leaderboards in itunes Connect! :] Reference Name Leaderboard ID Score range Car 1 level medium fastest time Car 1 level hard fastest time Car 2 level easy fastest time Car 2 level medium fastest time Car 2 level hard fastest time Car 3 level easy fastest time Car 3 level medium fastest time Car 3 level hard fastest time [Your Bundle ID].car1_level_medium_fastest_time [Your Bundle ID].car1_level_hard_fastest_time [Your Bundle ID].car2_level_easy_fastest_time [Your Bundle ID].car2_level_medium_fastest_time [Your Bundle ID].car2_level_hard_fastest_time [Your Bundle ID].car3_level_easy_fastest_time [Your Bundle ID].car3_level_medium_fastest_time [Your Bundle ID].car3_level_hard_fastest_time Enter whatever you like for the English localization for each, based on the previous examples. Use the leaderboard images provided in the LeaderboardIcons directory in the resources for this chapter. After you re done entering all the details, your leaderboards table in itunes Connect should look like the one below: 38

39 Chapter 24: Game Center Leaderboards When you ve finished adding your leaderboards, click Done to go back to your app s information page, and then click Done. Step 4: Reporting scores to Game Center Now that you ve got the leaderboards set up in itunes Connect, it s time to write some code. To keep things simple, you re going to add a method to the GameKitHelper class that will report a score to a leaderboard identified by its leaderboard ID. Make sure Circuit Racer is open in Xcode, then open GameKitHelper.swift and add the following method: func reportscore(score: Int64, forleaderboardid leaderboardid: String) { if!gamecenterenabled { println("local player is not authenticated") return //1 let scorereporter = GKScore(leaderboardIdentifier: leaderboardid) scorereporter.value = score scorereporter.context = 0 let scores = [scorereporter] //2 GKScore.reportScores(scores) {(error) in self.lasterror = error 39

40 Chapter 24: Game Center Leaderboards The above code is responsible for creating and sending a score to Game Center. Here is a step-wise explanation: 1. First, the method creates an object of type GKScore that holds information about the player s score. Game Center expects you to send out scores using this object. Game Center also returns objects of type GKScore when you retrieve scores. As you can see, a GKScore simply stores a value: the number to send to the leaderboard, which in this case is the number of seconds. You can also set a context for each score, which is an arbitrary 64-bit unsigned integer that you can associate with a score value, for use when you retrieve scores later. You don t need this for Circuit Racer, so you set it to Next, the method reports the score using GKScore s reportscores(scores, completionhandler) class method. The code calls the completion handler when Game Center is done processing the scores, and again, this method takes care of auto-resending scores on network failures. Awesome! You have all the code in place to send scores to Game Center. To do this, open GameScene.swift and add the following two properties: var maxtime = 0 let leaderboardidmap = ["\(CarType.Yellow.rawValue)_\(LevelType.Easy.rawValue)" : "com.razeware.circuitracer.car1_level_easy_fastest_time", "\(CarType.Yellow.rawValue)_\(LevelType.Medium.rawValue)" : "com.razeware.circuitracer.car1_level_medium_fastest_time","\(cartype.ye llow.rawvalue)_\(leveltype.hard.rawvalue)" : "com.razeware.circuitracer.car1_level_hard_fastest_time", "\(CarType.Blue.rawValue)_\(LevelType.Easy.rawValue)" : "com.razeware.circuitracer.car2_level_easy_fastest_time", "\(CarType.Blue.rawValue)_\(LevelType.Medium.rawValue)" : "com.razeware.circuitracer.car2_level_medium_fastest_time","\(cartype.bl ue.rawvalue)_\(leveltype.hard.rawvalue)" : "com.razeware.circuitracer.car2_level_hard_fastest_time", "\(CarType.Red.rawValue)_\(LevelType.Easy.rawValue)" : "com.razeware.circuitracer.car3_level_easy_fastest_time", "\(CarType.Red.rawValue)_\(LevelType.Medium.rawValue)" : "com.razeware.circuitracer.car3_level_medium_fastest_time","\(cartype.re d.rawvalue)_\(leveltype.hard.rawvalue)" : "com.razeware.circuitracer.car3_level_hard_fastest_time"] You ll use the first variable, maxtime, to store the total amount of time the player has to complete the current level, which you ll use to calculate the amount of time it took the player to complete the current track. The second variable, leaderboardidmap, is a dictionary where the key is a string with format CarType_LevelType and the value is its corresponding leaderboard ID. 40

41 Chapter 24: Game Center Leaderboards For example, the key 1_1 would have a leaderboard ID of com.razeware.circuitracer.car1_level_easy_fastest_time. The dictionary is initialized and ready to use. Make sure that you change the leaderboard IDs to exactly match the leaderboard IDs you entered into itunes Connect, and remember that capitalization matters. Next, add the following line at the bottom of loadlevel(): maxtime = timeinseconds This stores the maximum time for the current level. Then add this new method: func reportscoretogamecenter() { let timetocomplete = maxtime - timeinseconds GameKitHelper.sharedInstance.reportScore( Int64(timeToComplete), forleaderboardid: leaderboardidmap["\(cartype.rawvalue)_\(leveltype.rawvalue)"]!) This simply calls the method you added to GameKitHelper to report a new score. Now inside update(currenttime), add a call to reportscoretogamecenter() just before calling the gameoverblock: reportscoretogamecenter() Finally, it s time to test everything. Build and run the project. After you successfully complete a track, the game will automatically report your score to Game Center. Check the debug console to see if anything went wrong. If you don t see any error logs, then you can trust that Game Center got your score. 41

42 Chapter 24: Game Center Leaderboards Step 5: Displaying leaderboards to the player The last thing to do is to display those leaderboards. Game Center does provide helper methods that retrieve the scores in each leaderboard using the GKLeaderboard object. Once you have the scores, you can present the leaderboards to the player in any view. However, there s a much easier way to display leaderboards if you don t want to create your own custom user interface: using the GKGameCenterViewController. You added support for this view controller in the last chapter, so the leaderboards are actually already available to the player in your app right now! As this chapter is all about leaderboards, you re going to make one small change to the GKGameCenterViewController initialization to make it show leaderboards by default, rather than the achievements it currently defaults to. Open GameKitHelper.swift and navigate to the method named showgkgamecenterviewcontroller(viewcontroller). In it, change viewstate from.achievements to.leaderboards, as shown below: gamecenterviewcontroller.viewstate =.Leaderboards This sets up the view controller to display leaderboards by default when it is first opened, rather than achievements. Build and run. Tap on the Game Center button on the home screen and you should see the leaderboards you set up in itunes Connect with any scores you ve earned since you began sending scores to Game Center. 42

43 Chapter 24: Game Center Leaderboards Congratulations your app has leaderboards! And you added them! If you just wanted to learn how to add basic leaderboards to your app, you can stop reading now and skip ahead to the next chapter. But if you want to learn about some more advanced things you can do with leaderboards, read on! Leaderboard sets Now that you re familiar with leaderboards, I d like to tell you about leaderboard sets. This feature was introduced in Game Center with ios 7. Leaderboard sets give developers the ability to combine several leaderboards into a single group. Think of a leaderboard set as a tagging framework. Each leaderboard can belong to one or several groups/sets. This allows you to organize your leaderboards into a structured hierarchy, rather than the 9-long list of leaderboards mismatch you currently have, which can be overwhelming to players. To add support for leaderboard sets in Circuit Racer, you need an organizing strategy. You re going to group your leaderboards according to car type. Thus, all the leaderboards for the yellow car will belong to the Yellow car group, and so forth. To visualize this, take a look at the table below: 43

44 Chapter 24: Game Center Leaderboards Yellow car Blue car Red car car1_level_easy_fastest_ti me car1_level_medium_fastes t_time car1_level_hard_fastest_ti me car2_level_easy_fastest_ti me car2_level_medium_fastes t_time car2_level_hard_fastest_ti me car3_level_easy_fastest_ti me car3_level_medium_fastes t_time car3_level_hard_fastest_ti me Note: You could also organize the leaderboards according to difficulty level. Since leaderboard sets provide the capability to tag leaderboards multiple times, it would be easy to do it both ways. Login to itunes Connect and open the Game Center page for Circuit Racer, as you ve done before. Under the leaderboards section you ll find a button titled Move All Leaderboards into Leaderboard Sets. Click that button to create the first leaderboard set. On the next screen, enter Yellow Car for the Leaderboard Set Reference Name and [Your app Bundle ID].yellowcar for the Leaderboard Set ID. Select the Continue button when you re done entering the data. Now you need to add leaderboards to this set. On the next screen, under the section Leaderboards in This Set, select the Add to Leaderboard Set button. Since this is the Yellow Car group, all the leaderboards pertaining to the first car should be a part of this group. 44

45 Chapter 24: Game Center Leaderboards As shown in the image above, you first select the leaderboard you want to add to the set. Next, you enter a display name for the leaderboard within the set. Since the set is named Yellow Car, it makes sense to name the car1_level_easy_fastest_time leaderboard Easy level. After you ve done that, click Save. In the same fashion, add the other two leaderboards pertaining to the yellow car to this set, naming them Medium level and Hard level. Now you need to name the leaderboard set. Under the Leaderboard Set Localization section, select the Add language button. Select English as the language and enter Yellow car for the display name. Choose the leaderboard image leaderboard-yellow.png and then click Save. Using the Add Leaderboard Set button in the first section of the page, repeat the above procedure for the blue and red cars. Make sure you give them ID values of [Your app Bundle ID].bluecar and [Your app Bundle ID].redcar to remain consistent with the yellow car leaderboard set, replacing.razeware with your own Bundle ID, of course. 45

46 Chapter 24: Game Center Leaderboards Note: If you decide to support leaderboard sets, you need to ensure that every leaderboard is part of at least one group. Finally, once you ve organized all the leaderboards into their respective sets, select the Save button at the bottom-right. You should now see three display sets under the Leaderboard Sets section, as shown below: Note the link to View Leaderboards in Leaderboard Sets. This can be quite useful to visualize your leaderboard sets click it, and you ll see the following: That s it! You now have everything ready in itunes Connect. The next step is to show the leaderboard sets to the user/local player. Once again, the easiest way to do that is to use the GKGameCenterViewController. But you don t need to add any code! Simply build and run. When you tap on the Game Center button on the home screen, the GKGameCenterViewController opens up and shows the leaderboard sets you created in itunes Connect. Note that it might take a few minutes for your leaderboards to show up in your app if they don t appear right away, wait a few minutes. 46

47 Chapter 24: Game Center Leaderboards Ah, sweet simplicity! You now see just three leaderboards where before there were nine, and it s a lot easier to navigate between them thanks to the hierarchical organization. Security in Game Center You should be quite familiar with Game Center s features, so let s take a quick look at the different ways in which Game Center provides security. This is important to understand, because you don t want players to cheat and submit false scores! How does submission work? First, let s consider how the system handles submissions. When your game sends a score to Game Center, it doesn t send it directly to the Game Center servers. Instead, it sends the score to the gamed daemon, which in turn sends it to the servers. Why does the system manage communication this way? To answer that question, imagine that the device sending out the score has an Internet connectivity problem. In such a situation, the gamed daemon stores the scores and will send them to the Game Center servers when the Internet connection is back. This means you don t have to do anything in case the Internet connection is wonky. Game Center takes care of all the heavy lifting for you. 47

48 Chapter 24: Game Center Leaderboards Limit cheating Believe it or not, there are people out there who are going to try to cheat by submitting unearned scores, even scores that are not humanly possible to achieve! Game Center gives you three ways to combat cheating within your game: 1. Signed submissions: This feature is totally free and you don t need to make any changes to your game to use it. When your game submits scores/achievements to the gamed daemon, it automatically attaches a cryptographic signature to the submission. Game Center rejects any submission that does not have this signature. 2. Score range: Another way to limit cheating is to use the score range property on a leaderboard. The score range specifies minimum and maximum values that you think a player can achieve for a particular leaderboard. Any score that is higher or lower than the specified range is accepted by Game Center, but it is never shown to another player. Since Game Center holds onto the score, if you later find out that it is possible to get such a score, changing the set range will make that score visible. You already set up score ranges when you created your leaderboards earlier. 3. Score/player management: To view this console, login to itunes Connect and select any leaderboard. You ll see a list of the top 100 players in that leaderboard. This helps you, the developer, to view scores that were submitted. You can chose to remove a score from a leaderboard and you can even chose to remove a player, thereby preventing him or her from posting scores to that leaderboard again. This is an extremely powerful tool and as the saying goes, With great power comes great responsibility. Make sure you use this power appropriately and only when needed. That s it for leaderboards except for one final challenge for you! Challenges This chapter just has one challenge, designed to give you a bit more practice with leaderboard sets. 48

49 Chapter 24: Game Center Leaderboards Note that you don t actually need to make any changes to your project to complete this challenge, so there is no solution project for this chapter. Challenge 1: Leaderboard sets Make three new leaderboard sets that organize the nine leaderboards you created in this chapter by difficulty level. Remember that you only have to make the changes in itunes Connect and the GKGameCenterViewController will automatically show them in the leaderboards section. There is no solution provided for this challenge, as it requires no code changes. 49

50 Chapter 25: Game Center Multiplayer By Kauserali Hafizji Out of all of the features you could add to your game, multiplayer support is one of the best. For your users, playing with or against other players can be a whole lot more fun, memorable and unpredictable than playing alone. Multiplayer support can also help increase the word-of-mouth visibility of your game after all, if players like your game, they ll want to recommend it to their friends so they can play together! In the old days, creating a multiplayer game on ios was a lot of work involving complicated networking and back-end code to find players and create matches. These days, Game Center solves most of this for you, freeing you to focus on your game. Using Game Center APIs, your game can host matches in real time, either between the player s friends or by auto-matching random players from around the world. You can also send data between players and broadcast events to keep the game state in sync. In this chapter, you re going to add multiplayer support to Circuit Racer so that multiple players can race each other in real time. Even though the game in this chapter is only one simple example of what a multiplayer game could be, we ve ensured that the GameKitHelper class you ll develop is reusable. That means you can use it in your own games, and it s a great starting point for more sophisticated implementations. Note: This chapter begins where the previous chapter s left off, with a slight modification. If you were unable to complete the challenge or skipped ahead from an earlier chapter, don t worry you can simply open CircuitRacer-Starter from this chapter s resources to begin in the right place. However, to follow along with this chapter, you need an entry in itunes Connect for Circuit Racer and a number of achievements and leaderboards. For instructions on how to perform these steps, see the previous chapters.

51 Chapter 25: Performance: Texture Atlases Before you start, it s important to know that the starter project for this chapter uses the analog control instead of the accelerometer to drive the car. This is because the accelerometer doesn t provide any information when you run the app in the ios Simulator. Since you re going to add multiplayer support, you ll need to test the app on two devices. In case you don t have an additional device, having a game with an analog control allows you to use the Simulator as one of your test devices. Choosing a multiplayer strategy When implementing multiplayer support, the first decision you have to make is whether you want your multiplayer game to be turn-based or played in real time. Game Center supports both, but with different APIs. Turn-based games are those in which only one user can play at a time. A classic example is chess. Role-playing games also tend to be turn-based. Real-time games are those in which all the players can make their moves simultaneously, such as in a racing game. Circuit Racer being a racing game, you re going to implement a real-time multiplayer strategy in which two players compete against each other to finish a set number of laps around a track. The one who finishes first wins. Simple enough? Note: This chapter focuses on real-time multiplayer games. To learn more about turn-based multiplayer games, check out the Game Center chapters in ios 5 and ios 6 by Tutorials. The second decision you have to make is how many players the game will support. Game Center supports a maximum of four simultaneous players, unless you use a hosted server, as discussed in a minute. To keep things simple, for Circuit Racer you will only support two simultaneous players. To implement this strategy, you need to use the Game Center matchmaking API to find two players who want to play the game. Once you ve got a match, you need to send data back and forth between the devices, the most important being the positions of both cars on the track. Game Center provides two ways to send data between players in a match: 1. Peer-to-peer: If you use this method, you don t need to worry about much of the complicated networking code such as setting up connections or writing backend code. Instead, you can use Game Center APIs to easily send data between devices. In the background, Game Center will automatically create the 51

52 Chapter 25: Performance: Texture Atlases appropriate connections, route data through Game Center s servers and ensure that it reaches the other devices. 2. Hosted game: This method is best for developers who want to host the networking layer on their own servers rather than use Game Center s servers. This might be useful if you already have the networking layer in place and just want to use Game Center s matchmaking API to create a match, or if you want to support more than four simultaneous players. For this tutorial, you re going to stick with the first method and route all the data through Game Center s servers. Player matchmaking Before you can begin a game, you first need to find a set of players who want to play together. This is called matchmaking in Game Center terms. Lucky for you, Game Center includes APIs that make this easy. Let s learn how to use them! In Xcode, open GameKitHelper.swift and add the following protocol at the top of the file: protocol GameKitHelperDelegate { func matchstarted() func matchended() func matchreceiveddata(match: GKMatch, data: NSData, fromplayer player: String) This protocol defines how the GameKitHelper object will communicate with the outside world when using multiplayer APIs. Game Center will notify the object that implements this protocol of three events: 1. Match started: This function notifies the implementing object that Game Center has found a suitable match and the game can start. 52

53 Chapter 25: Performance: Texture Atlases 2. Match ended: This function notifies the implementing object that a match has ended. There are a number of reasons this might happen, such as player disconnection or networking errors. 3. Match data received: This function tells the implementing object that Game Center has received data from the other end, such as updates to game state. Next, add the following variables to the GameKitHelper class: var delegate: GameKitHelperDelegate? var multiplayermatch: GKMatch? The first variable is the delegate, which represents the object that will receive all the multiplayer events you created in the protocol above. The GKMatch object represents the network between the devices involved in the multiplayer game. Since both of these variables can be nil if the user is playing in single-player mode, you ve defined them as optionals. Along with the above variables, declare two more variables, as shown below: var presentingviewcontroller: UIViewController? var multiplayermatchstarted: Bool You ll use the first variable, presentingviewcontroller, to keep hold of the view controller over which you re going to show Game Center s matchmaking UI. You ll use the second variable to track if the game is currently in multiplayer mode. By default, the game will be single-player, so you need to initialize the multiplayermatchstarted variable to false. Add the following line to init(): multiplayermatchstarted = false Next, define this new method: func findmatch(minplayers: Int, maxplayers: Int, presentingviewcontroller viewcontroller: UIViewController, delegate: GameKitHelperDelegate) { //1 if!gamecenterenabled { println("local player is not authenticated") return //2 multiplayermatchstarted = false multiplayermatch = nil self.delegate = delegate presentingviewcontroller = viewcontroller 53

54 Chapter 25: Performance: Texture Atlases //3 let matchrequest = GKMatchRequest() matchrequest.minplayers = minplayers matchrequest.maxplayers = maxplayers //4 let matchmakerviewcontroller = GKMatchmakerViewController(matchRequest: matchrequest) matchmakerviewcontroller.matchmakerdelegate = self presentingviewcontroller?. presentviewcontroller(matchmakerviewcontroller, animated: false, completion: nil) This may seem like a lot to digest, so let s go through it step by step to understand what this method does. 1. The method first checks if the local player is authenticated. If not, the method exits without attempting to do anything else. 2. If the player is authenticated, the method then sets the variables you declared earlier to an appropriate value. 3. Next, the method creates a GKMatchRequest object. This object does exactly what its name implies: It creates a match request with the appropriate criteria. In this case, you ve used Game Center s minimum and maximum numbers of players. Later, you ll set these to 2, because you want only two players per game in Circuit Racer. 4. The method then uses the GKMatchRequest object to initialize the GKMatchmakerViewController. Just like the built-in view controller to display leaderboards and achievements (GKGameCenterViewController) that you saw in the last chapter, Apple provides a built-in view controller to help players with the matchmaking process (GKMatchmakerViewController). This allows players to create a match with either one of their Game Center friends or with a random player. Game Center does provide APIs that you can use to programmatically create a match check out GKMatchmaker if you re curious. However, using GKMatchmakerViewController is much simpler and gives you a clean ios 7-style layout for free. At this point, build the game. Xcode will generate a warning like the one shown below: 54

55 Chapter 25: Performance: Texture Atlases Drill down and you ll notice that you ve set the delegate of the GKMatchmakerViewController to self, but you haven t marked your class as implementing the delegate protocol nor implemented any of the required methods. To fix this, add the protocol to the class declaration in GameKitHelper.swift: class GameKitHelper: NSObject, GKGameCenterControllerDelegate, GKMatchmakerViewControllerDelegate Also add the following GKMatchmakerViewControllerDelegate protocol methods: func matchmakerviewcontrollerwascancelled(viewcontroller: GKMatchmakerViewController!) { presentingviewcontroller?.dismissviewcontrolleranimated(true, completion: nil) delegate?.matchended() func matchmakerviewcontroller(viewcontroller: GKMatchmakerViewController!, didfailwitherror error: NSError!) { presentingviewcontroller?.dismissviewcontrolleranimated(true, completion: nil) println("error creating match: \(error.localizeddescription)") delegate?.matchended() func matchmakerviewcontroller(viewcontroller: GKMatchmakerViewController!, didfindmatch match: GKMatch!) { presentingviewcontroller?.dismissviewcontrolleranimated(true, completion: nil) multiplayermatch = match multiplayermatch!.delegate = self if!multiplayermatchstarted && multiplayermatch?.expectedplayercount == 0 { println("ready to start the match") 55

56 Chapter 25: Performance: Texture Atlases You ve added three of the protocol s methods. Here s a brief description of each: matchmakerviewcontrollerwascancelled(viewcontroller): This method is called when the user cancels the GKMatchmakerViewController. All you have to do here is dismiss it, as the user doesn t want to play a multiplayer game. You also call matchended on the delegate object to notify that the game couldn t create the match. matchmakerviewcontroller(viewcontroller, didfailwitherror): This method is called when the matchmaking API fails to create a match. The only things to do here are to dismiss the view controller, log an error to the console and call matchended on the delegate. matchmakerviewcontroller(viewcontroller, didfindmatch): This is where things get interesting. This method is called when Game Center successfully finds a match. You store the GKMatch object in an instance variable and set the delegate of the GKMatch object to self. In matchmakerviewcontroller(viewcontroller, didfindmatch), notice that you re checking the expectedplayercount property of the GKMatch object. This property represents the number of players that have yet to join the game before it can begin. If this property is 0, then all the required players have joined and the game can start. The last piece of the puzzle is the GKMatchDelegate. When you create a match, you set the delegate property of the GKMatch object to self. GKMatch informs this delegate of important events related to the match. In GameKitHelper.swift, add the protocol to the class declaration, as you did before: class GameKitHelper: NSObject, GKGameCenterControllerDelegate, GKMatchmakerViewControllerDelegate, GKMatchDelegate You will now add the GKMatchDelegate protocol methods one by one to make it easier to understand them. At the bottom of the implementation section, add the following method: func match(match: GKMatch!, didreceivedata data: NSData!, 56

57 Chapter 25: Performance: Texture Atlases fromplayer playerid: String!) { if multiplayermatch!= match { return delegate?.matchreceiveddata(match, data: data, fromplayer: playerid) The game calls this method when it receives data from the other player s device. All you re doing is passing data to the delegate, which in your case is the scene object more on that later. Add the following method below match(match, didreceivedata): func match(match: GKMatch!, didfailwitherror error: NSError!) { if multiplayermatch!= match { return multiplayermatchstarted = false delegate?.matchended() The game calls the method above when there s an error during the match. In this case, you send a matchended message back to the delegate. Now add the final delegate method: func match(match: GKMatch!, player playerid: String!, didchangestate state: GKPlayerConnectionState) { if multiplayermatch!= match { return switch state { case.stateconnected: println("player connected") if!multiplayermatchstarted && multiplayermatch?.expectedplayercount == 0 { println("ready to start the match") case.statedisconnected: println("player disconnected") multiplayermatchstarted = false delegate?.matchended() case.stateunknown: println("initial player state") 57

58 Chapter 25: Performance: Texture Atlases The game invokes this method every time a player s status changes. There are two possible states: either the player is connected to the match or the player is disconnected from the match. In the first case, you check to see if all the players have joined in and then take some action. You ll learn more about that in the next section. In the second case, you simply end the match. That s it! You now have the entire machine in place to create a multiplayer game. Testing your matchmaking code In this section, you re going to test if all the matchmaking code you wrote in the GameKitHelper class works as expected. The first thing you need is a button on the home screen to launch a multiplayer game. We ve provided the image for this button in the Resources directory for this chapter. Simply drag the btn-multiplayer@2x.png file into your project: To add the multiplayer option to Circuit Racer, open Main.storyboard and add a new button to the HomeScreenViewController, below the Game Center button. As you did for the other buttons, set its type to Custom and delete its title. Set the image for the button to btn-multiplayer.png. Resize the button to 360x66 to match the size of the other buttons. As you did in the earlier Circuit Racer chapters, you need to set the constraints for this new button so that it fits on any device screen size. To do this, add the following four constraints by Ctrl-dragging from the new Multiplayer button, as shown: Ctrl+Drag from Ctrl+Drag to Menu option Constraint 1 Multiplayer button Multiplayer button Aspect Ratio Constraint 2 Multiplayer button Game Center button Constraint 3 Multiplayer button Game Center button Center X Equal widths Constraint 4 Multiplayer button Background image Center Y 58

59 Chapter 25: Performance: Texture Atlases For the three home screen buttons to fit on the screen, you need to change the Center Y constraint on each of them. Select the Play button and double-click on the Align Center Y constraint in the Size Inspector. Change the Multiplier value to This will move the button up slightly. Now select the Game Center button and double-click on its Align Center Y constraint. Change the Multiplier value to 1.5. Finally, select the new Multiplayer button and double-click on its Align Center Y constraint. Change the Constant value to 0, the Multiplier value to 1.85 and remember to check that this new constraint has the button as the First Item. Preview the results of adding the above constraints in the Assistant Editor. You ll notice that the buttons are placed very close to one another. To fix this, select the Play button and double-click the Proportional Width to: bg-main-menu.png constraint in the Size Inspector. Change the Multiplier value to 0.5 and the button will reduce in size, as shown below. 59

60 Chapter 25: Performance: Texture Atlases Now you need to hook up this new button to the HomeScreenViewController class. With the Assistant Editor open and displaying HomeScreenViewController.swift, Ctrl-drag from the Multiplayer button to HomeScreenViewController.swift. In the popup that appears, select Action for the Connection, UIButton for the Type and enter showmatchmakerviewcontroller for the Name. Click Connect to create the action that the game will call when the user touches the Multiplayer button. Next, open HomeScreenViewController.swift and implement the method as func showmatchmakerviewcontroller(sender: UIButton) { GameKitHelper.sharedInstance.findMatch(2, maxplayers: 2, presentingviewcontroller: self, delegate: self) Once again, Xcode tells you that your class doesn t conform to the required protocol. Modify the class declaration in HomeScreenViewController.swift to add the GameKitHelperDelegate protocol: class HomeScreenViewController: UIViewController, GameKitHelperDelegate 60

61 Chapter 25: Performance: Texture Atlases Next, implement the protocol methods as follows: func matchstarted() { println("match has started successfully") func matchended() { println("match has ended") func matchreceiveddata(match: GKMatch, data: NSData, fromplayer player: String) { In these methods, you only have log prints to show the current state, since for the moment, you re simply testing the GameKitHelper API. You also need to import the GameKit framework. Add this line at the top of the HomeScreenViewController class: import GameKit Before testing, make sure you have at least one physical device. Two devices are best, but if necessary, you can use the ios Simulator as the second device. You also need two Game Center accounts. Make sure you ve turned on sandbox mode in Game Center using the Settings app. Build and run to install the app on your device. Then build and run on a second device or in the Simulator. Finally, open the app on the first device so that you now have your new code running in both places, with one of them connected to Xcode so you ll be able to see its log messages in the console. On both devices, after you see the Game Center login banner, select the Multiplayer button. This will launch the GKMatchmakerViewController, as shown below. 61

62 Chapter 25: Performance: Texture Atlases Select the Play Now button to create a match between two players. This may take some time. Since you re using Game Center in sandbox mode, only your devices will be available for a match. You can t go cruising for n00bs yet! :] After Game Center creates the match, your game dismisses the GKMatchmakerViewController and you should see the following log on the console: Ready to start the match W00t this shows that Game Center has automatically created a match between the two players. At this point, the connection between the devices is active and you can start sending data between them using Game Center s built-in APIs and that s exactly what you ll do in the next section. But first, try sending the game to the background on one of your devices to see what happens. After a few minutes, you should see the following in the console on your other device: Player disconnected Match has ended This shows that when your game is in the background, the connection between the devices goes away and the match ends. Network coding strategy Now that you have all the code in place to create a multiplayer match, let s briefly return to talking strategy. 62

63 Chapter 25: Performance: Texture Atlases Assigning Player 1 The problem is, how do you decide which player gets which car? Another way to think about this is that you need to find a way to decide which player is Player 1. You re going to use the following strategy: As soon as the match begins, each side will generate a random number. You will consider the side that generates the bigger number to be Player 1. In the rare case that both players devices generate the same number, you will simply have them generate another number and try again. Being Player 1 has certain unavoidable advantages: It is this player s device that decides when the game starts and when it ends. Note: You can extrapolate the same strategy to support three or more players. In such a case, you would order the players in a descending fashion according to the random numbers they generate. Player aliases Since the game has two cars that will be onscreen at the same time, it could sometimes be difficult for the players to know which car belongs to which player. You can solve this problem by using player aliases, the nicknames chosen by every Game Center user. To get these, you simply query Game Center using the GKPlayer API, which you ll learn more about in the sections to come. You re going to place a player alias above each car, thus telling anyone watching the screen which player is in control of that car. Game states One tricky thing about developing multiplayer real-time games is that you have no control over the order in which things happen. For example, one side could have finished initializing the game, as well as sent the random number for player assignment, all before the other player has even started. 63

64 Chapter 25: Performance: Texture Atlases To keep track of things, you re going to store the current state of the game and only perform certain actions in certain states. On each player s side, the game will move logically from one state to the next, allowing you to keep the devices synchronized. The following diagram illustrates the states that Circuit Racer will have: Let s go through each state in detail: Waiting for match: The game is waiting for Game Center to return a match and look up the player aliases. Waiting for random number: Once the game has the match and the player aliases, it generates a random number and sends it to the other side(s). Then it moves into this state and waits for a random number from the other device(s), in turn. Waiting for start: Once the game receives a random number, it knows if it is Player 1 or Player 2. If the game is Player 2, it moves into this state and waits for Player 1 to send a start message. Playing game: Once Player 1 has sent a start message (or Player 2 receives it), the game moves to this state, which indicates that the game is active. In this state, the game sends data, such as the car positions, back and forth between the two players. Done: In this final state, the game ends and decides on a winner. That s it for the high-level plan let s see what it looks like in code! 64

65 Chapter 25: Performance: Texture Atlases Multiplayer game scene Currently, GameScene supports only a single car at a time. You need a modified version of this scene that supports multiple players. To keep this chapter s focus purely on Game Center, I ve created a starter project that includes refactored code and serves as a starting point for multiple players. You can find this in the CircuitRacer-After Multiplayer Game Scene folder open this in Xcode now. Let s go through all the changes: 1. Main.storyboard: The Multiplayer button on the home screen now sends a message to playmultiplayergame(sender) of the HomeScreenViewController. 2. HomeScreenViewController.swift: This view controller no longer implements the GameKitHelperDelegate protocol. Instead, the MultiplayerNetworking class will implement this (more on this later). 3. GameViewController.swift: a. There is a new variable, numberofcars. b. viewdidload() now checks the numberofcars variable. If it s greater than 1, then this is a multiplayer game, so the method creates a MultiplayerNetworking object. c. The code now sets the scene as the delegate of the MultiplayerNetworking object. d. Finally, the code now shows the GKMatchmakerViewController and sets the MultiplayerNetworking object as the delegate of the GameKitHelper object. 4. GameScene.swift: I ve made a ton of changes here to allow Circuit Racer to have multiple cars in the scene. 5. Set.swift: This is a nice helper class that you can use as a set to store items. It ensures that each element is unique and prevents duplicates. 6. MultiplayerNetworking.swift: This is a new class and will contain all the networking code for a multiplayer race. Think of it as separate module that will handle all the heavy lifting and will communicate with the scene through a protocol. 7. GameKitHelper.swift: This now invokes the matchstarted delegate method when all the players have joined the multiplayer match. Take a few moments to go through all the changes mentioned above. You ll notice that now the game scene has an array of cars. In case the user initializes the game in single-player mode, the array will have only one car, whereas in multiplayer mode, it will have two cars. With the segregation of the networking code, the game scene will only be responsible for gameplay and the networking engine (MultiplayerNetworking) will 65

66 Chapter 25: Performance: Texture Atlases inform the scene about the positions of the other cars. This way, you maintain a clean interface. Moving forward, you ll make changes to the MultiplayerNetworking class and add real-time multiplayer support. Before you proceed, let s try out the new project. But before you do, switch to your target settings and change the Bundle Identifier to whatever you chose for your App Bundle Identifier earlier: Also, do a clean build (Project\Clean) and delete your existing project from your devices before continuing, to make sure you start from a clean slate. Now build and run, and select the Multiplayer button on the home screen. The game will take you to a view controller that presents the GKMatchmakerViewController: To keep things simple, this bypasses the level and car selection screens that appear for a single-player game. 66

67 Chapter 25: Performance: Texture Atlases Adding networking code Your scene is set up and you have the ability to create a match. Now you need to add the networking code that will structure communication between each device. This is it the heart of your multiplayer implementation! Setting up the game states Remember the game states I mentioned? Add the following structure at the top of the MultiplayerNetworking class: enum GameState: Int { case WaitingForMatch, WaitingForRandomNumber, WaitingForStart, Playing, Done Define the message types and structures for each of the messages you ll be sending back and forth by adding the following: enum MessageType: Int { case RandomNumber, GameBegin, Move, LapComplete, GameOver struct Message { let messagetype: MessageType struct MessageRandomNumber { let message: Message let randomnumber: UInt32 struct MessageGameBegin { let message: Message struct MessageMove { let message: Message let dx: Float let dy: Float let rotate: Float struct MessageLapComplete { let message: Message 67

68 Chapter 25: Performance: Texture Atlases struct MessageGameOver { let message: Message Each structure represents a type of message your game will send to the other device. Notice that each message structure has a message type. This is so that when you receive data, you can safely cast the data into a Message struct, since that s the first part of every struct. Then, using the messagetype value, you can determine the type of message sent. Next, add the following variables to the MultiplayerNetworking class. var ourrandomnumber: UInt32 var gamestate: GameState var isplayer1: Bool var receivedallrandomnumbers: Bool var orderofplayers: [RandomNumberDetails] These variables keep track of the random number for the local device and the ones received from the other player(s). You ll use these numbers to sort all of the players in the game and determine playing order more on this in the next section. You ve declared the orderofplayers variable to be an array of type RandomNumberDetails. You need to define this class, and since these variables aren t optional, you need to initialize them in init(). Add the following to the class: class RandomNumberDetails: NSObject { let playerid: String let randomnumber: UInt32 init(playerid: String, randomnumber: UInt32) { self.playerid = playerid self.randomnumber = randomnumber super.init() override func isequal(object: AnyObject?) -> Bool { let randomnumberdetails = object as? RandomNumberDetails return randomnumberdetails?.playerid == self.playerid override init() { ourrandomnumber = arc4random() gamestate = GameState.WaitingForMatch isplayer1 = false 68

69 Chapter 25: Performance: Texture Atlases receivedallrandomnumbers = false orderofplayers = [RandomNumberDetails]() super.init() The RandomNumberDetails class keeps track of the playerid and the randomnumber generated by that player s device. This helps you know which player generated which number, which is crucial to the player ordering process. In init(), you initialize the variables with appropriate values. ourrandomnumber stores the random number for the local player, which you initialize using the famous arc4random() method. Sending data and starting the game Begin to implement the networking code by first replacing matchstarted() and adding two new stub methods: func matchstarted() { println("match has started successfuly") if receivedallrandomnumbers { gamestate = GameState.WaitingForStart else { gamestate = GameState.WaitingForRandomNumber sendrandomnumber() trystartgame() func sendrandomnumber() { func trystartgame() { matchstarted() first checks if the game has received random numbers from all players. If it has, it moves the game state to waiting for start. Next, add a method to send data to other players in the match: func senddata(data: NSData) { var senddataerror: NSError? let gamekithelper = GameKitHelper.sharedInstance 69

70 Chapter 25: Performance: Texture Atlases if let multiplayermatch = gamekithelper.multiplayermatch { let success = multiplayermatch.senddatatoallplayers(data, withdatamode:.reliable, error: &senddataerror) if!success { if let error = senddataerror { println("error:\(error.localizeddescription)") matchended() To send data across the network with Game Center, all you have to do is call senddatatoallplayers(data, withdatamode, error) on the match object, as you do above. Using GKMatchSendDataMode.Reliable means that the data you send is guaranteed to arrive at its destination (as long as the network connection stays alive), at the cost of slower delivery times compared to GKMatchSendDataMode.Unreliable. Now that you have the power to send data, add the following code to sendrandomnumber(): var message = MessageRandomNumber(message: Message(messageType: MessageType.RandomNumber), randomnumber: ourrandomnumber) let data = NSData(bytes: &message, length: sizeof(messagerandomnumber)) senddata(data) The above code creates a structure of type MessageRandomNumber and assigns the random number created on initialization to the message. It then sends the message to the other player(s). Using the same technique, you need to add methods to send messages of different types. Naturally, let s begin with the begin game message. For this, add the new method shown below: func sendbegingame() { var message = MessageGameBegin(message: Message(messageType: MessageType.GameBegin)) let data = NSData(bytes: &message, length: sizeof(messagegamebegin)) senddata(data) And add the following code to trystartgame: 70

71 Chapter 25: Performance: Texture Atlases if isplayer1 && gamestate == GameState.WaitingForStart { gamestate = GameState.Playing sendbegingame() This method checks if the local player is Player 1. If so, it moves the game state to playing game and sends a MessageGameBegin message. Remember, only Player 1 can send this message. Build and run on two devices, and create a match as usual. When you re done, check the console for any errors. If everything goes well, you should see the following message on the console: Ready to start the match Match has started successfully Receiving data Until now, you ve been adding code to send data to other players in the match. Let s handle the other part of the equation: receiving data. Incoming random number messages Still in MultiplayerNetworking.swift, add the following stub methods, which you ll fill out in just a minute: func processreceivedrandomnumber(randomnumberdetails: RandomNumberDetails) { func islocalplayerplayer1() -> Bool { return false Modify matchreceiveddata(match, data, player) to handle the random number message by adding the following code: //1 var message = UnsafePointer<Message>(data.bytes).memory if message.messagetype == MessageType.RandomNumber { let messagerandomnumber = UnsafePointer<MessageRandomNumber>(data.bytes).memory println("received random number:\(messagerandomnumber.randomnumber)") 71

72 Chapter 25: Performance: Texture Atlases var tie = false if messagerandomnumber.randomnumber == ourrandomnumber { //2 println("tie") tie = true var idx: Int? for (index, randomnumberdetails) in enumerate(orderofplayers) { if randomnumberdetails.randomnumber == ourrandomnumber { idx = index break if let validindex = idx { ourrandomnumber = arc4random() orderofplayers.removeatindex(validindex) orderofplayers.append(randomnumberdetails(playerid: GKLocalPlayer.localPlayer().playerID, randomnumber:ourrandomnumber)) sendrandomnumber() else { //3 processreceivedrandomnumber(randomnumberdetails(playerid: player, randomnumber: messagerandomnumber.randomnumber)) //4 if receivedallrandomnumbers { isplayer1 = islocalplayerplayer1() if!tie && receivedallrandomnumbers { //5 if gamestate == GameState.WaitingForRandomNumber { gamestate = GameState.WaitingForStart trystartgame() 72

73 Chapter 25: Performance: Texture Atlases That s a long method, and there s much more to come, so let s take it one step at a time: 1. You cast the received data into a Message struct. Using the messagetype field, you check for the type of Message received. For now, the method only handles random number messages. 2. You compare the number with the locally-generated number, and if it s a tie meaning the numbers are identical you send another random number. (And immediately go play the lottery!) You also update the orderofplayers array with the newly-generated random number. 3. If the received number isn t equal to the one generated locally, the method creates an object of the RandomNumberDetails class, which stores the random number and the player ID of the player who sent it. 4. When you ve received all random numbers and determined the order of play, the receivedallrandomnumber variable will be true. In this case, you check if the local player is Player Finally, if there was no tie and you ve received all random numbers, you advance the game state and call trystartgame, which makes Player 1 initiate the game by sending the GameBegin message. You now have all the code in place to handle incoming random number messages. However, you still need to add code to process the received numbers and arrange all the players in order. Let s do that now. Ordering the players Add the following to the bottom of the initializer of MultiplayerNetworking, above the call to super.init(): orderofplayers.append(randomnumberdetails(playerid: GKLocalPlayer.localPlayer().playerID, randomnumber: ourrandomnumber)) The orderofplayers array will store each player s details along with their random number. Later, you ll sort this array on the basis of the stored random number, and that will be the order of players in the game. Add the following to processreceivedrandomnumber(randomnumberdetails): //1 let mutablearray = NSMutableArray(array:orderOfPlayers) mutablearray.addobject(randomnumberdetails) //2 let sortbyrandomnumber = NSSortDescriptor(key: "randomnumber", ascending: false) let sortdescriptors = [sortbyrandomnumber] mutablearray.sortusingdescriptors(sortdescriptors) 73

74 Chapter 25: Performance: Texture Atlases //3 orderofplayers = NSArray(array: mutablearray) as [RandomNumberDetails] //4 if allrandomnumbersarereceived() { receivedallrandomnumbers = true Here is a brief explanation: 1. The method first adds the received data to the orderofplayers array. 2. It then sorts the orderofplayers array on the basis of the random number generated by each player. 3. Lastly, in case you ve received all the random number messages, the method sets the receivedallrandomnumbers Boolean variable to true. At this point, Xcode will show an error saying that allrandomnumbersarereceived() is unresolved. Let s fix that now. Add the following method: func allrandomnumbersarereceived() -> Bool { var receivedrandomnumbers = Set<UInt32>() for playerdetail in orderofplayers { receivedrandomnumbers.insert(playerdetail.randomnumber) if let multiplayermatch = GameKitHelper.sharedInstance.multiplayerMatch { if receivedrandomnumbers.count == multiplayermatch.playerids.count + 1 { return true return false This is a helper method that will return a Boolean value that will be true if unique random numbers have been received from all players. Now, replace islocalplayerplayer1 with the following implementation: func islocalplayerplayer1() -> Bool { let playerdetail = orderofplayers[0] if playerdetail.playerid == GKLocalPlayer.localPlayer().playerID { 74

75 Chapter 25: Performance: Texture Atlases println("i'm player 1.. w00t :]") return true return false The above method is also a helper method that will return a Boolean value. This value is true if the local player is Player 1. Build and run, and now when the game initiates a match, each device will send out a random number and use that data to name one of the devices as Player 1. The console output for Player 1 will show the following logs (note that the ordering may be different for you due to network timing): Ready to start the match Match has started successfuly Received random number: I'm player 1.. w00t :] Receiving gameplay messages The next message type to handle is the begin game message. Append the following code snippet to matchreceiveddata(match, data, fromplayer): else if message.messagetype == MessageType.GameBegin { gamestate = GameState.Playing Remember that only Player 1 can send out the begin game message, so when Player 2 s device receives this message, its game state moves to Playing. When the device receives a move message from another player, you need to change the position of that player s car. Now, since the GameScene class is responsible for gameplay, the MultiplayerNetworking class needs to tell the scene to move that car more on this in the next section. For the moment, add the following to the same method to handle move messages: else if message.messagetype == MessageType.Move { let messagemove = UnsafePointer<MessageMove>(data.bytes).memory println("dx: \(messagemove.dx) Dy: \(messagemove.dy) Rotation: \(messagemove.rotate)") Next, you ll handle the lap complete message, which tells other players the number of laps the local player has completed around the track. Later, you ll use this information to determine a winner. 75

76 Chapter 25: Performance: Texture Atlases To keep track of the number of laps each player has completed, declare a variable, as shown below: var lapcompleteinformation: Dictionary<String, Int> Initialize the above variable in init(), as shown below: lapcompleteinformation = Dictionary<String, Int>() Next, add the following helper method: func setuplapcompleteinformation() { if let multiplayermatch = GameKitHelper.sharedInstance.multiplayerMatch { let playerids = multiplayermatch.playerids as [String] for playerid in playerids { lapcompleteinformation[playerid] = nooflaps lapcompleteinformation[gklocalplayer.localplayer().playerid] = nooflaps When the match starts, all players will need to complete the max number of laps; hence, the method stores the player ID and total laps (five, in your case). When a player sends out a lap complete message, the number of laps for that player will reduce until someone reaches zero. Add a call to the above method at the end of matchstarted: setuplapcompleteinformation() When you receive a lap complete message, you need to reduce the number of laps left for that player by 1. Add a helper method that does just this, as follows: func reducenumberoflapsforplayer(playerid: String) { if let laps = lapcompleteinformation[playerid] { lapcompleteinformation[playerid] = laps - 1 println("reduced laps:\(laps - 1)") You now have everything in place to handle the lap complete message, so add the following to the end of matchdidreceivedata(match, fromplayer): 76

77 Chapter 25: Performance: Texture Atlases else if message.messagetype == MessageType.LapComplete { reducenumberoflapsforplayer(player) There s just one more message type to handle: game over. Append the following to the same method: else if message.messagetype == MessageType.GameOver { You ll add code to the above condition in the next section. For now, the MultiplayerNetworking class can send and receive all types of messages. You re getting close to a full-fledged multiplayer game. Hurray! Handling movement data You still need to add code to move the cars. Don t worry you re getting there. :] Since the MultiplayerNetworking class is responsible for deciding which car belongs to which player, you need to add a method to the MultiplayerProtocol that will inform the implementing object, which in your case is GameScene, of the car that belongs to the local player. Add the following method declaration to the MultiplayerProtocol in MultiplayerNetworking.swift: func setcurrentplayerindex(index :Int) Next, modify trystartgame() by adding the following directly below sendbegingame(), inside the if condition: //first player delegate?.setcurrentplayerindex(0) The above code will inform the scene that the local player is Player 1. But what if the local player is not Player 1? In that case, you need to find out the index of the local player and send that to the scene. Add the following helper methods to do just that: func indexforlocalplayer() -> Int? { return indexforplayer(gklocalplayer.localplayer().playerid) func indexforplayer(playerid: String) -> Int? { var idx: Int? 77

78 Chapter 25: Performance: Texture Atlases for (index, playerdetail) in enumerate(orderofplayers) { let pid = playerdetail.playerid if pid == playerid { idx = index break return idx These methods simply find the index of a player based on the player s ID. Next, append the following code to the section of matchreceiveddata(match, data, player) that handles messages of type GameBegin: if let localplayerindex = indexforlocalplayer() { delegate?.setcurrentplayerindex(localplayerindex) Try to compile, and Xcode will complain that the GameScene class doesn t conform to the MultiplayerProtocol protocol. Fix that now by opening GameScene.swift and adding setcurrentplayerindex(index), as shown: func setcurrentplayerindex(index :Int) { currentindex = index Build and run, and tap the Multiplayer button on the home screen. Once Game Center creates a match, you ll see that the game assigns each player a car according to the random number generated by that player s device. But try to move one of the cars. You ll see that when a car moves, the game doesn t update its position on the other device. 78

79 Chapter 25: Performance: Texture Atlases Let s add code to send each player s move to the other device. Add the following method to MultiplayerNetworking.swift: func sendmove(dx: Float, dy: Float, rotation: Float) { var messagemove = MessageMove(message: Message(messageType: MessageType.Move), dx: dx, dy: dy, rotate: rotation) let data = NSData(bytes: &messagemove, length: sizeof(messagemove)) senddata(data) Now that you can send the position of the local player s car as it moves around the track, add the following code at the end of the if condition in analogcontrolpositionchanged(analogcontrol) in GameScene.swift: networkingengine?.sendmove(float(car.physicsbody!.velocity.dx), dy: Float(car.physicsBody!.velocity.dy), rotation: Float(car.zRotation)) This sends the velocity and rotation of the local player s car to the other players in the match. Earlier, while writing matchreceiveddata(match, data, fromplayer), you added a log statement to print the values received if the message is of type Move. Build and run the game on two devices to test if the data gets through. The console will show you statements like the one below: Dx: Dy: Rotation:

80 Chapter 25: Performance: Texture Atlases At this point, you are sending and receiving position and rotation values for each player. Now you need to use the received data to change the position of the opponent s car. To do that, define the following method declaration in MultiplayerProtocol in MultiplayerNetworking.swift: func setpositionofcar(index: Int, dx: Float, dy: Float, rotation: Float) Next, in the same file, add the following line of code at the end of the section of matchreceiveddata(match, data, fromplayer) that handles move messages: delegate?.setpositionofcar(indexforplayer(player)!, dx: messagemove.dx, dy: messagemove.dy, rotation: messagemove.rotate) Add the implementation of the above method in GameScene.swift, as shown below: func setpositionofcar(index: Int, dx: Float, dy: Float, rotation: Float) { let car = cars[index] as SKSpriteNode car.physicsbody?.velocity = CGVector(dx: CGFloat(dx), dy: CGFloat(dy)) if rotation!= 0 { car.zrotation = CGFloat(rotation) Build and run. As you drive around the track on one device, you ll see the position of your car update on the other device. 80

81 Chapter 25: Performance: Texture Atlases Take a break and play with the game. Race against yourself if you have to one hand on each device! No cheating. :] Ending the match OK! It s time to add a game over condition and determine the game s winner. For this, you first need to send a lap complete message when the player completes a lap. Add the following method to MultiplayerNetworking.swift: func sendlapcomplete() { var lapcompletemessage = MessageLapComplete(message: Message(messageType: MessageType.LapComplete)) let data = NSData(bytes: &lapcompletemessage, length: sizeof(messagelapcomplete)) senddata(data) reducenumberoflapsforplayer(gklocalplayer.localplayer().playerid) Open GameScene.swift and add the following code in the section of update(currenttime) that checks the lap over condition. Place it below the code that plays the lapsoundaction: networkingengine?.sendlapcomplete() With that in place, switch to MultiplayerNetworking.swift and add the following method: func isgameover() -> Bool { for (playerid, numberoflaps) in lapcompleteinformation { if numberoflaps == 0 { return true 81

82 Chapter 25: Performance: Texture Atlases return false The above method checks the lapcompleteinformation dictionary to see if any player in the match has completed all laps. According to your strategy, only Player 1 can send out the game over message. Let s first add a method to send this message. Add the following method below isgameover(): func sendgameovermessage() { var gameovermessage = MessageGameOver(message: Message(messageType: MessageType.GameOver)) let data = NSData(bytes: &gameovermessage, length: sizeof(messagegameover)) senddata(data) Along with the above method, you also need a way to determine if the local player has won. Since you store the number of laps each player has completed, you can use that information to check for a winner. Add the following helper methods below sendgameovermessage(): func haslocalplayerwon() -> Bool { let winningindex = indexforwinningplayer() if let index = winningindex { let playerdetails = orderofplayers[index] if playerdetails.playerid == GKLocalPlayer.localPlayer().playerID { return true return false func indexforwinningplayer() -> Int? { var winningplayerid: String? for (playerid, numberoflaps) in lapcompleteinformation { if numberoflaps == 0 { winningplayerid = playerid 82

83 Chapter 25: Performance: Texture Atlases break if let playerid = winningplayerid { return indexforplayer(playerid) return nil Using the above methods, you can easily determine who has won the game. However, you need a way to tell the game scene about the results. For this, add the following method declaration to the MultiplayerProtocol: func gameover(didlocalplayerwin: Bool) There are two times you need to check the game over condition: 1. When the game sends a lap complete message. 2. When the game receives a lap complete message. Append the following code to sendlapcomplete(): if isgameover() && isplayer1 { sendgameovermessage() delegate?.gameover(haslocalplayerwon()) The above piece of code checks if the game is over, and if the local player is Player 1, it sends out the game over message. Also, using the new gameover(didlocalplayerwin) method, the code notifies the delegate that the game is over. Now add the very same code to the end of the section of matchreceiveddata(match, data, fromplayer) that handles the lap complete message. If the local player receives a game over message, the networking engine should inform the scene that the game is complete. Add the following code to the section of matchreceiveddata(match, data, fromplayer) that handles the game over message: delegate?.gameover(haslocalplayerwon()) Phew! This is a coding marathon. There s just one piece left. Open GameScene.swift define the gameover(didlocalplayerwin) method of the MultiplayerProtocol, as shown below: func gameover(didlocalplayerwin: Bool) { paused = true 83

84 Chapter 25: Performance: Texture Atlases gameoverblock?(didwin: didlocalplayerwin) For a finishing touch, add the following code to the top of matchended() in MultiplayerNetworking.swift: GameKitHelper.sharedInstance.multiplayerMatch?.disconnect() In case of any network errors, the above code will disconnect the local player from the multiplayer game. Build and run the game on two devices. Now you can play a full-fledged, twoplayer, real-time racing game! :] Congratulations you ve made a multiplayer game with Game Center and Sprite Kit! If you just wanted to learn the basics, feel free to stop reading here and skip to the next chapter. But if you want to add a little polish and get some extra practice, keep reading! Displaying player aliases You ve been tooling around with your fully-functional multiplayer game, maybe trying to rope a friend or family member into a match. During your testing, you may have come across one problem: It s sometimes difficult to understand which car belongs to which player, since the players are selected randomly. If you look at the code, Player 1 will always be the yellow car and Player 2 the blue one, but as a player, you won t always be sure at the outset whether you are Player 1 or Player 2! 84

85 Chapter 25: Performance: Texture Atlases Your strategy calls for displaying a player alias above each car, so get to it! The first step is to retrieve details about all the players in the match, so let s start there. Retrieving player data You re going to adopt the strategy of looking up the details of all the players in the match right before the match starts. Only after you ve done that will you call matchstarted(). Open GameKitHelper.swift and add a variable to keep track of all player details: lazy var playerdetails: Dictionary<String, GKPlayer> = { return Dictionary<String, GKPlayer>() () Next, add the following method: func lookupplayersofmatch(match: GKMatch!) { println("looking up \(match.playerids.count) players") GKPlayer.loadPlayersForIdentifiers(match.playerIDs) {(players, error) in if error!= nil { println("error: \(error.localizeddescription)") self.multiplayermatchstarted = false self.delegate?.matchended() else { for player in players as [GKPlayer] { println("found player: \(player.alias)") self.playerdetails[player.playerid] = player self.playerdetails[gklocalplayer.localplayer().playerid] = GKLocalPlayer.localPlayer() self.multiplayermatchstarted = true self.delegate?.matchstarted() This method looks up the details of all the players in the match. It calls the GKPlayer method loadplayersforidentifiers(identifiers, withcompletionhandler), which returns player details in an array. If there s an error, you log it to the console and send a matchended message through the delegate. If there s no error, the method stores all the players in the playerdetails variable and sends a matchstarted() message. 85

86 Chapter 25: Performance: Texture Atlases Inside match(match, player, didchangestate), change the GKPlayerConnectionState.StateConnected switch case to the following: println("player connected") if!multiplayermatchstarted && multiplayermatch?.expectedplayercount == 0 { println("ready to start the match"); lookupplayersofmatch(multiplayermatch) Likewise, replace the code inside matchmakerviewcontroller(viewcontroller, didfindmatch) with the following: presentingviewcontroller?.dismissviewcontrolleranimated(true, completion: nil) multiplayermatch = match multiplayermatch!.delegate = self if!multiplayermatchstarted && multiplayermatch?.expectedplayercount == 0 { println("ready to start the match") lookupplayersofmatch(multiplayermatch) Build and run the game on two different devices, or on one device plus the Simulator. Just as you did before, select the Multiplayer button on the home screen in both apps. You should now see logs in the console similar to the following: Ready to start the match Looking up 1 players Found player: Olieh Match has started successfuly Received random number: I'm player 1.. w00t :] Now that you have all the information about the players, you need a way to map each player alias to a specific car. The strategy you used in MultiplayerNetworking was to sort all of the players in an order that matches the cars they are driving. Using the same approach, you ll add code to create an array that has all player aliases in the same order. Add the following method to MultiplayerNetworking.swift: func retrieveallplayeraliases() { var playeraliases = [String]() 86

87 Chapter 25: Performance: Texture Atlases for playerdetail in orderofplayers { let playerid = playerdetail.playerid if let player = GameKitHelper.sharedInstance.playerDetails[playerId] { playeraliases.append(player.alias) The above method uses the player IDs of all the players in the match to retrieve their aliases from GameKitHelper. You should invoke this method after you ve determined the order of players, which means when the game starts. Add the following code at the end of sendbegingame() and also at the start of the section of matchreceiveddata(match, data, fromplayer) that handles the game begin message: retrieveallplayeraliases() Now that all player aliases are in order, you simply have to notify the game scene about these aliases, and the game scene will be responsible for drawing them above each car. Add the following method declaration to the MultiplayerProtocol: func setplayerlabelsinorder(playeraliases: [String]) Now add code to invoke the above method on the delegate object. Add the following to the end of retreiveallplayeraliases(): delegate?.setplayerlabelsinorder(playeraliases) You re all set with the networking engine, so let s switch to the game scene. Open GameScene.swift and add the following helper method: func addplayerlabel(text: String, atposition position: CGPoint) -> SKLabelNode { println("adding player with label: \(text)") let label = SKLabelNode(fontNamed: "Marker Felt") label.position = position label.fontsize = 72 label.fontcolor = SKColor.whiteColor() label.text = text addchild(label) return label 87

88 Chapter 25: Performance: Texture Atlases The above method creates an SKLabelNode object and adds it to the game scene. The text and position for the label are provided as method parameters. You need to add a variable to keep track of all player aliases in GameScene. Declare a lazy array as shown below: lazy var playerlabels: [SKLabelNode] = { return [SKLabelNode]() () The playerlabels variable will hold references to all the SKLabelNodes in the scene. Finally, you have to implement the setplayerlabelsinorder(playeraliases) method of the MultiplayerProtocol. func setplayerlabelsinorder(playeraliases: [String]) { for (index, playeralias) in enumerate(playeraliases.generate()) { let car = cars[index] let label = addplayerlabel(playeralias, atposition:cgpoint(x: car.position.x, y: car.position.y + 120)) playerlabels.append(label) Build and run the game. When the game starts, you ll see a player alias above each car corresponding to the car s driver. Here is an image that shows this in action: 88

89 Chapter 25: Performance: Texture Atlases The labels take some time to show up because it takes time for the game to decide who is Player 1. As soon as that s done, the game displays the aliases onscreen. You ll notice the labels don t follow the cars. Let s fix this now. Add the following code to the end of update(currenttime) in GameScene.swift: if ismultiplayermode { for (index, label) in enumerate(playerlabels) { label.position = CGPoint(x: cars[index].position.x, y: cars[index].position.y + 120) This simply changes the position of the label to match the position of the appropriate car. Build and run the game. The labels now move along with their respective cars and with that, you re done with the multiplayer version of Circuit Racer! That must mean it s time for a couple of challenges. Challenges This time, there are two challenges to give you extra practice using the Game Center multiplayer APIs. As always, you can find the solutions in the resources for this chapter, but give it your best shot first! Challenge 1: Display player photos In the last section of this chapter, you added player aliases to each car. You can also retrieve a player s Game Center photo using similar APIs. Your challenge is to do just that, then add code to display each photo above its respective car. Follow these simple instructions to implement this on your own: 1. Similar to the way you implemented the retrieval of player aliases, write a method in MultiplayerNetworking.swift that will retrieve player photos. Call this method retrieveallplayerphotos(). This method will loop over all the player IDs in orderofplayers and retrieve the photo using loadphotoforsize(size, withcompletionhandler). Since the method to download the photo is asynchronous every time the game invokes the success handler, check to see if you have all the photos. 89

90 Chapter 25: Performance: Texture Atlases Note: The image retrieved from Game Center can sometimes be nil. This happens when the user has explicitly deleted her image from her Game Center profile. In this case, use the in the Resources folder as a placeholder. 2. Invoke this new method when the game sends or receives the begin game message. This way, you ensure all the photos match the order of the cars. 3. Add a method declaration in MultiplayerProtocol to notify the scene about the downloaded photos. func setplayerphotosinorder(playerphotos: [UIImage]) 4. Once you ve downloaded all the photos, invoke the above method on the delegate. 5. Open GameScene.swift and implement setplayerphotosinorder(playerphotos). 6. Add a variable to store all player photos in GameScene.swift: lazy var playerphotos: [SKSpriteNode] = { return [SKSpriteNode]() () 7. Similar to addplayerlabel(text, atposition), create another helper method that will add the player s photo to the scene. This method should ideally have a signature like the one shown below: func addplayerphotoswithtexture(texture: SKTexture, atposition position: CGPoint) -> SKSpriteNode 8. Fill out the setplayerphotosinorder(playerphotos) method of the MultiplayerProtocol. This method will go through all the images, add them to the scene using the helper method you added above and then add the photo nodes to the playerphotos array. This is very similar to setplayerlabelsinorder(playeraliases). 9. At this point, when you build and run the game you should see a photo of each player above his or her respective car. However, the photos don t move with the cars and are really big in size. To fix this, set a size for the photo node in addplayerphotowithtexture(texture, atposition) and add the following code below the loop that changes the position of the player aliases in update(currenttime). for (index, photo) in enumerate(playerphotomanager.playerphotos) { photo.position = CGPoint(x: cars[index].position.x, y: cars[index].position.y + 280) 90

91 Chapter 25: Performance: Texture Atlases Build and run the game. Your game scene will now show you photos of all the players in the match. Challenge 2: Honk at other players This will be a fun challenge. When a player taps on the screen, you want the game to play a horn sound on all the other players devices. Follow these simple instructions to implement this on you own: 1. First, you need a way to detect a tap on the scene. For this, open GameViewController.swift and add a UITapGestureRecognizer variable: var taprecognizer: UITapGestureRecognizer! 2. Initialize the above variable at the end of viewdidload(): taprecognizer = UITapGestureRecognizer(target: self, action: Selector("tapDetected")) view.addgesturerecognizer(taprecognizer) 3. Add an empty selector that the game will call when it detects a tap: func tapdetected() { 4. Open GameScene.swift and define the empty method shown below: func tap() { 91

92 Chapter 25: Performance: Texture Atlases 5. Switch to GameViewContoller.swift and modify tapdetected() as shown below: func tapdetected() { let skview = view as SKView let gamescene = skview.scene as GameScene gamescene.tap() 6. Open MultiplayerNetworking.swift and define a new type of message: struct MessageHonk { let message: Message 7. Also define this new message type in the MessageType enum. Name it Honk. 8. Add a method to the implementation section to send a message of type MessageHonk. This should be quite easy to do since you ve already written similar methods take a look at sendgameover(). Make sure you name the method sendhonkmessage(). 9. Open GameScene.swift and fill in the tap method: func tap() { networkingengine?.sendhonkmessage() 10. Your game now has the ability to send a honk message, so let s take care of the receiving part. Add the following method to MultiplayerProtocol: func playhorn() 11. Open MultiplayerNetworking.swift and add an else condition to matchreceiveddata(match, data, player) to handle the honk message: else if message.messagetype == MessageType.Honk { delegate?.playhorn() 12. Open GameScene.swift and implement playhorn() of MultiplayerProtocol: func playhorn() { runaction(hornsoundaction) Build and run. When you tap the screen on any device, all other devices participating in the match will play a horn sound. Cool! 92

93 Chapter 25: Performance: Texture Atlases At this point, you ve acquired the skills and concepts you need to go about supporting multiple players in your own games. You can reuse the GameKitHelper class as well as parts of the MultiplayerNetworking class to employ many of the strategies you learned in this chapter. So what are you waiting for? 93

94 Chapter 26: Performance: Texture Atlases By Rod Strougo A great game needs more than just fun and polish it also needs to perform well. Customers expect a smooth frame rate while they play. Nothing breaks the player s concentration like lag or a mid-game stutter! One of the most important performance optimizations you can make to your game is to use texture atlases. As you may have noticed, you ve already been using texture atlases for most of the games in this book! Starting with Cat Nap back in Chapter 9, Intermediate Physics, you put your sprites inside a folder that ends with.atlas. Behind the scenes, Sprite Kit generated a texture atlas for you that looked something like this: Although you learned that using texture atlases is good practice, you didn t learn why. You also didn t learn about some neat optimizations you can make to your texture atlases good practice on top of good practice all of which adds up to your game using less memory and running faster.

95 Chapter 26: Performance: Texture Atlases In this chapter, you ll take a deeper look at texture atlases and exactly why it s important to use them. You ll also learn how you can save memory in your game by choosing the proper pixel format for your images, as well as how you can avoid some common pitfalls with texture atlases. Let s begin by making the case for texture atlases and examining their two major advantages. Advantage 1: Use less memory The first benefit of using texture atlases is that it decreases your game s memory usage. Memory is a precious resource on ios devices. The iphone 6, 6 Plus, and 5S only have 1GB memory, with older models having even less, while a typical Mac Mini has 4GB+. Reducing the amount of memory your app uses is important for several reasons: Increase performance. After flash storage, accessing memory is the second slowest part of the hardware. The more you can reduce memory requirements, the faster your app will run. Keep your app from crashing. If your game has a lot of images and textures, it s quite possible for your app to use more memory than ios currently has available. ios will try its best to terminate other apps to make up the difference, but if that fails ios may terminate your app due to insufficient memory. To customers, this will look like a crash, so is definitely something you want to avoid. Keep your app running in the background. When memory gets low, ios terminates apps that are running in the background, beginning with the largest memory hogs. If you decrease your memory requirements, you decrease the chances of ios terminating your app when it s in the background. This makes your customers happy because your app appears to launch faster, and will resume exactly where you left off without having to relaunch. The biggest use of memory in your games by far will be textures. Therefore, when it comes to reducing your memory load, this is the first area you should address. Determining memory costs of textures The first question to ask yourself when considering your game s memory load is, How much memory are my textures using? You might think that s as simple as looking at the size of your images in Finder, but unfortunately that s not the case. The GPUs on ios devices can only handle a special format called PVRTC natively. Every other image format must be stored uncompressed for the GPU to be able to render it. As an example, a 1420x640 pixel, all-green PNG background takes up a measly 57KB on flash storage, but once loaded into memory it consumes 3,640KB! 95

96 Chapter 26: Performance: Texture Atlases Each image has a pixel format, which specifies how many bits track each color for a pixel. How many bits you use per pixel is what determines whether your image supports 8, 16, 256, thousands or millions of colors. The more bits per pixel, the larger the number of colors that you can store in an image, and generally the better the image looks. The ios GPU supports millions of colors and the highest quality pixel format is called RGBA8888. This means each pixel has 32 bits 8 bits each for the Red, Green, Blue and Alpha values. By default, Sprite Kit creates textures and texture atlases in this format, but later in this chapter you ll learn how you can specify lower quality formats if you re willing to sacrifice quality in return for memory savings. The simple formula you can use to determine how much memory a particular image uses is: (Length x Width x Bits per Pixel)/8 = Bytes in RAM. For example, ios will uncompress a 128x128 image to (128 x 128 x 32) / 8 = 65K bytes in memory. Why texture atlases help Now that you know how to calculate the memory cost of textures, let s see why texture atlases help save memory. Imagine you wanted to use these four 161x161 images in Cat Nap: If you added these images to your game individually rather than in an.atlas folder, using the formula above, ios will uncompress each image to (161 x 161 x 32) / 8 = ~101K in memory, or ~405K for all four images. If you put these sprites in an.atlas folder instead, Sprite Kit will automatically generate a texture atlas for you. A texture atlas is just a single larger image containing all of the smaller images inside it, as well as instructions for how to extract each of the sub-images. Here is what it might look like for the cat images above: 96

97 Chapter 26: Performance: Texture Atlases In this example, Sprite Kit generates an image that is 277x309 pixels. Using the formula above, ios will uncompress this to (277x 309 x 32) / 8 = ~334K in memory. This is 71K in savings! The best part is usually the more images you pack into a texture atlas, the more memory savings you get. Advantage 2: Run faster The second advantage of using texture atlases is that it makes your game run faster above and beyond the performance boost you get from Advantage 1, using less memory. Sprite Kit is built on top of the OpenGL ES graphics API. To draw a texture on the screen with OpenGL, there are two steps: 1. Bind the texture. First OpenGL needs to make the texture you want to use active. This is called binding the texture, and it s an expensive operation. 2. Draw the texture. Then OpenGL draws a portion of the texture (i.e. the portion that contains the sprite image) to a portion of the screen (i.e. where the sprite is). This is also an expensive operation. If you aren t using texture atlases and are just using sprites directly, every time you draw a sprite you have to perform both of these steps. Here is what the bind and draw calls would look like for a game with three images: 97

98 Chapter 26: Performance: Texture Atlases There would be a total of six calls, three bind calls and three draw calls interleaved. Since both of these calls are expensive, once you have a non-trivial number of sprites in your game you will quickly see your game s frame rate drop as the GPU spends more and more time binding and drawing the individual textures. Why texture atlases help Since texture atlases combine all of your sprites into a single larger image, you only need about one bind call and one draw call total for all the sprites that are part of the texture atlas. The exact amount of bind and draw calls can vary depending on your usage of texture atlases and how Sprite Kit optimizes things behind the scenes. Here is what the bind and draw calls would look like if you were to use a texture atlas for this three-image game: 98

99 Chapter 26: Performance: Texture Atlases Since all of the sprites are using the same texture atlas, OpenGL only needs to bind the texture once. And there is also only one call to draw textures, since OpenGL can draw the three different sub-parts of the texture to the screen in one pass. With fewer OpenGL calls, your game runs faster. You might not notice a big difference if you only have a small number of sprites, but the more sprites your game has, the more important this becomes. Introducing PerformanceTest Throughout this chapter you will be using Xcode projects provided for you. You can find them in the resources folder for this chapter. To see the performance benefits of texture atlases for yourself, open the PerformanceTest-NoTextureAtlas project in Xcode. Build and run on an actual ios device it is very important to do performance testing on an actual ios device rather than the simulator in order to get accurate results. You should see something like the following: 99

100 Chapter 26: Performance: Texture Atlases This is a very simple app that displays 500 sprites to the middle of the screen. This version uses individual images for every sprite, and you can see that it is getting about 8 FPS on my iphone 5. Newer devices, like the iphone 5S, will produce higher FPS values, and may get close to the 60 FPS max. If you are seeing 60 FPS on your device, then increase the number of nodes in the code, by changing the MaxNumberOfShips variable at the top of GameScene.swift until you see an impact on the FPS. Next, open the PerformanceTest-RGBA8888 project. This is the same exact project, but it uses a texture atlas (AllElements.atlas) instead of individual sprites. If you changed the MaxNumberOfShips value in the last project, change it to the same value in this project before running. Build and run on your device, and you will see an increase in FPS (now ~9-10 FPS on my iphone 5S): In the first edition of the book, this difference was much more noticeable. Note that the Swift language and Sprite Kit itself is in active development and the performance of each version is slightly different. Your FPS numbers may vary from Swift and Xcode version and ios device. What you will notice is better performance 100

101 Chapter 26: Performance: Texture Atlases in the PerformanceTest-RGBA8888 then the PerformanceTest-NoTextureAtlas project. As for demonstrating the memory benefits of texture atlases, you ll see a better example of this later in this chapter. Using texture atlases It s very simple to use texture atlases in Sprite Kit. There is no additional code you have to type most of the work is done for you by Xcode. As you have seen in this book, all you need to do is put any images you want included in a texture atlas inside a folder with an.atlas extension and add that folder to your Xcode project. For example, here s what happens if you were to place the two ships and asteroid into a folder named MiniElements.atlas. Xcode would automatically combine the three images into a single texture atlas behind the scenes: There s no additional code required to use texture atlases. When you initialize a sprite like this: var asteroid = SKSpriteNode(imageNamed: "asteroid-medium") Sprite Kit will first look for a stand-alone image file with this file name and then it will search texture atlases for a match. If your image is in a texture atlas, that atlas is automatically loaded and used behind the scenes. 101

102 Chapter 26: Performance: Texture Atlases Note: Sprite Kit will by default look for PNG images, so the extension is not necessary. If your images are in other formats, such as JPG, you will need to add the extension to the call shown above. If you want more control over the specific texture atlas to load, you can use the SKTextureAtlas and SKTexture classes. Here s an example: let atlas = SKTextureAtlas(named: "MiniElements") let texture = SKTexture(imageNamed: "asteroid-medium") var asteroid = SKSpriteNode(texture: texture) You can use the SKTextureAtlas class to get a reference to a specific texture atlas and the SKTexture class to get a reference to a particular texture within a texture atlas. Then you can create a SKSpriteNode from that texture to have fine-grained control over which texture atlas a sprite comes from. This is the same technique that Pest Control used earlier in this book. Of course, going this slightly longer route is not required it s only for those occasions when you find you need the extra control. Maximum texture atlas size According to Apple s Texture Atlas Help document, the maximum size of a texture atlas is 2048x2048 pixels on iphones and 4096x4096 on retina ipads. If you have more images than can fit in a 2048x2048 texture atlas, Xcode will create additional texture atlases until all images in the.atlas folders are inside of an atlas. Note: The GPU on the iphone 4S/5/5S support textures up to 2048x2048. The ipad retina (ipad Air, ipad mini retina) GPU support textures up to 4096x4096 pixels in size. You can tell Xcode to create texture atlas of a maximum size of 2048x2048 (default) or 4096x4096 if your game is designed to only run on retina ipads and later devices. Using 4096x4096 sized textures on devices that only support the smaller 2048x2048 pixel texture size will result in your textures not loading and an empty black screen. If you need to change the maximum texture size in Xcode from the 2048x2048 default to 4096x4096 search for texture in the Build Settings tab, and select the either 2048 or 4096 in the dropdown as shown below: 102

103 Chapter 26: Performance: Texture Atlases When considering texture atlas size, there are a few guidelines: Make them fit. If you have more images than can fit inside a single texture atlas, you start to lose some of the performance benefits you learned about here. It s good to put some thought into how big a texture atlas will be and structure your texture atlases to avoid going over this limit where possible. Separate by scene and layer. As you ll learn later in this chapter, one good way to organize your texture atlases is based on what is displayed in a single game scene (or layer within a scene) at once. Don t put backgrounds in texture atlases. For very large images, like fullscreen backgrounds, you don t get much benefit from using texture atlases. This is because the benefits of using texture atlases result from memory and performance savings from small images you draw many times, but these are large images you draw only once. It s often better to leave full-screen background images as normal sprites instead of putting them inside texture atlases. Texture atlases and retina displays With the iphone 4, Apple introduced a new technology called retina displays. These displays are so crisp that the human eye is unable to detect the pixel boundaries at a typical viewing distance. The iphone 4 s screen is 640x960 pixels double that of its predecessor, which was 320x480 pixels. To make it easier to deal with the different screen resolutions, instead of working with pixel units in your code, Apple has introduced the concept of points. Points are a unit of measurement where: On a non-retina display, 1 point equals 1 pixel. On a retina display, 1 point equals 2 pixels. You might not have realized this, but you ve been using points in this book all along, as that is Sprite Kit s unit of measurement. The advantage of using points is that you don t have to change your positioning logic or values when moving between retina and non-retina displays. You can position an image at (100,100) points and trust that it will be in the same position on both types of displays. The second piece to this puzzle is that you need artwork that is double the size in pixels for retina displays. Sprite Kit also makes this easy. Just like in UIKit, all you need to do is add a double-sized version of each image to your project with suffix on the filename. For example: spaceship.png! Non-Retina 103

104 Chapter 26: Performance: Texture Atlases Retina There is also variant that is used on iphone 6 Plus devices. Sprite Kit will automatically choose between the 1x, 2x, and 3x versions of your images at runtime without any additional code from you. Loading an image or texture atlas will cause Sprite Kit to choose appropriately, according to the device. For example, consider the line to create a sprite: var playership = SKSpriteNode(imageNamed: "spaceship") If you execute this code on a retina device, Sprite Kit will load spaceship@2x.png, while on non-retina device, it will load spaceship.png. Xcode 6 will create two texture atlases if it detects you have retina and non-retina images in your.atlas folder. The retina images in your atlas folder should have a suffix and the same base file name as the non-retina versions. Note: In addition, Sprite Kit also supports the ~iphone and ~ipad suffixes. These allow you to specify different versions of images specific to iphone or ipad devices. To learn more about this, check out Apple s Drawing and Printing Guide for ios. Pixel formats Just as important as using texture atlases instead of individual images is choosing the correct pixel format for your texture atlases. As you learned earlier in this chapter, by default Sprite Kit uses the highest quality pixel format: RGBA8888. This means texture atlases use 32 bits of color data for each pixel: 8 bits for each of red, green, blue and alpha (transparency). This gives you the best looking images, but also requires more memory than you might actually need for your game. Let s take an example of how this works. With your PerformanceTest-RGBA8888 project still open, build and run your project on your ios device and select the Memory option in the Debug Navigator (CMD+6). You should see something similar to the following: 104

105 Chapter 26: Performance: Texture Atlases Note: At the time of writing this chapter, there appears to be an unusually large amount of memory used here. In previous versions of Sprite Kit, this same project only used 23.1MB (2.3%). We suspect there may be a bug here, and we hope that it will be fixed in a future version of Xcode. Depending on the artwork you are using for your game, you may be able to get away with much lower quality settings for your artwork to decrease the memory overhead. For example, the RGBA4444 format uses 4 bits for each of red, green, blue and alpha channels and requires half the memory of the same image stored using RGBA8888. If your game s textures look OK in RGBA4444, that is a big savings! Changing the texture atlas pixel format in Xcode You can change Sprite Kit s default pixel format for texture atlas generation directly in Xcode. Once you have at least one.atlas folder in your project, Xcode will show the options for texture atlas generation. With the PerformanceTest-RGBA8888 project open, select PerformanceTest- RGBA888 in the Project Navigator and click on Build Settings. Scroll down to Sprite Kit Deployment Options, or just search for texture, and you will see the following: Here you can turn texture atlas generation on and off and set the pixel format that Xcode will use when generating texture atlases. 105

106 Chapter 26: Performance: Texture Atlases Choose the RGBA4444_COMPRESSED option here. Sprite Kit will automatically lower the quality of your images the next time you build and run. Note: The compressed formats you see in the Xcode dropdown are only how the texture atlas will be stored on disk (flash storage) and not in memory. If you chose RGBA8888_COMPRESSED, Xcode will gzip the normal RGBA8888 texture atlas. This saves you a little space on your game s bundle size at the expense of a slightly longer loading time for the texture atlas, because it needs to be decompressed. Remember that in memory the images are stored uncompressed. With the option key pressed down, select Product\Clean Build Folder and delete the PerformanceTest-RGBA8888 app from your device. Build and run the project and you will see it run just as before but if you zoom in or look closely, you might notice the colors aren t quite as crisp as before. Also, if you switch to the Debug navigator (CMD+6) and select the Memory option, you ll see the memory load has decreased: It is only a slight decrease as this project uses a very small texture atlas, but for your games you may notice a large savings. You might have seen that there were many options to choose from besides RGBA8888 and RGBA4444. Let s take a deeper look at each option so you better understand which to choose when. More about color formats The diagram below shows the image quality degradation of the RGB formats supported by Xcode and the TextureAtlas tool (which you ll learn about shortly): 106

107 Chapter 26: Performance: Texture Atlases Take a close look at the images and you can make the following observations: RGBA8888 is a 32-bit pixel format using 8 bits for each of red, green, blue and alpha. This is the highest quality image with no degradation. RGBA4444 is a 16-bit pixel format using 4 bits for each of red, green, blue and alpha. Since the sample image above uses only a small number of colors, you can t discern much of a difference. However, if you look very closely at the gray on the wings you may notice some slight artifacts as a result of the smaller color space. RGBA5551 is a 16-bit pixel format using 5 bits for each of red, green and blue, and 1 bit for alpha. This means the colors can be sharper, but pixels must be either fully transparent or fully opaque, which removes the gradient along the edges so that they look more jagged. RGBA565 is a 16-bit pixel format using 5 bits for red, 6 bits for green and 5 bits for blue. This is a great format to use for images that don t have any transparency, such as full-screen background images. Of course, this image does have transparent areas, which simply become black. Keep in mind that these images look much larger on this page than they would in a game, which means you can see the image artifacts more easily here. For this particular example, if it were up to me I d choose RGBA4444. It looks almost as good as RGBA8888 but uses half the memory! 107

108 Chapter 26: Performance: Texture Atlases This image works particularly well for RGBA4444 because it doesn t make much use of gradients. In general, the more you use gradients, the more noticeable the difference between 32-bit and 16-bit will become. For example, consider this rainbow image: In the rainbow image, the difference between RGBA8888 and RGBA4444 is quite noticeable because gradients use a wide range of colors and limiting the colors results in a striping effect. When making artwork for mobile devices, it s a good idea to limit your use of gradients to avoid this problem. If you take a closer look at the ship image earlier, the artist kept this in mind and limited the colors in the ship to a small number so it would look great even at lower bit depths. Whatever you choose for your output texture atlas format in Xcode will apply to all texture atlases in your game. But what do you do if you want to have one texture atlas use a high-quality format like RGBA8888 and another use a lower quality format like RGBA4444? Is this possible? The answer is yes, but you have to venture behind Xcode s wonderful GUI and into the world of the command line. Manual texture atlas generation Xcode also allows you to create the texture atlases for your project manually via a command line tool called TextureAtlas. This tool is located inside of the Xcode App Bundle, under the following path: /Applications/Xcode.app/Contents/Developer/usr/bin/TextureAtlas 108

109 Chapter 26: Performance: Texture Atlases This may already be on your path if you have installed the Xcode command line tools, but if not you can give the full path to TextureAtlas to run it. One useful feature of running TextureAtlas via the command line is that you can force the pixel format of the texture atlas being generated. Above you can see that the RGBA8888 format is the default for all texture atlases created by Sprite Kit, although there is support for lower quality pixel formats as well. Note: Starting with Xcode 6 you can now set the maximum texture size via the command line with the s parameter. Testing out TextureAtlas via Terminal To get a better feel for the TextureAtlas tool, it is useful to run it manually via the command line (rather than via the Xcode GUI or a script). 1. Create a folder on your Mac where you can save some texture atlas files, such as ~/Desktop/Textures. 2. From the chapter resources, copy the TextureTest.atlas folder into the new folder you just created. 3. Start Terminal and cd into the folder you just created, such as ~/Desktop/Textures. 4. From the Terminal command line, execute the following command, changing the paths to match the folder name you created: /Applications/Xcode.app/Contents/Developer/usr/bin/TextureAtlas -v -f 1 TextureTest.atlas This creates a texture atlas for the sprites in the TextureTest.atlas folder, using format 1, which is RGBA8888 uncompressed. Note the input folder contains both normal and retina-sized (@2x) sprites. After issuing the command, you will see results similar to the following: 109

110 Chapter 26: Performance: Texture Atlases Loading texture file:...desktop/textures/texturetest.atlas/asteroid-medium.png. Loading texture file:.../desktop/textures/texturetest.atlas/enemy-ship.png. Loading texture file:.../desktop/textures/texturetest.atlas/player-ship.png. Generated texture atlas size [124, 133]. Extrude texture: enemy-ship.png Extrude texture: player-ship.png Extrude texture: asteroid-medium.png Writing texture atlas.../texturetest.atlasc/texturetest.1.png. Loading texture Loading texture Loading texture Generated texture atlas size [243, 261]. Extrude texture: Extrude texture: Extrude texture: Writing texture Writing texture atlas plist.../desktop/textures/texturetest.atlasc/texturetest.plist. Once the command completes, open the ~/Desktop/Textures folder or your own equivalent in Finder and you will see the output folder and texture atlases created by TextureAtlas. The created texture atlas folders always have the original folder name with a c suffix: Inside the.atlas folder there are always at least two files: 110

111 Chapter 26: Performance: Texture Atlases 1. The texture atlas images (pngs, or pvr.gz if compressed). These are the packed images for the texture atlas. Xcode will automatically split normal and retina images into separate atlases for you and will automatically create additional atlases if you go above the 2048x2048 pixel limit. 2. A property list (plist) file. This file contains instructions to Sprite Kit for how to cut out your images from the texture atlas image(s). Let s take a closer look at this. Xcode contains a great plist editor that you can use for inspecting the output from the TextureAtlas tool. Open TextureTest.plist in Xcode and expand the items by clicking on the triangle icon to the left of the plist keys, as shown below. In this screenshot you can see that enemy-ship.png is located at coordinates (1,66) inside of the TextureTest.1.png file and that it is (79,66) points in size. You can also see that it is not rotated inside the texture atlas png. Sprite Kit uses this information behind the scenes when you use texture atlases. A word about Texture Packer As you learned in Chapter 15, Imported Tile Maps, you can also use a popular third party tool called Texture Packer to generate texture atlases for you instead of using Xcode s built-in TextureAtlas tool. The advantage of TexturePacker is it has some neat options not currently in Xcode s TextureAtlas tool like dithering, which can make your images look better than usual at lower quality pixel format. It also has full command line support, just like TextureAtlas does. 111

112 Chapter 26: Performance: Texture Atlases Personally we are big fans of Texture Packer, and we recommend that you consider using it as your texture atlas needs grow more complex. Texture atlas pitfalls As you ve seen, texture atlases improve performance without requiring any code changes. So drop everything into a single.atlas folder and call it a day, right? Not so fast. Here s something you need to know about texture atlases: if you use a single subtexture from a texture atlas, the entire texture is loaded into memory. If you re not careful, this can cause your app to use more memory than you may have intended. To help you understand this better, I have created another starter project called MemoryHog-Starter. You can find it in the resources for this chapter. Open this project in Xcode before continuing. Introducing Memory Hog Imagine you were making a game composed of all the mini-games you have created thus far in this book. Your first inclination would be to put each game s artwork into its own texture atlas. But what if you wanted to create a special mashup level using some items from each game? In Memory Hog, you only need three images: the sleepy cat from Cat Nap, the zombie from Zombie Conga and the yellow car from Circuit Racer. These three images are inside a texture atlas from each respective game. In the Memory Hog project you will find three.atlas folders named game1.atlas, game2.atlas and game3.atlas. Each of these folders contains a lot of images from one of the mini-games, including a single image that Memory Hog needs. Memory Hog is built from the Sprite Kit template and all of the code on which you need to focus is inside GameScene.swift. Open GameScene.swift and take a look at didmovetoview(view: SKView). It simply sets the background color and then creates three SKSpriteNodes, placing the sleepy cat in the screen center, the zombie 100 points to the left and the Circuit Racer car 100 points to the right 112

113 Chapter 26: Performance: Texture Atlases Build and run Memory Hog on your ios device. With the game running, select the Debug Navigator (CMD+6) and choose the Memory section on the left panel. Note that Memory Hog is using around 12.6 MB. That is a lot of memory for just three small images! Let s see what you can do to clean up Memory Hog and make it a better mobile citizen. Bringing Memory Hog down to size To reduce the memory footprint of Memory Hog, it is important to understand the process that Sprite Kit goes through when you ask it to create a sprite from an image. For example, consider this command in Memory Hog: let cat = SKSpriteNode(imageNamed: "cat_sleepy") Sprite Kit will look for the cat_sleepy image in the following order: 1. First it will check to see if there is a single file in the App Bundle called cat_sleepy.png. (More specifically, it looks for an appropriate variation for the current device, such as cat_sleepy@2x.png for a retina display). 2. If no such single files exist, it will then look in the plist files for the texture atlases in your app s bundle to see if any contain an appropriately named file. 3. If more then one texture atlas contains the image, Sprite Kit will choose the first one it finds. In tests, this seems to be based on the names of the atlases, sorted alphabetically, but it may be a bad idea to rely on this when designing your games in case the behavior changes in future versions of Sprite Kit. If you want greater control over which texture atlas Sprite Kit uses, you can use the SKTextureAtlas and SKTexture classes described earlier in this chapter. Memory Hog s memory hogging is due to the fact that each of the three images is in a separate texture atlas, so Sprite Kit is loading three large texture atlases when all you need are three small images. To fix this, you will create a texture atlas that contains only the three images you need for the scene. In the resources for this chapter, locate the 3elements.atlas folder and drag it into the Memory Hog project in Xcode, under the Images group. Your Memory Hog project should now look as shown below: 113

114 Chapter 26: Performance: Texture Atlases Build and run the updated version of Memory Hog on your ios device. While the game is running, switch over to the Debug Navigator (CMD+6) and note the memory usage. Memory Hog s footprint went from 12.6MB down to a much more reasonable ~7.8MB, but why? Sprite Kit searched the texture atlases for the file names and the first time it found each file, it was in 3elements, so it never needed to load any of the larger atlases. Of course, if 3elements had been named three_elements, your app would still be using 12.6MB of memory due to Sprite Kit searching texture atlases alphabetically for a match. Once again, this behavior is subject to change so should not be relied upon. There are two takeaways from this section: first, try to keep sprites that you will use together in a scene inside the same texture atlas. Second, if you have images with the same name in multiple texture atlases, Sprite Kit will probably load the atlas whose name appears first alphabetically. But when you have a situation like this, it s better not to rely on behaviors like that and instead manually load the correct texture atlas, which is what you ll do in the next section. 114

115 Chapter 26: Performance: Texture Atlases Manually loading texture atlases As you learned earlier, you specify an exact texture atlas and texture to use with the SKTextureAtlas and SKTexture classes. Let s give this a shot in Memory Hog. Open GameScene.swift and replace didmovetoview: with the following code: override func didmovetoview(view: SKView) { self.backgroundcolor = SKColor.whiteColor() let screencenter = CGPoint(x:self.size.width/2, y:self.size.height/2) let textureatlas = SKTextureAtlas(named: "3elements") let cattexture = textureatlas.texturenamed("cat_sleepy") let cat = SKSpriteNode(texture: cattexture) cat.position = CGPoint(x: screencenter.x, y: screencenter.y + 50) cat.setscale(2.0) self.addchild(cat) let zombietexture = textureatlas.texturenamed("zombie1") let zombie = SKSpriteNode(texture: zombietexture) zombie.position = CGPoint(x: screencenter.x - 200, y: screencenter.y + 50) zombie.setscale(2.0) self.addchild(zombie) let cartexture = textureatlas.texturenamed("car_1") let car = SKSpriteNode(texture: cartexture) car.position = CGPoint(x: screencenter.x + 200, y: screencenter.y + 50) car.setscale(2.0) self.addchild(car) //loadrandomimagefromallgameatlases() After setting up the scene, you create a reference to the 3elements texture atlas in memory. Then for each image, you retrieve the appropriate SKTexture from the 3elements texture atlas. You then use that texture to construct the SKSpriteNode, rather than via spritenodewithimagenamed:, as before. 115

116 Chapter 26: Performance: Texture Atlases As you learned in Chapter 14, Beginning Tile Maps, using SKTextureAtlas also reduces the number of Swift objects your app uses and hence its memory usage. You won t notice it in this app because you re only creating three objects, but if you were showing the same image many times, such as in a tile-based game, you would see a significant memory savings. Build and run Memory Hog on your ios device and check the memory usage under the Debug Navigator (CMD+6). It should be back to ~7.8MB again. Understanding batching logic The above example covered the best-case scenario, in which you organize your images so there is one texture atlas per scene. In this way, all bind and draw calls for your game s scene will be batched. However, in game development best-case scenarios are rare, and you may in fact need several texture atlases if your scene requires a significant amount of artwork. In the case of multiple texture atlases per scene, you will want to organize your scene logically into layers, as discussed in several of the chapters in this book. Each layer in your game should be its own SKNode and within each layer you should only use one texture atlas. For example, if you are making a space game, you should consider having one layer for the background objects like asteroids, and one layer for the foreground objects like the ship and lasers. You want to avoid having the images you need spread across multiple texture atlases randomly, which will force Sprite Kit to switch texture atlases multiple times whenever it has to draw a scene. 116

117 Chapter 26: Performance: Texture Atlases By arranging your game s sprites in layers in this manner, you enforce z-ordering (i.e. sprites from one texture atlas should always be all behind or all in front of another texture atlas) and hence reduce the number of bind/draw calls. You ll see an example of this in the next chapter. There s one more thing that can break your batching, even if you use a single texture atlas per layer. This was touched upon in Chapter 13, Beginning Tile Maps, but it s important to mention again. If you have multiple sprites that are all children of the same node, each using a texture from the same texture atlas, you would expect that to draw with a single draw call. However, if some of those sprites have a blend mode of SKBlendModeReplace, for example, while others have a different blend mode, such as the default mode of SKBlendModeAlpha, Sprite Kit will not batch the draws properly. This occurs because Sprite Kit issues a separate draw call each time it needs to change any rendering setting. In order to avoid that, keep sprites that use different blend modes on different layers, even if they use the same texture atlas. At this point, you have a solid understanding of texture atlases and are ready to turn toward other areas of improving performance in Sprite Kit games. But before 117

118 Chapter 26: Performance: Texture Atlases you do, how about a quick challenge to take your texture atlas knowledge a bit further? Challenge: Preloading power There s just one challenge in this chapter and it s to teach you a bit more about preloading textures in Sprite Kit. When working on real-world games, it s common to use tons of textures, and sometimes these textures can take significant time to load. To simulate this, open Memory Hog s LoadingScene.swift and replace didmovetoview: with the following: override func didmovetoview(view: SKView) { let loadinglabel = SKLabelNode(fontNamed:"Chalkduster") loadinglabel.text = "Loading..."; loadinglabel.fontsize = 32; loadinglabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:cgrectgetmidy(self.frame)); self.addchild(loadinglabel) let scaleupaction = SKAction.scaleTo(1.5, duration: 0.5); let scaledownaction = SKAction.scaleTo(0.5, duration: 0.5); let scalesequenceaction = SKAction.sequence([scaleUpAction,scaleDownAction]) let repeatforeveraction = SKAction.repeatActionForever(scaleSequenceAction) loadinglabel.runaction(repeatforeveraction) var textureatlasesarray : [SKTextureAtlas] = [] for i in { let atlasname = "game\(i)" let textureatlas = SKTextureAtlas(named: atlasname) textureatlasesarray.append(textureatlas) startmyscenewithtextureatlases(textureatlasesarray) This initializes an array with all 31 texture atlases, then transitions to the GameScene immediately. Next, open GameScene.swift and uncomment the line in didmovetoview: that calls loadrandomimagefromallgameatlases: 118

119 Chapter 26: Performance: Texture Atlases loadrandomimagefromallgameatlases() GameScene will use this to display the first image from each texture atlas, simulating a game with a large amount of image resources. Build and run your project on an actual device. You will see that the app takes a few seconds to start up, and in the console you will see something like the following (this output is from my iphone 5): First render performed in: 1.98 seconds Whenever you have a long-running operation like this, it s always a good idea to show a progress indication screen to the player so they know something is going on and are less likely to get impatient and quit the game. Sprite Kit comes with a method that allows you to preload textures in the background while still performing animations and other processing in the foreground. Your challenge is to modify Memory Hog to preload the textures in the LoadingScene that runs before GameScene. Here are some hints for how to accomplish this: Inside LoadingScene.swift in didmovetoview:, comment out the call to startmyscenewithtextureatlases:. SKTextureAtlas has a method named preloadtextureatlases:withcompletionhandler: that you should use to preload the texture atlases. You should use the following code block for the completion handler: let endtime = CACurrentMediaTime() let timedifference = endtime - self.starttime println(nsstring(format: "Loaded Texture Atlases in %0.2f seconds", timedifference)) self.startmyscenewithtextureatlases(textureatlasesarray) Once you ve got it working, build and run your project and you ll see a loading screen appear a few seconds before the main scene appears: 119

120 Chapter 26: Performance: Texture Atlases Then in the console, you ll see some output similar to the following: Loaded texture atlases in: 1.65 seconds First render performed in: 0.31 seconds This results in the app being quicker to load and feeling more responsive. Note that at the time of this writing, there is no method to manually unload an SKTextureAtlas from memory. Sprite Kit will automatically unload any texture atlases when they no longer have any active references. This is one of the reasons why the LoadingScene passes a reference to the preloaded textures to GameScene so the texture atlases aren t unloaded during the transition. This is also another reason why it s important to segment your texture atlases based on how you re using them so they can be unloaded when no longer in use! That s it for texture atlases! Use them wisely, as they are the single most important approach to optimizing your games. 120

121 Chapter 27: Performance: Tips and Tricks By Ray Wenderlich Sprite Kit does an excellent job of optimizing your game under the hood for great performance. Behind the scenes, Sprite Kit automatically creates object pools, avoids drawing off-screen nodes, creates bitmap font atlases and much more, so your games are fast and efficient without you having to think too much about it. However, as your games get more complicated, from time to time you ll run into performance issues. This chapter s goal is to show you a few problem areas you might encounter when developing your games, and give you some workaround tips and tricks. In particular, you re going to work on a game called Bullet Storm. Here s what it currently looks like: Bullet Storm is a demo of a side-scrolling space shooter. It s a good start to a space game, but the performance is terrible. As you can see from this screenshot, the game is running at less than 1 frame per second (FPS) on an iphone 5. This app would be better off as a slide show! In this chapter, you will debug Bullet Storm to discover its performance issues and then you will solve them. By the time you re done, Bullet Storm will be running smoothly, and you ll be well equipped to give your own games a performance boost!

122 Chapter 27: Performance: Tips and Tricks Getting started In the resources for this chapter, you ll find a project called BulletStorm-Starter. Open the project and build and run on your ios device. Note: Remember that it s important to run on your ios device when testing for performance, as the artificial conditions of the Simulator will give inaccurate results. This is because your ios device is running a completely different GPU and different resources than your Mac, and the Simulator uses software-based rendering. In short any performance numbers you get from the Simulator should not be trusted one way or the other! You ll see a very sluggish game appear (your FPS may vary): While the game is running, switch to the sixth navigator tab, the Debug Navigator. Click the down arrow to expand if necessary, then click the FPS section on the left and you ll see the game s FPS (1 FPS on my iphone 5), as well as how many milliseconds your app is using the CPU or GPU each frame: As you can see in this screenshot, an overwhelming majority of the time is spent on the CPU: over 1,000 ms on my iphone 5. This is generally a sign of a performance problem you want your game to use the GPU as much as possible so that the graphics hardware does the hard work. 122

123 Chapter 27: Performance: Tips and Tricks Obviously something very wrong is happening here, but how can you find out what it is? Well, you could dig through the code looking for issues, but there s a much better way to find performance problems: using your friend Instruments. Introducing Instruments Instruments is a handy set of tools built into Xcode that you can use to peek into your app. Instruments has tools that can find memory leaks, check resource usage and what you re going to do in this section analyze your app for performance. Stop Bullet Storm and go to Product\Profile from the Main Menu (shortcut key I). The following popup will appear: This displays the various instruments you can integrate into your app. In this case you want to find out what s taking so long, so select Time Profiler and click Choose. A window will appear that looks like this: 123

124 Chapter 27: Performance: Tips and Tricks Click the red circle in the upper-left corner to begin recording, wait about seconds, and click the black stop button to stop recording. At this point, your screen should look like this: The top panel shows you a graph of the CPU resources your app requires over time notice the constant heavy usage! 124

125 Chapter 27: Performance: Tips and Tricks The lower panel shows you a percentage-wise breakdown of where the CPU spends its time. You ll see that the CPU is mostly occupied with the main thread. Click the down arrow next to Main Thread to display the list of sub-functions called by the main thread, and how much time the CPU spends on each. The panel sorts the subfunctions in order of running time, so keep opening the most expensive item until you drill deep enough to reveal functions that look related to Sprite Kit: This is one way to gather clues about the problem, but there s an even easier way. Usually, you just want to see what leaf methods that is, the ones at the end of the call tree are taking up the largest amount of time. To figure this out, click the Invert Call Tree checkbox on the left: This inverts the order of the tree so the methods that take the most CPU time are at the top of the tree, and their children are the callers of the methods. Take a look at the first two methods in this list: SKRenderer::preprocessSpriteImpl. This method seems to be related to processing sprites. As a general rule, the more sprites there are in a game, the slower the game performs. This method suggests you should check Bullet Storm to see how many sprites are in the game at any one time maybe there s a way you can reduce them. 125

126 Chapter 27: Performance: Tips and Tricks SKCEmitterSprite::update. This method seems to be related to processing particle systems. As a general rule, particle systems get more expensive the more particles they generate. This tells you to check Bullet Storm to see how many particle systems are in the game and how many particles each generates. Maybe you can find a way to reduce those, too. If you re an advanced reader, this is a good chance to challenge yourself: Can you take things from here and find and resolve Bullet Storm s performance problems? There have actually been plenty of hints about how to solve these issues in the earlier chapters of this book! :] But if you want to walk through the resolution step-by-step, keep reading. Let s start with the first two hints and peek into Bullet Storm to see if there is a way to reduce the number of sprites and particle systems. Touring the code If you haven t done so already, spend a few minutes looking through Bullet Storm s source code to see how it works. Here are a few notes: There is a class for each of the objects in the game: asteroids, lasers, explosions, enemies and the player. They all derive from a class called Entity. Most of the game logic is in GameScene. The code should look quite familiar it s based on the example projects from other parts of this book! The game is already set up to use texture atlases (see sprites.atlas), and you learned about why that is so important in the previous chapter. If the game weren t using texture atlases, its performance would be even worse. I know that s hard to imagine! :] As you re looking through the code, keep an eye out for any sprites or physics bodies that are created unnecessarily. If you think you ve found the source of the problem, keep reading to the next section to see if you re right. Hint: Remember Zombie Conga! Reducing sprites You may have noticed the following method in GameScene.swift: func spawnasteroids() { for i in 0..<500 { let position = CGPoint( x: size.width (i * size.width * 0.25), y: CGFloat.random(min: CGRectGetMinY(playableRect), 126

127 Chapter 27: Performance: Tips and Tricks max: CGRectGetMaxY(playableRect))) spawnasteroidatposition(position) This method creates an asteroid field through which the player must navigate. It creates a large number of asteroids (500, to be exact) off-screen to the right and moves them slowly to the left, across the screen. This is similar to having a level file that specifies the position of each asteroid, and then creating each asteroid at the appropriate off-screen location on startup. You might not see a problem here. This is an easy and straightforward way to create an asteroid field and Sprite Kit doesn t draw nodes that aren t visible on the screen. However, even when nodes are off-screen, Sprite Kit still needs to do processing on the nodes: running actions, checking for off-screen collisions or processing any attached particle systems, for example. That s the problem with the asteroids here Sprite Kit has to perform many calculations on asteroids that aren t even visible. Apple recommends that you only add a node to the scene graph when: It has a reasonably good chance of being rendered in the future. The node runs actions that are required for accurate gameplay. The node has a physics body that is required for accurate gameplay. In the case of off-screen asteroids, none of these conditions apply. So you should be able to get much better performance by only creating asteroids when you need them that is, right before they appear on the screen, just like in Zombie Conga! To fix this, comment out this line in didmovetoview(): //spawnasteroids() And add the following in its place: runaction(skaction.repeatactionforever( SKAction.sequence([ SKAction.runBlock(spawnAsteroid), SKAction.waitForDuration(0.25) ]) )) This calls a helper function to spawn an asteroid just off-screen every 0.25 seconds. Now build and run, and you should see a marked performance improvement: 127

128 Chapter 27: Performance: Tips and Tricks From 1 FPS to 23.6 on my iphone 5 not a bad start! However, 23.6 FPS still isn t great, plus that node count of 3255 looks insane! The next step is to examine the particle systems to see if you can tune down anything there. Note: Your frame rate and node count will probably vary widely if you tilt your device to play the game rather than resting it on your desk (the game moves the space ship with the accelerometer). That s mainly because your ship s bullets will collide with more enemies if you move around the screen. This is another great opportunity to challenge yourself: Can you tweak the particle systems so they perform a bit better, but still look good? Try it out on your own, and if you get stuck, follow along with the next section! Reducing particle systems Particle systems perform well in Sprite Kit and you should feel free to use them liberally in your games. However, since particle systems become more expensive as they generate greater numbers of particles, you want to make sure yours generate just enough to achieve the desired effect. Let s take a look at one of the particle systems in Bullet Storm. Open BulletStorm\ Particles\AsteroidTrail.sks in Xcode, and click somewhere in the editor to start the particle simulation. View the SKNode inspector in the Utilities section on the left, and you ll see the following: 128

129 Chapter 27: Performance: Tips and Tricks The important settings to notice are Particles Birthrate, Particles Maximum and Lifetime Start/Range. To review: Particles Birthrate represents the number of particles generated per second. In this instance, 400 particles are generated per second. Particles Maximum represents the maximum number of particles generated at a time. Here it is set to 0, which means unlimited. Particles Lifetime/Range represents how long a particle lives before it s destroyed. Here it s set to 2 with a range of 0.4, meaning a particle will live from seconds. To determine the average number of particles onscreen for a given particle system, use one of these algorithms: Is Particles Maximum 0? Then the average equals Particles Birthrate x Particles Lifetime. Is Particles Maximum not 0? Then the average equals either Particles Maximum or Particles Birthrate x Particles Lifetime, whichever value is lower. In this case, Particles Maximum is 0, so 400 x 2 = an average of 800 particles onscreen per second. That s a lot for a simple asteroid trail effect tune this way down by setting the Particles Birthrate to 4. Now it will have an average of 8 particles per second, which will still result in a neat effect. Repeat this for the other effects in the game: For EnemyTrail.sks, change Particles Birthrate from 2000 to 200. For Explosion.sks, change Particles Birthrate from 5000 to 500. For PlayerTrail.sks, change Particles Birthrate from 300 to 30. That s a lot fewer particles! Build and run, and you ll see another performance improvement: 129

130 Chapter 27: Performance: Tips and Tricks The game is up to ~60 FPS on my iphone 5. And it still looks great some might say even better than before! Switch to the Debug Navigator again (the sixth navigator tab), and look at the FPS section. You ll see that now the game only spends 13.2 milliseconds using the CPU, and there s a much better balance between CPU and GPU usage: Now let s check the profiler results. Stop the app and run it under the Time Profiler again, making sure Invert Call Tree is checked: You ll notice the following changes from the last time: mach_msg_trap is on the list, which means the CPU is idle a fair percentage of the time. This is a good thing it means you re not using up all of the CPU! SKRenderer::preprocessSpriteImpl and SKCEmitterSprite::update are still on the list, but are lower on the list, indicating that they take a lot less time than before. If you needed to optimize still further, you could look for ways to use even fewer sprites and particle systems but for now, performance is pretty good. Even though the game is up to ~60 FPS now, there s one more thing to do for learning purposes (because it might not be this easy in your own games!). So far, 130

131 Chapter 27: Performance: Tips and Tricks you ve been looking at the slowest functions in the game, whether they are in the Bullet Storm code or in Sprite Kit itself. But sometimes, you want to limit the profiler results to your own code, so you can focus on improving the performance of the code you ve written. Limiting profiler results to your code Let s try this out. In the sidebar of the profiler, click the button that says Hide System Libraries: Note: At the time of writing this chapter, there appears to be a bug in Xcode 6.1 where it does not list the symbol names from your app like it should (as shown above). Hopefully this will be fixed in a future version of Xcode. In the meantime, this chapter will show you what should happen. This limits the results to Bullet Storm code only, showing you where the CPU spends the most time the slowest methods in your code. Spending your own time optimizing these methods will give you the most bang for your buck. You ll see the top entry is initializing Entity, especially the Laser entity. This leads into a new discussion: object pooling! Object pooling In the games in this book, whenever you need to create a new object, like a laser or an explosion effect, you simply create a new object at that time. However, on ios devices, it s expensive to allocate objects at runtime. One way to improve performance is to pre-allocate a small array of objects and grab the next available object when you need it. The objects that you create most frequently in Bullet Storm are lasers for the player s ship. Instead of continuously allocating them, let s pre-allocate and reuse a bunch of laser sprites. 131

132 Chapter 27: Performance: Tips and Tricks First, add a new property variable to the top of GameScene.swift: var playerlasers: [Laser] = [] This creates an array that will contain pre-allocated, reusable player lasers. Next, add this new method to the file: func setupplayerlasers() { for i in 0..<100 { let laser = Laser(laserType:.Player) laser.name = "laser" laser.hidden = true fglayer.addchild(laser) playerlasers.append(laser) This pre-allocates 100 laser objects and adds them to the foreground layer, but marks them as hidden. Whenever you need a laser, you ll look for the first available laser in the playerlasers array that is not hidden (when you spawn a laser, you ll unhide it). To set up the lasers, call this method in didmovetoview(): setupplayerlasers() Next, replace spawnplayerlaser() with the following code: func spawnplayerlaser() { var curlaser: Laser? = nil for playerlaser in playerlasers { if playerlaser.hidden { curlaser = playerlaser break if let laser = curlaser { laser.hidden = false laser.position = player.position + CGPoint(x: 6, y: -4) laser.alpha = 0 laser.configurecollisionbody() laser.runaction(skaction.fadealphato(1.0, duration: 0.1)) 132

133 Chapter 27: Performance: Tips and Tricks let actionmove = SKAction.moveToX(size.width + laser.size.width/2, duration: 0.75) let actionremove = SKAction.runBlock { laser.cleanup() laser.runaction(skaction.sequence([actionmove, actionremove])) runaction(laserplayersound) shots++ This modifies the method to pull an object from the laser pool rather than allocating a new object each time. Finally, open Laser.swift and replace cleanup() with the following: override func cleanup() { physicsbody = nil hidden = true This sets the physics body to nil and hides the laser, rather than removing it from its parent node. Build and run, and check your profiler results again: You ll see that initializing entities is no longer the top item, and the time spent initializing lasers has dropped considerably. You could use a similar technique to make object pools for the other types of nodes or actions you create in the game. 133

134 Chapter 27: Performance: Tips and Tricks Avoid enumerating by name Now the CPU spends most of its time in BulletStorm.GameScene.update(), so double-click that method and Xcode will take you straight to the slow code: This is some code that is enumerating the layer to find all of the asteroids and enemies, and call their update methods. Enumerating child nodes by name is convenient, but slow. Luckily, there s an easy workaround: put each type of object in its own layer, to make it easy to enumerate the entire layer, rather than the node names. To do this, add two new properties to the top of GameScene.swift: let enemylayer = SKNode() let asteroidlayer = SKNode() This creates two empty nodes that you ll use as layers to contain the enemies and asteroids, respectively. Then add these lines to didmovetoview(), at the end of the Add layers section: fglayer.addchild(enemylayer) fglayer.addchild(asteroidlayer) This adds both of the new layers as children of the foreground layer. Inside spawnasteroidatposition(), change the lines that add the asteroid and the emitter to the following: asteroidlayer.addchild(asteroid) asteroidlayer.addchild(asteroid.emitter) Similarly, modify the final line of spawnenemy() to the following: enemylayer.addchild(enemy) 134

135 Chapter 27: Performance: Tips and Tricks This adds the asteroids (and their emitters) and the enemies to their own specific layers. Last but not least, replace the enumeration loops for the asteroids and enemies in update() with the following: for node in asteroidlayer.children { if let asteroid = node as? Asteroid { asteroid.updatewithdt(self.dt) for node in enemylayer.children { if let enemy = node as? Enemy { enemy.updatewithdt(self.dt) Run the Time Profiler again, and you ll notice that update() no longer appears in the list: Bullet Storm is now running well enough that you can declare success. For your own games, you can use Instruments to run through a similar process. However, your problems may differ from the ones just described. Just rember to use the Time Profiler instrument to determine the slowest areas of your code, and optimize those first. The following section is an overview of other types of performance bottlenecks you may encounter and gives you some tips and tricks for how to resolve them. Other performance tips and tricks Use effect nodes and Core Image sparingly As mentioned in Chapter 12, using effect nodes and Core Image in your games can result in some amazing effects, but they can also consume a lot of system resources. 135

136 Chapter 27: Performance: Tips and Tricks Here are a few workarounds to consider if you have a performance issue related to an effect node: Are you filtering the lowest-possible granularity? For example, applying a filter to the entire scene is a lot more expensive than applying a filter to a single small sprite or even a single layer of sprites. Make sure you re only filtering exactly what you need to filter. Can you rasterize the effect? SKEffectNode has a property called shouldrasterize. This is set to NO by default, which means that each frame, Sprite Kit discards the previous result and re-applies the effect. If you set this to YES, it caches the image and reuses it until one of the SKEffectNode s children nodes change. This is great to use if you re applying an effect to something that doesn t change often, like a background image it can result in much faster performance. Are there pre-rendered or fake alternatives? In cases where you try the previous two tips and your game still doesn t perform well enough, consider using an alternative to effect nodes. Is there any way you can pre-render the effect into a sprite that you ship with the app? Or fake it by some combination of prerendered sprites overlaid on top of each other? Can you reduce the size of the effect? In cases such as mask nodes, it often seems easiest to align your masks with your nodes by making them all the same size and then giving them all the same position. However, try to use as little transparent space as possible because transparent pixels add processing time. Instead, trim away any unused areas of your masks and adjust their positions to align them appropriately. Perform long-running operations in the background By default, everything in Sprite Kit runs on the main thread. If you have a computationally intensive operation such as a complicated path-finding algorithm, you may wish to perform this on a background thread. There s an easy way to run a block of code on a background queue in Sprite Kit: through the runblock(queue:) action. Use texture atlases and organize them wisely This is probably the most important tip, which is why the book dedicates an entire chapter to the matter. If you haven t already, be sure to read Chapter 26, Performance: Texture Atlases. Keep physics as simple as possible As you saw with Bullet Storm, there is a cost associated with using the physics engine. Here are a few rules of thumb: The more physics bodies, the more expensive for performance. Consider making physics bodies only for sprites that need them. If a sprite doesn t engage in collision or contact detection, then it doesn t need a physics body. You may be 136

137 Chapter 27: Performance: Tips and Tricks able to design your gameplay in a way to reduce the requirements for physics bodies, as well. Prefer static physics bodies to dynamic ones. That is, if you know an object will not move, set its dynamic property to NO. This allows the physics engine to make various optimizations that increase performance. Prefer simple shapes to complex shapes. Remember, you can choose which shapes to use for your physics bodies use the simplest shapes you can get away with for the gameplay you require. Here are the shapes in order of least expensive to most expensive: circles, rectangles, polygons, compound shapes and alpha mask shapes. Be particularly wary of alpha mask shapes use them only when you really need them for gameplay and one of the simpler options won t do. For polygon shapes, remember that the more vertices a physics body has, the more expensive it is for performance. Consider making a simplified collision shape for your sprite it doesn t have to match the sprite s shape exactly. You ll find that the faster your objects move, the less exact the physics shape needs to be to give a visually satisfying collision. Remember, you re trying to make a game, not a real-world physics simulation. Use usesprecisecollisiondetection sparingly. This is a flag you can set on physics bodies to prevent fast-moving bodies from passing through other bodies within a single frame. It sounds good, but it does have a performance cost, so usually you want to set this only when you need it. That s it for performance tips and tricks and for the technical content of this book! Congratulations for making it through to the end! Take a bow, take a break and get ready for one last treat: learning how to make 2D art for your game. 137

138 Chapter 28: Making Art for Programmers By Mike Berg In this book, you ve created some great mini-games, but they ve all used premade art that I made for you. You may wonder how you can get art like that in your games and that s what this chapter is all about! These days, to succeed in the App Store, your game not only needs to be fun and innovative; it needs to look great, too. This is a problem for many aspiring game developers because although you may be great at programming and game design, you might not be so great at making your game look the way it does in your daydreams. There s a reason many games feature doodle or stick man art! :] The good news is, you have two solid options as a game developer: you can either hire an artist to help out, or you can make the art yourself. After all, making art is a skill you can practice and improve, just like any other skill. In this chapter, I ll first help you decide whether you d prefer to hire a game artist, or do it yourself. In the event you choose to do it yourself, I ll show you how to create cartoon artwork for your video game that s similar in style to the art you ve used in this book so far: I ll also provide tips for how you can continue to develop your skills as a game artist. By the end of this chapter, you ll have a solid starting point and a map for making your games look as great as they play!

139 Conclusion Choose your path: Hire or DIY? When deciding how to acquire art for your game, you have two basic choices: You can hire an artist or make the art yourself. Let s go over each option in turn. Hire an artist There are advantages to hiring an artist: Work with a pro. If you hire an artist, you can get someone who is already great at his/her craft and likely has been practicing for years. They can focus on what they re good at, leaving you free to focus on your own specialty which is likely programming! Choose your style. Different artists have different styles and your game might benefit from a certain type of style. By hiring an artist, you can look around and find someone whose style is the perfect fit for your game. Rapid development. If you have to make the art as well as program the game, you ve just doubled your development time. Obviously, splitting up the work can save a lot of time, allowing you to get your game to market quicker. Collaboration. Sometimes working with an artist can help improve your game, as you share ideas and feed off each other s energy and passion. Think of the best games you ve seen I bet most of them were made by a team of at least two! Think this option is for you? If so, skip ahead to How to find and hire an artist. Do it yourself Doing it yourself has advantages, as well: Save money. Sadly, most artists are not willing to make art for your game out of the kindness of their heart or promises for future payment they ll want cold, hard cash. And sometimes that is the very thing indie game developers lack. If saving money is on your mind, doing it yourself might be the only option. Build skills. A lot of indie developers find the entire process of making a game fun and exciting, and want to learn the skills involved in every step. If this sounds like you, maybe you want to make art simply for the experience and skills you ll pick up along the way. Lack of dependencies. Working with an artist does introduce a delay into the process. If you need a piece of art right away, you ll need to wait until the artist is awake/available to work on it/etc. If you make the art yourself, or at least know enough basics to make some placeholders, you can keep moving at the rapid speed that game development often entails. Glory and honor. The final benefit of doing it yourself is the pure bragging rights you ll earn. See that game? Yeah, I programmed it AND made all the art myself. Booyah. Just be prepared for a long development cycle! :] 139

140 Conclusion Think this option is for you? If so, skip ahead to Getting started. How to find and hire an artist Many game developers struggle with finding an artist. Here are some tips to get you in touch with a suitable artist as quickly as possible. Conferences and meet-ups As with looking for work, one of the best ways to find an artist is through networking: You meet one in person at an event in your field, or have one recommended to you by a friend or colleague. How to network? In a nutshell, get yourself out to conferences, local meet-ups and user groups. Here are some recommended conferences: Game Developers Conference (GDC): Apple s Worldwide Developers Conference (WWDC): 360iDev: Unite: As for meet-up groups, search meetup.com for your local area. The best meet-ups are game developer, artist or ios hangouts. You can also check game developer forums like or to see if there are any local meet-ups or game jams in your area. Twitter Many game developers hang out on Twitter. While you won t necessarily get to know someone personally on Twitter, it s a great way to expand your list of developer contacts around the world, people who you may eventually meet at a conference or event. Be an active Twitter user and get to know the work of those you follow and those who follow you. The larger your network of developers, the easier it will be for you to find someone with the skills you need when the time comes. Search for portfolios Do a web search for portfolios of the style you require: pixel art portfolio or fantasy cartoon art portfolio. You know what you re looking for and will quickly see the range of quality available. This will help you make a choice even if you d rather go with a personal recommendation. Here are some additional online resources for portfolios: 140

141 Conclusion A lot of artists post their work on Deviant Art it s often a great way to find upand-coming artists: If you re into pixel art, look no further than Pixel Joint, a community of pixel artists who regularly post their work and portfolios: 3D Total has a terrific gallery that s organized into categories such as Character, SciFi, Fantasy and Cartoon. The site also has very active forums: The Polycount forum has portfolios specific to game art: Call for applications Post your project needs on your website and on job boards. Here are a few sites and boards where you can post jobs: Concept Art has a job board with a lot of game illustrators: Gamasutra has a job board: 3D Total, Polycount, Pixel Joint, GameDev.net and TigSource (mentioned above) also have job boards. Of the applications you receive, discard those without good portfolios, regardless of their experience. A portfolio should demonstrate the artist s ability to do the job in the style you want it done. Does this really work? Yes! As an example, Ray and I first got to know each other via Twitter. He looked at my online portfolio a few times, so he was familiar with my work. A few years ago, we met in person at a conference. Over the years we stayed in touch, and that s how I came to work on this book! The moral of the story is this: get to know artists you admire, preferably in person, and make connections you never know when you might want to work together on a project! Paying your artist As I mentioned earlier in this chapter, most artists aren t willing to work for free you ll need to find some way to reimburse them for their time and effort. This section will cover what to expect, as well as my personal recommendations. 141

142 Conclusion Revenue share contracts With a revenue share contract, you promise an artist a percentage of the income from your game, but no money up front. These types of contracts are often appealing to indie game developers with little money to spend, but it s very hard to find an experienced artist who is willing to take this kind of deal. Many an artist has been burned by promises of a revenue share that fails to deliver in the end! These types of deals only seem to work if you have a strong preexisting relationship with the artist. For example, if your good friend or significant other is an artist, you might be in business. Also, the artist will probably need to be passionate about the project for it to work out otherwise he or she might lose interest and motivation. If you don t have an artist on hand who trusts you and believes in your project, you ll probably have to pay an artist up front. This can be a good thing if your game does well, you ll get to keep the profits for yourself! Fixed-quote contracts vs. hourly contracts If you re going to pay the artist up front, you have a choice: You can either pay her a fixed amount up front, or you can pay her by the hour. The principal advantage of a fixed-quote contract is that you know how much you re going to spend. However, I personally recommend against fixed-quote contracts. Game dev projects are constantly evolving; at the beginning of a project, it s impossible to know how much art the project will eventually require. As soon as the artist s time goes over that fixed quote because of evolving to-do lists, his or her interest and motivation are likely to drop like a stone. Here s what works for my clients and me. My own method is to provide an estimate based on the initial asset request list. I will then send an invoice for a percentage of that estimate (usually 50%, or a smaller percentage if the project is larger), to be paid up front. As I work on the project, I keep very detailed track of my time and provide the client with regular updates that show how I ve used my time. I send an interim invoice every time the total owing reaches a certain agreed-upon amount. For example, the client and I might have an agreement that I send an interim invoice for every $2,000 of work. This method gives both the client and the artist the freedom to make changes to the to-do list on the fly, which is always necessary while a game is in development. Potential disagreements over cost-value can be headed off before they develop. The client knows exactly what they re getting for their money and the artist stays motivated and involved in the process. 142

143 Conclusion Price expectations People often ask me, How much do artists charge to make art for a game? If you re hiring an experienced artist, you can expect to pay anywhere from $30-$90 per hour. At the time of writing, my own rate is $60 per hour. Generally, the more skilled the artist, the more you can expect to pay. You might find an artist who is willing to take less, but expect their experience and the overall results to be commensurate. You get what you pay for is an old adage for a reason. There s also great value in finding an artist with experience making video game art (ios-specific experience is especially helpful). There are many ways an artist can make your life as a developer easier (or harder!), and their level of game development experience is a large factor. For example, an artist who knows about object coordinates, anchor points, texture atlases and overdraw will be able to provide you with graphic files that are ready-to-use in your tools of choice, requiring as little extra processing on your end as possible. Knowledge of games and how they work will help an artist create assets that are efficient and extendible, for long-term reuse and adaptability in the event of future updates. Occasionally, changes to one art asset can require updates to several others; good game artists know how to keep these sorts of snowball effects to a minimum. That s it for my advice when it comes to hiring an artist. If this is the route you ve decided to take, stop reading here and go find yourself an awesome artist. But if you re eager to learn how to make art yourself, read on! Getting started The rest of this chapter will show you how to create a cat sprite for your game in a style similar to that of the sprites you ve used in the minigames throughout this book: 143

144 Conclusion You ll create the artwork in Adobe Illustrator using vector shapes. This will allow you to use the art at any resolution or size without degrading the quality of the image. If you don t already have access to Adobe Illustrator, you can download a free trial here: This will install an interface to Adobe s Creative Cloud, which lets you try many different Adobe products for 30 days. Once you have Illustrator installed and ready to go, pull out a pencil and paper and get ready to sketch! Start with a sketch You re first going to make a rough sketch to give yourself a general idea of the shape of the art so you can trace it in Illustrator later. The type of paper you use for this kind of sketch doesn t matter use whatever you have on hand, as long as it s clear of other markings. Use a pencil to draw. The cat you ll be sketching is made up of four main shapes: head, body, front legs and tail. Here s a quick preview of the four main shapes: Now follow the instructions below to draw the cat. The shapes shown here are just guides; if you re comfortable with adapting it as you go, please put your own spin on them! 144

145 Conclusion Draw an oval for the head. The pear-shaped bottom of the body should be a bit larger than the head, with the upper body/neck stretching up to meet the head. Near the head and close to the edges of the body, draw two smooth lines that converge a bit at the bottom for the legs. At the bottom of the legs, add an open half-circle for the tops of the feet. 145

146 ios Games by Tutorials Conclusion For the bottoms of the feet, add an arc that s a bit flatter than the tops of the feet. Define the shape of the tail starting with the leftmost curve. Add a second curve to give it thickness. Cartoon tails can be a lot thicker than real tails! Now that you have the cat s basic shape, add some detail. Cartoon cats can have pointy cheeks. Don t ask why! Look at each arc of the ear separately. Start near the middle of the head and draw an arc slightly up, but mostly out. Then curve down and slightly inward to finish. 146

147 ios Games by Tutorials Conclusion Round out the sides a bit, where the ears meet the head, and add a bit of height to the top of the head. Think of the eyes as egg shapes, tilted inward a bit. The nose is a flat oval. Add a short vertical line under the nose with a wide W shape underneath for the mouth. A wide arc over the nose defines the cat s snout. Add large arcs for the irises, some straight whiskers pointing slightly upward, and don t forget those eyelashes for extra lovability. 147

148 ios Games by Tutorials Conclusion Moving on to the legs, draw a straight line down the middle, not quite as high as the outer lines for the legs. Cut a small wide triangle out of the bottom center of the feet to make them point slightly outward. Add two short lines on each foot to make toes. Smooth out the arc for the back a bit. Add a line for the top of the back leg. One at a time, add curved points to make up the fur at the end of the tail. Make them varied in size, with the middle one the biggest. 148

149 Conclusion Optional: Add a curve near the end of the tail, and curves over the top edge of the paws. These will define areas with white fur. Optional: Erase unnecessary lines. You only need to do this if your drawing is very sketchy (which is OK!) and it s hard to see the final lines, which you ll be going over in Illustrator. Getting the sketch into Illustrator Don t worry if you don t have a scanner the camera on your iphone or ipad works just as well for this purpose. Lay your sketch flat in a well-lit area and hold your iphone directly over it. Try to get the angle as square as possible. Line up the edges of your page with the edges of the screen to help with this. 149

150 Conclusion yourself the photo, using the Large setting when asked how you want to resize the image. Open Illustrator and select File\New (command-n) to create a new document. Give the document a Name of cat and choose Devices from the drop-down list of Profiles. Then, choose iphone 5S from the drop-down menu of Sizes. This should result in an image that is 640x1136 pixels, which is the size of an iphone 5 screen. Click OK. Select File\Place (command-shift-p) and select the file you ed yourself for the sketch. Leave the checkbox options as they are by default, with only Link selected. Click Place to add it to the Illustrator file. 150

151 Conclusion You will see the Place cursor, along with an icon of your image. Click and drag to draw a box that mostly fills the canvas. This sets the size and position of your placed file. Many of the controls you ll be using to modify your artwork are organized into what Illustrator calls palettes. These are shown on the right side of the screen. To start, make sure the ones you ll use most often are visible. Select 151

152 Conclusion Window\Workspace\Essentials. If you see only buttons, click the tiny Expand Panels button at the top-right of each panel. You should see an arrangement of palettes something like this don t worry if it s not exactly the same: In the Layers palette (select Window\Layers if you don t see it), click the empty square next to the eye icon on the layer for the cat. This will lock the 152

153 Conclusion layer, preventing you from selecting it or moving it accidentally. Click the Create New Layer button to add a layer that you ll use to hold your vector tracing. Tracing the sketch with vector lines Before you begin tracing the sketch, you should set your default colors. At the bottom of the Tools palette, there are swatches for the current fill color (the solid box) and stroke color (the box with the thick outline). Illustrator will use these fill and stroke colors will for any object you create. Press D to set the default colors for stroke and fill (white fill, black stroke). Click the fill swatch, which is a white square: Press the forward slash key (/) to clear the background swatch and make it transparent a red line will appear through the swatch. Now the pen tool will draw black lines with no fill, which is ideal for creating outlines. Next, select the Pen Tool (shortcut: P). With the pen tool, every click creates an anchor point for the curve, and dragging allows you to define the direction and strength of the curve for that point. You may be familiar with the concept of a Bezier curve from programming that is what this tool lets you create! Click to draw straight lines with sharp corners; click-and-drag to create curves. A curved point has handles that define the direction and strength of the curve. Each handle is a dot at the end of the line coming out of the point. Follow the instructions below to use the pen tool to trace the ears of the cat, which will be simple 3-point curves. Your first point will be a curve, so start by clicking and dragging, as shown below. And yes, you can edit a line after you ve drawn it more on that below. 153

154 Conclusion Click at the start of the line and drag toward the upper-left. Click at the tip of the ear and drag out shorter handles, roughly perpendicular to the first set of handles. Click at the end of the line and drag a handle out until the line looks right. Hold the command key and click anywhere else on the canvas to finish creating that line. Use the Selection Tool (shortcut: V) to click on the line you just created. At the top of the screen, increase the stroke width to something more substantial, like 4px. Continue creating paths for the top of the head and the other ear. The pen tool remembers the settings for the last line you selected, so new lines will have the same stroke thickness. Great work! It can take some time to get used to the pen tool, but with a little practice, you ll create your lines with minimal fuss. Editing a path Your line won t always look exactly the way you want it on the first try, and that s OK! It s easy to adjust the shape of a line. To change a line after you ve created it, use the Direct Selection Tool (shortcut: A) to click on the line. This will highlight the points. Click on a single point to show its handles. Drag the point to move it, or drag the handles to adjust the angle and strength of the curve. Note: Illustrator has a feature called Smart Guides. If it is turned on, it will attempt to snap points, paths and even 90-degree angles automatically. It s often easier to edit your lines with Smart Guides turned off. Select View\Smart Guides (command-u) to toggle this feature. 154

155 Conclusion Creating complex lines Lines that are a mix of curves and sharp corners, like the cat s pointy cheeks, require one extra step during their creation. Rather than creating curved lines and then editing each sharp corner afterward, you can create corners while drawing the path. The key is to option-drag a handle immediately after you create it: Click and drag the first point s handle downward. Click and drag to create the second point. With the pen tool still active, option-drag the handle so it points roughly toward the next corner. This method allows you to create almost any shape of line with a single series of mouse gestures, all without having to change tools. Remember, once you ve created a line, you can always refine its shape with the Direct Selection Tool. Use this method to create the rest of corners for this line, as shown here: 155

156 Conclusion Rounding the ends of the lines By now, you may have noticed that each line is squared off at the ends. This stands out and looks bad if your lines don t meet exactly. Rounding the ends can help make your lines more visually pleasing. Press command-a to select all. In the Stroke palette (select Window\Stroke if you don t see it), set the Cap and Corner buttons to Round. If you don t see the Cap and Corner buttons, click the palette menu button at the top-right and click Show Options: Finishing the outlines Using the skills you ve learned so far, create the rest of the outlines for the cat, until all your lines are vector paths. Note: Press command-s to save. If you want to have a look at the lines I ve created at this point, go to the Resources folder and open cat-01-outlines.ai. Custom stroke widths Now that all your lines are done, your drawing looks much cleaner! You re well on your way to creating beautiful, game-ready art. It also looks a little mechanical, though, since all the lines are exactly the same width. You can use the Width Tool (shortcut: shift-w) to give your art a more hand-drawn look. Note: The width tool is only available in Illustrator CS5 and up. 156

157 Conclusion With the Width Tool, hover your cursor over the tip of the ear. It indicates it will add a new width point here. Click and drag to make the line slightly wider at that point. The line is made thicker, with a smooth transition to the end points. Create a default width profile for illustration That was easy, but there s a way to speed this up even further! Use the Selection Tool (shortcut: V) to click on the line you just edited. At the top of the screen, you ll see some options for the current path, including a popup that shows the Width Profile you just created. Click this popup and click Add to Profiles: Name it Thick in the middle. This creates a reusable width profile that you can apply to any path. Press command-a to select all the paths in your drawing, and then select your new profile from the Width Profiles popup. 157

158 Conclusion v The effect is subtle, so look closely if you don t think you see it. Now those lines really do look more like they were hand-drawn. Notice how in the picture above, the path on the left ear (the first one you made) is thicker than the others. If this happened to you, select the thicker path with the Selection Tool the screen: and change its stroke width back to 4px in the bar at the top of Using other width profiles for different line shapes In certain places, your stroke widths may still not look exactly as you d like. Select all of the eyelashes by shift-clicking them with the Selection Tool. Click the Width Profile popup and select the triangular profile: v Experiment with the width tool to customize your lines. Keep going until you re happy with how they look. The width tool is powerful, with several features not covered here. For a more detailed look, watch this video: Note: Press command-s to save your file. My version of the file at this point is in the Resources folder and is called cat-02-width-tool.ai. 158

159 Conclusion Coloring your artwork After you ve finished tweaking the widths of your lines, add some color. Start by using the Selection Tool to select the outline of the head: Select Edit\Copy (command-c), then Edit\Paste in Back (command-b) to create a copy of the paths in exactly the same position, layered behind the current paths. This will be the starting point for creating a colored shape that fits precisely behind the lines that already exist. Press shift-x to reverse the fill and stroke colors. Your fill was transparent before, so now your strokes are transparent and your fills are black. It looks a little strange, but we re about to fix that: You need to edit these points, but since they re behind and completely covered by the outline paths, it s impossible to be sure you re editing the points for the fill and not the paths for the stroke. To solve this, select Object\Group (command-g) to group the new set of paths and double-click the group to enter Isolation Mode. This lets you work on the contents of the group without any other objects getting in the way. In Isolation Mode, Illustrator fades the rest of your art a bit, showing you that you can t select it: 159

160 Conclusion You need to join all these separate paths together to make one solid shape you can color. Use the Direct Selection Tool (shortcut: A) to draw a marquee around the bottom two points to select them: Select Object\Path\Join (command-j) to join the two points. This joins the left and right paths, creating a filled path that s almost the size of cat s head, but it s still open at the top: Use the Direct Selection Tool to select the top-left point of the path. Shiftclick the left point of the curve that forms the top of the cat s head: 160

161 Conclusion Press command-j to join the points. Your cat s head should now be completely filled. The path is still open at the top right, under the ear, but that s OK. You re going to merge it with the ear shapes next. Using the Pathfinder to combine shapes The Pathfinder is a very powerful set of tools that lets you combine multiple shapes in interesting ways. You ll start by using the simplest function, Unite, to combine all three color fills into a single shape. Switch to the Selection Tool and shift-click each of the ears to add them to the current selection. In the Pathfinder palette (select Window\Pathfinder if you don t see it), click the Unite button. This merges all three paths into a single shape. With the fill shape selected, let s give it a better color. In the Swatches palette (select Window\Swatches if you don t see it), select a color for your cat: Note: If the stroke color changes instead of the fill, press command-z to undo, press X to make the fill color the active swatch, then select your color again. Double-click outside of your shape to exit Isolation Mode. Your first filled shape is done! 161

162 Conclusion Why have you colored the head separately? It s best to create a separate fill for each basic shape so that when you get to the shading stage, you can layer shading objects behind objects in front. For example, you ll layer the shading for the body behind the shape of the legs. If the fill for the body and legs were the same object, this wouldn t be possible without doing some extra trimming of the shaded area. If you re confused, sit tight this will become clear shortly. Use the same method to create a filled shape for the legs, body and tail. Remember the basic steps: 1. Use the direct selection tool to select the paths that create the outline of the area you re trying to fill. 2. Select Edit\Copy (command-c), then Edit\Paste in Back (command-b) to create a copy of the paths in exactly the same position. 3. Press shift-x to reverse the fill and stroke colors. 4. Select Object\Group (command-g) to group the new set of paths. 5. Double-click the group to enter Isolation Mode. 6. As necessary, combine the paths with the direct selection tool and command-j, or via the Unite button in Pathfinder. Once you re done, use the Selection Tool to select the lines and fill for the tail and send them to the back by pressing command-shift-[. 162

163 Conclusion Note: Press command-s to save. Refer to cat-03-basic-fill.ai in the Resources folder to start from here. Coloring the eyes Next, add some color to the eyes. Select the outer, egg-shaped ovals for both eyes with the Selection Tool. Copy the paths (command-c) and paste behind (command-b). Swap the stroke and fill colors (shift-x) to create a filled path with no stroke. Click the white swatch in the Swatches Palette to change the fill color to white. Go through the following steps to complete the eyes: With the Selection Tool (V), click the top arc of the iris. Press command- C to copy it, then command-b to paste behind. Press shift-x to swap the fill and stroke colors. Double-click the new black shape to enter Isolation Mode. 163

164 Conclusion This shape isn t big enough for the iris. To make it the right shape, use the Pen Tool (P) and click the end point of the path. Then click once for each point shown here, covering the lower part of the eye with the new shape. Double-click outside of the shape to leave Isolation Mode. You need a duplicate of the white eye-shape to use for the Pathfinder operation. Use the Selection Tool (V) to click it. Press command- C to copy and command- F to paste in front. Still using the Selection Tool, hold shift and click the shape you just created for the iris. In the Pathfinder Palette, click the Intersect button. The shapes are combined, leaving just the shape of the iris. With the iris shape still selected, click a green color in the Colors Palette. Click empty space on your document to deselect. Click the black swatch in the Swatches Palette. Then use the Ellipse Tool (L) to draw a black circle for the pupil. Now you need a duplicate of the iris shape to use for the Pathfinder operation. Use the Selection Tool (V) to select it. Press command-c and then command-f to create the duplicate. 164

165 Conclusion Hold shift and click the pupil to have both shapes selected. In the Pathfinder Palette, click the Intersect button. You re left with the iris and pupil looking very nice. Using the Ellipse Tool (L), draw a white circle to use for a highlight on the eye. Repeat this entire process for the other eye. Select the curves for the tops of the irises and change them to a dark green. Note: Press command-s to save. Refer to cat-04-eyes.ai in the Resources folder to start from here. Coloring certain areas You ll start adding extra color to your cat by making the end of the tail and the two front paws white. You need a shape that is white with no stroke. Press D to set the default fill (white) and stroke (black). Make sure the stroke swatch is on top of the fill swatch, as shown here: If it isn t, press X to switch them, bringing the stroke to the front. Press the forward slash key (/) to remove the stroke. Follow the steps below: 165

166 Conclusion Using the Pen Tool, draw a curved line across the tail, near the top. The curve should extend out past the edges of the tail, as shown. You ll trim it down later. Continue the path around the end of the tail by clicking once for each point. Make sure you cover the end of the tail completely. The shape doesn t need to be closed the Pathfinder tools work just as well with open paths. Use the Selection Tool (V) to select the tail, press command-c to copy it, then press command-f to paste in front. You ll now use this copy of the tail shape with the Pathfinder to make the white path match the shape of the tail. Hold the shift key and click the white path so it s also selected. For the Pathfinder to work, it s important that both the tail shape and the white shape you just created are selected, that they are the only shapes selected, and that they are overlapping. 166

167 Conclusion In the Pathfinder palette, click the Intersect button. The parts of each shape that are not overlapping are trimmed away. What remains is a single shape for the white end of the tail. A word about layers I ve spoken a bit about layers and the layering of objects. Let s look more closely at how this works. Look at your Layers Palette currently there are two layers, one for your sketch, Layer 1, and one for your vector artwork, Layer 2. Click the disclosure triangle next to Layer 2: This reveals all the objects that are within that layer: An object is a single path or shape. A single layer can hold any number of objects. As you can see, you ve created a lot of objects in Layer 2 already! The Layers 167

168 Conclusion Palette shows the objects in order of their layering. Objects at the top of the list appear on screen in front of objects farther down the list. Reordering object layers You can change object layering by dragging an object s name up or down in the Layers Palette, or you can use Object\Arrange\Send Backward (command-[) or Object\Arrange\Send Forward (command-]). From now on, when I refer to object layers, I mean the layering of objects inside Layer 2. You don t need to create new primary layers, or change the order of the two primary layers you have. Getting back to your cat s tail, the white shape at the tip of the tail is currently sitting in front of the black outlines for the tail. You could press command-[ until it s behind the outlines, but that can get tedious in a document with many objects. Instead, select the white shape for the end of the tail, press command-x to cut it, and then select the object you want it to appear in front of in this case, the color fill shape for the entire tail and press command-f to paste in front. This is the fastest way to get an object exactly where you want it in the layer stack. Use this method to paste the white tip of the tail in front of the fill for the tail. It should now show up behind the stroke: v Follow the same steps to create a white fill for the front paws. As a reminder, here are the basic steps: 1. Use the Pen Tool to draw a shape that colors the area in the paws you want filled, overlapping the area outside the paws. 2. Select the paws and press command-c to copy them; then press command-f to paste in front. 3. Hold the shift key and click the white path so it s also selected. 4. In the Pathfinder palette, click the Intersect button. 5. Use Object\Arrange\Send Backward (command-[) until the white shape is properly behind the black strokes, as you can see below. 168

169 Conclusion The last piece to add is an oval around the cat s mouth and nose. Use the Ellipse Tool (shortcut: L) to create one: To get the ellipse into the right place in the layer stack, use the paste in front technique to cut the white ellipse and paste it in front of the color fill for the head. This will place it behind all the strokes for the face. Checkpoint! Press command-s to save. If you wish, you can start from here using the file cat-05-tail-and-toes.ai from the Resources folder. A bit about shadow and light Typically, in-game lighting comes from the top-right. This is because many games have a general progression from left to right, so lighting from the top-right highlights the hero s face as they progress. With the light shining from the top-right, shadows will fall on the lower-left areas of any given object: 169

170 Conclusion To create shadows like this in Illustrator, you ll use almost the same Pathfinder technique you used to create the white areas for the tail and paws. Start by using the Selection Tool (P) to select the fill for the body: Recall that two shapes are required for a Pathfinder operation. Press command-c to copy it. Then press command-f twice. Drag the currently selected shape (the topmost one) up and to the right a bit, shown here in black so it s easier to see. I also dragged the bottomright resize handle up and left a bit to make it smaller. Hold shift and click the first copy you made of the body shape so that they re both selected. 170

171 Conclusion In the Pathfinder palette, click the Subtract button. The front shape is subtracted from the back shape. What remains will work great for a shadow on the body. If you haven t already changed the color to black, click Fill box in the toolbar on the left to bring it forward, then click the black swatch in the Swatches palette. In the Transparency palette (select Window\Transparency if you don t see it), change the opacity to 30%. Remember to rearrange the layers. Cut the shadow shape, select the body fill shape, then paste in front (command-f), as described previously. Use these steps to create shaded areas for the front legs, head and tail. Here are reminders of the steps to follow: 1. Select the area you want to shade with the Selection Tool. 2. Create two copies of the shape by pressing command-c to copy it, then press command-f twice. 3. Move one of the copies up and to the right a bit, optionally resizing it. 4. Shift-click to select the original shape as well, and use Pathfinder\Subtract to create a new shadow area. 171

172 Conclusion 5. Set the color of the shadow area to black and set the transparency to 30%. Remember: Copy the base shape, paste twice, drag the top shape up and to the right and use the Pathfinder to subtract it from the first copy. Then, change the opacity and make sure it s layered correctly. Extra shadow details for more depth Adding extra shadows can help define some of the shapes in your art, providing a sense of depth even where there are no outlines. For example, in the image below, you can see shark-fin-like shapes used to show fur sticking up in front of the ears, with the shaded areas forming the inner ears themselves. Create these by drawing a black path with the pen tool and then setting its transparency to 30%. Here are the abbreviated steps: 172

173 Conclusion Click and drag. Click and drag. Option-drag the handle. Click and drag. Option-drag the handle. Click and drag. Option-drag the handle. Click once (for a sharp corner with no handles). Click and drag. Option-drag the handle. Click once on the first point to close the shape. In the same way, add depth to the tufts of hair at the end of the tail. Imagine extending the inner line down further into the tail, then curving back up to the tip, and you have the shape for your tail shadows: 173

174 Conclusion Save! Press command-s to save your progress. A version of the file at this point is in the Resources folder and is called cat-06-shaded.ai. Coloring the outlines The final step is to color the outlines. Some game art will look great with black outlines, but usually, coloring the outlines brings out the color of the object and gives it even more depth. Start by using the Selection Tool to select any one of the black outlines. In the menu bar, choose Select\Same\Stroke Color. This will select all the black lines in the document. Have a look at the swatches and make sure the stroke swatch is on top of the fill: If it s not, click on it to bring it forward or press X to swap them. Click on a slightly darker version of your fill color in the Swatches palette to change your stroke color. 174

175 Conclusion Some of your strokes could stay black (the nose) or be dark grey (the whiskers and mouth). Use the Selection Tool (P) to select the strokes you want to change and then click the color in the Swatches palette that you want to use. This is mostly a matter of preference, so play around with the outlines until they look just the way you want. You ve finished your cat! Don t forget to save. Press command-s to do so now. Note: A finished version of the Illustrator file is in the Resources folder as cat- 07-finished.ai. Exporting PNG files In Illustrator, the Artboard refers to the dimensions of the document in which you re working. At the beginning of the chapter, you set the artboard to 640x1136 to match an iphone 5 screen. If you were to export a PNG of your cat now, it would have a lot of unnecessary transparent space around the edges of the cat image. Before you export the cat image, you need to shrink down the Artboard to match the size of the cat itself. Press command-a to select all the art on the canvas. Select Object\Artboards\Fit to Selected Art to make the Artboard match the size of the art. 175

176 Conclusion That was easy! Select File\Save for Web At the top-right, choose the PNG-24 preset and click Save. PNG-24 will save a full-quality PNG file with transparency and is the preferred graphic format for all ios applications. ios games require two versions of your graphics Retina and non-retina. You created your Illustrator document at Retina resolution for iphone (1136x640), so begin by exporting the Retina version of your graphic. to the end of the filename for example, cat@2x.png and click Save. Next, create the non-retina version of the graphic, which is scaled down by 50%. Select File\Save for Web again. In the Image Size panel, set Percent to 50 and click Save to create the non-retina version of the graphic. Make sure you use the same filename as in the previous step, but that is, cat.png. 176

177 Conclusion Congratulations! You ve taken a blank piece of paper and turned it into a resolutionindependent piece of game art, ready to use in your next project. Challenges Want to brush up your artistic skills even more? Here are two more characters you can use as guides to practice the techniques you ve learned in this chapter. As always, if you get stuck, you can find the solutions in the resources for this chapter but give it your best shot first! Challenge 1: Squeak! Your first challenge is to draw one of the simplest possible characters: a lowly mouse. The shape is nice and simple so you can quickly practice going through the steps you ve learned in this chapter all on your own. 177

178 Conclusion Challenge 2: Woof! For a greater challenge, how about making a dog? This one involves more complicated shapes and more shadows to match. If you ve made both of these characters, huge congratulations you now have some solid tools on your belt to make simple 2D game art! Be sure to keep experimenting and most importantly, have fun. The key to developing your skills as an artist and as a programmer is practice, practice, practice. If you d like to share any of the artwork you create after following this chapter, we d love to see it. Please stop by the book forums for show and tell! 178

2D ios & tvos Games. by Tutorials. Bonus Chapters

2D ios & tvos Games. by Tutorials. Bonus Chapters 2D ios & tvos Games by Tutorials Bonus Chapters Table of Contents: Overview Chapter 25: Game Center Achievements... 6 Chapter 26: Game Center Leaderboards... 32 Chapter 27: ReplayKit... 49 Chapter 28:

More information

Assignment II: Set. Objective. Materials

Assignment II: Set. Objective. Materials Assignment II: Set Objective The goal of this assignment is to give you an opportunity to create your first app completely from scratch by yourself. It is similar enough to assignment 1 that you should

More information

Overview. The Game Idea

Overview. The Game Idea Page 1 of 19 Overview Even though GameMaker:Studio is easy to use, getting the hang of it can be a bit difficult at first, especially if you have had no prior experience of programming. This tutorial is

More information

Space Invadersesque 2D shooter

Space Invadersesque 2D shooter Space Invadersesque 2D shooter So, we re going to create another classic game here, one of space invaders, this assumes some basic 2D knowledge and is one in a beginning 2D game series of shorts. All in

More information

understanding sensors

understanding sensors The LEGO MINDSTORMS EV3 set includes three types of sensors: Touch, Color, and Infrared. You can use these sensors to make your robot respond to its environment. For example, you can program your robot

More information

The Beauty and Joy of Computing Lab Exercise 10: Shall we play a game? Objectives. Background (Pre-Lab Reading)

The Beauty and Joy of Computing Lab Exercise 10: Shall we play a game? Objectives. Background (Pre-Lab Reading) The Beauty and Joy of Computing Lab Exercise 10: Shall we play a game? [Note: This lab isn t as complete as the others we have done in this class. There are no self-assessment questions and no post-lab

More information

My Earnings from PeoplePerHour:

My Earnings from PeoplePerHour: Hey students and everyone reading this post, since most of the readers of this blog are students, that s why I may call students throughout this post. Hope you re doing well with your educational activities,

More information

Term Definition Introduced in:

Term Definition Introduced in: 60 Minutes of Access Secrets Key Terms Term Definition Introduced in: Calculated Field A field that displays the results of a calculation. Introduced in Access 2010, this field allows you to make calculations

More information

Set Up Your Domain Here

Set Up Your Domain Here Roofing Business BLUEPRINT WordPress Plugin Installation & Video Walkthrough Version 1.0 Set Up Your Domain Here VIDEO 1 Introduction & Hosting Signup / Setup https://s3.amazonaws.com/rbbtraining/vid1/index.html

More information

Bridgemate App. Information for bridge clubs and tournament directors. Version 2. Bridge Systems BV

Bridgemate App. Information for bridge clubs and tournament directors. Version 2. Bridge Systems BV Bridgemate App Information for bridge clubs and tournament directors Version 2 Bridge Systems BV Bridgemate App Information for bridge clubs and tournament directors Page 2 Contents Introduction... 3 Basic

More information

TABLE OF CONTENTS. Logging into the Website Homepage and Tab Navigation Setting up Users on the Website Help and Support...

TABLE OF CONTENTS. Logging into the Website Homepage and Tab Navigation Setting up Users on the Website Help and Support... TABLE OF CONTENTS Logging into the Website...02 Homepage and Tab Navigation...03 Setting up Users on the Website...08 Help and Support...10 Uploding and Managing Photos...12 Using the Yearbook Ladder...16

More information

CONCEPTS EXPLAINED CONCEPTS (IN ORDER)

CONCEPTS EXPLAINED CONCEPTS (IN ORDER) CONCEPTS EXPLAINED This reference is a companion to the Tutorials for the purpose of providing deeper explanations of concepts related to game designing and building. This reference will be updated with

More information

Solving tasks and move score... 18

Solving tasks and move score... 18 Solving tasks and move score... 18 Contents Contents... 1 Introduction... 3 Welcome to Peshk@!... 3 System requirements... 3 Software installation... 4 Technical support service... 4 User interface...

More information

Frequently Asked Questions

Frequently Asked Questions Frequently Asked Questions Index Frequently Asked Questions... 1 Being a Mystery Shopper... 3 What is a mystery shopper?... 3 How can I become a mystery shopper?... 3 What are you looking for in a mystery

More information

Official Documentation

Official Documentation Official Documentation Doc Version: 1.0.0 Toolkit Version: 1.0.0 Contents Technical Breakdown... 3 Assets... 4 Setup... 5 Tutorial... 6 Creating a Card Sets... 7 Adding Cards to your Set... 10 Adding your

More information

COMPUTING CURRICULUM TOOLKIT

COMPUTING CURRICULUM TOOLKIT COMPUTING CURRICULUM TOOLKIT Pong Tutorial Beginners Guide to Fusion 2.5 Learn the basics of Logic and Loops Use Graphics Library to add existing Objects to a game Add Scores and Lives to a game Use Collisions

More information

2809 CAD TRAINING: Part 1 Sketching and Making 3D Parts. Contents

2809 CAD TRAINING: Part 1 Sketching and Making 3D Parts. Contents Contents Getting Started... 2 Lesson 1:... 3 Lesson 2:... 13 Lesson 3:... 19 Lesson 4:... 23 Lesson 5:... 25 Final Project:... 28 Getting Started Get Autodesk Inventor Go to http://students.autodesk.com/

More information

Words Mobile Ready Game Documentation

Words Mobile Ready Game Documentation Words Mobile Ready Game Documentation Joongly games 2016 Words Mobile Ready Game Contents Overview... 3 Quick Start... 3 Game rules... 4 Basics... 4 Board... 4 Tiles... 4 Extra Point Values... 4 Game start...

More information

The Joy of SVGs CUT ABOVE. pre training series 2. svg design Course. Jennifer Maker. CUT ABOVE SVG Design Course by Jennifer Maker

The Joy of SVGs CUT ABOVE. pre training series 2. svg design Course. Jennifer Maker. CUT ABOVE SVG Design Course by Jennifer Maker CUT ABOVE svg design Course pre training series 2 The Joy of SVGs by award-winning graphic designer and bestselling author Jennifer Maker Copyright Jennifer Maker page 1 please Do not copy or share Session

More information

An easy user guide AN EASY USER GUIDE

An easy user guide AN EASY USER GUIDE AN EASY USER GUIDE 1 Hello! Welcome to our easy user guide to Create my Support Plan. We have created this guide to help you start using Create my Support Plan. And we hope that you will find it useful.

More information

PAGE 1 THE PERFECT WORDPRESS DEVELOPMENT WORKFLOW

PAGE 1 THE PERFECT WORDPRESS DEVELOPMENT WORKFLOW PAGE 1 THE PERFECT WORDPRESS DEVELOPMENT WORKFLOW There are a lot of steps in the development process, so to help you jump exactly where you need to be, here are the different topics we ll cover in this

More information

Welcome to Storyist. The Novel Template This template provides a starting point for a novel manuscript and includes:

Welcome to Storyist. The Novel Template This template provides a starting point for a novel manuscript and includes: Welcome to Storyist Storyist is a powerful writing environment for ipad that lets you create, revise, and review your work wherever inspiration strikes. Creating a New Project When you first launch Storyist,

More information

Adding in 3D Models and Animations

Adding in 3D Models and Animations Adding in 3D Models and Animations We ve got a fairly complete small game so far but it needs some models to make it look nice, this next set of tutorials will help improve this. They are all about importing

More information

Annex IV - Stencyl Tutorial

Annex IV - Stencyl Tutorial Annex IV - Stencyl Tutorial This short, hands-on tutorial will walk you through the steps needed to create a simple platformer using premade content, so that you can become familiar with the main parts

More information

Game Technologies for Apple Watch

Game Technologies for Apple Watch Graphics and Games #WWDC16 Game Technologies for Apple Watch Session 612 Christy Warren Game Technologies Engineer Fatima Broom Game Technologies Engineer Tyler Casella Game Technologies Engineer 2016

More information

Blackfin Online Learning & Development

Blackfin Online Learning & Development Presentation Title: Introduction to VisualDSP++ Tools Presenter Name: Nicole Wright Chapter 1:Introduction 1a:Module Description 1b:CROSSCORE Products Chapter 2: ADSP-BF537 EZ-KIT Lite Configuration 2a:

More information

INTRODUCTION. Welcome to Subtext the first community in the pages of your books.

INTRODUCTION. Welcome to Subtext the first community in the pages of your books. INTRODUCTION Welcome to Subtext the first community in the pages of your books. Subtext allows you to engage in conversations with friends and like-minded readers and access all types of author and expert

More information

Kismet Interface Overview

Kismet Interface Overview The following tutorial will cover an in depth overview of the benefits, features, and functionality within Unreal s node based scripting editor, Kismet. This document will cover an interface overview;

More information

Addendum 18: The Bezier Tool in Art and Stitch

Addendum 18: The Bezier Tool in Art and Stitch Addendum 18: The Bezier Tool in Art and Stitch About the Author, David Smith I m a Computer Science Major in a university in Seattle. I enjoy exploring the lovely Seattle area and taking in the wonderful

More information

Welcome To Noodle Live

Welcome To Noodle Live Features Guide Welcome To Noodle Live We re here to revolutionalise the way you collect, store and share information at events - it s time to wave goodbye to endless flyers and tatty event programmes.

More information

Once your church has set up the Church App for Seraphim, you can now download the app onto your mobile device from the the App Store or Google Play.

Once your church has set up the Church App for Seraphim, you can now download the app onto your mobile device from the the App Store or Google Play. Once your church has set up the Church App for Seraphim, you can now download the app onto your mobile device from the the App Store or Google Play. Once the app has completed downloading, open the app.

More information

Shoot It Game Template - 1. Tornado Bandits Studio Shoot It Game Template - Documentation.

Shoot It Game Template - 1. Tornado Bandits Studio Shoot It Game Template - Documentation. Shoot It Game Template - 1 Tornado Bandits Studio Shoot It Game Template - Documentation Shoot It Game Template - 2 Summary Introduction 4 Game s stages 4 Project s structure 6 Setting the up the project

More information

DESIGN A SHOOTING STYLE GAME IN FLASH 8

DESIGN A SHOOTING STYLE GAME IN FLASH 8 DESIGN A SHOOTING STYLE GAME IN FLASH 8 In this tutorial, you will learn how to make a basic arcade style shooting game in Flash 8. An example of the type of game you will create is the game Mozzie Blitz

More information

Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Apple ios Devices 2015

Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Apple ios Devices 2015 Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Apple ios Devices 2015 The Liverpool Public Library, the larger Onondaga County system, and libraries all over the country, subscribe

More information

Race for Your Life. Brake. w Look back. y Steer Checkpoint reset < Pause Free look. C Accelerate. x Change camera

Race for Your Life. Brake. w Look back. y Steer Checkpoint reset < Pause Free look. C Accelerate. x Change camera CONTENTs 1 Introduction 1 Default Control Layout 2 Game Screen 4 The Cars 4 Checkpoint Resets 4 Gas Stations 5 Driver Abilities 5 Driver Profile 5 Challenge Series 6 Game Modes 6 Online Multiplayer Racing

More information

Photo Editing in Mac and ipad and iphone

Photo Editing in Mac and ipad and iphone Page 1 Photo Editing in Mac and ipad and iphone Switching to Edit mode in Photos for Mac To edit a photo you ll first need to double-click its thumbnail to open it for viewing, and then click the Edit

More information

SAVING, LOADING AND REUSING LAYER STYLES

SAVING, LOADING AND REUSING LAYER STYLES SAVING, LOADING AND REUSING LAYER STYLES In this Photoshop tutorial, we re going to learn how to save, load and reuse layer styles! Layer styles are a great way to create fun and interesting photo effects

More information

VACUUM MARAUDERS V1.0

VACUUM MARAUDERS V1.0 VACUUM MARAUDERS V1.0 2008 PAUL KNICKERBOCKER FOR LANE COMMUNITY COLLEGE In this game we will learn the basics of the Game Maker Interface and implement a very basic action game similar to Space Invaders.

More information

Welcome to JigsawBox!! How to Get Started Quickly...

Welcome to JigsawBox!! How to Get Started Quickly... Welcome to JigsawBox!! How to Get Started Quickly... Welcome to JigsawBox Support! Firstly, we want to let you know that you are NOT alone. Our JigsawBox Customer Support is on hand Monday to Friday to

More information

far- Play Developers Manual

far- Play Developers Manual far- Play Developers Manual The Main Page To log into the far- Play developer s suite, visit the projects website: http://hypatia.cs.ualberta.ca/aarg_project/far- play/. Click on the Login button and enter

More information

C# Tutorial Fighter Jet Shooting Game

C# Tutorial Fighter Jet Shooting Game C# Tutorial Fighter Jet Shooting Game Welcome to this exciting game tutorial. In this tutorial we will be using Microsoft Visual Studio with C# to create a simple fighter jet shooting game. We have the

More information

Easy Input Helper Documentation

Easy Input Helper Documentation Easy Input Helper Documentation Introduction Easy Input Helper makes supporting input for the new Apple TV a breeze. Whether you want support for the siri remote or mfi controllers, everything that is

More information

MODULE 1 IMAGE TRACE AND BASIC MANIPULATION IN ADOBE ILLUSTRATOR. The Art and Business of Surface Pattern Design

MODULE 1 IMAGE TRACE AND BASIC MANIPULATION IN ADOBE ILLUSTRATOR. The Art and Business of Surface Pattern Design The Art and Business of Surface Pattern Design MODULE 1 IMAGE TRACE AND BASIC MANIPULATION IN ADOBE ILLUSTRATOR The Art and Business of Surface Pattern Design 1 Hi everybody and welcome to our Make it

More information

04. Two Player Pong. 04.Two Player Pong

04. Two Player Pong. 04.Two Player Pong 04.Two Player Pong One of the most basic and classic computer games of all time is Pong. Originally released by Atari in 1972 it was a commercial hit and it is also the perfect game for anyone starting

More information

Mod Kit Instructions

Mod Kit Instructions Mod Kit Instructions So you ve decided to build your own Hot Lava Level Mod. Congratulations! You ve taken the first step in building a hotter, more magmatic world. So what now? Hot Lava Tip: First off,

More information

PHOTOSHOP PUZZLE EFFECT

PHOTOSHOP PUZZLE EFFECT PHOTOSHOP PUZZLE EFFECT In this Photoshop tutorial, we re going to look at how to easily create a puzzle effect, allowing us to turn any photo into a jigsaw puzzle! Or at least, we ll be creating the illusion

More information

Final Project: NOTE: The final project will be due on the last day of class, Friday, Dec 9 at midnight.

Final Project: NOTE: The final project will be due on the last day of class, Friday, Dec 9 at midnight. Final Project: NOTE: The final project will be due on the last day of class, Friday, Dec 9 at midnight. For this project, you may work with a partner, or you may choose to work alone. If you choose to

More information

Scratch for Beginners Workbook

Scratch for Beginners Workbook for Beginners Workbook In this workshop you will be using a software called, a drag-anddrop style software you can use to build your own games. You can learn fundamental programming principles without

More information

Photoshop CS6 automatically places a crop box and handles around the image. Click and drag the handles to resize the crop box.

Photoshop CS6 automatically places a crop box and handles around the image. Click and drag the handles to resize the crop box. CROPPING IMAGES In Photoshop CS6 One of the great new features in Photoshop CS6 is the improved and enhanced Crop Tool. If you ve been using earlier versions of Photoshop to crop your photos, you ll find

More information

How to Blog to the Vanguard Website

How to Blog to the Vanguard Website How to Blog to the Vanguard Website Guidance and Rules for Blogging on the Vanguard Website Version 1.01 March 2018 Step 1. Get an account The bristol vanguard website, like much of the internet these

More information

Enhanced Push-to-Talk Application for iphone

Enhanced Push-to-Talk Application for iphone AT&T Business Mobility Enhanced Push-to-Talk Application for iphone Standard Version Release 8.3 Table of Contents Introduction and Key Features 2 Application Installation & Getting Started 2 Navigating

More information

QUICK-START FOR UNIVERSAL VLS 4.6 LASER!

QUICK-START FOR UNIVERSAL VLS 4.6 LASER! QUICK-START FOR UNIVERSAL VLS 4.6 LASER! The laser is quite safe to use, but it is powerful; using it requires your full caution, attention and respect. Some rules of the road: Rules of the road If you

More information

QUICK-START FOR UNIVERSAL VLS 4.6 LASER! FRESH 21 SEPTEMBER 2017

QUICK-START FOR UNIVERSAL VLS 4.6 LASER! FRESH 21 SEPTEMBER 2017 QUICK-START FOR UNIVERSAL VLS 4.6 LASER! FRESH 21 SEPTEMBER 2017 The laser is quite safe to use, but it is powerful; using it requires your full caution, attention and respect. Some rules of the road:

More information

Signaling Crossing Tracks and Double Track Junctions

Signaling Crossing Tracks and Double Track Junctions Signaling Crossing Tracks and Double Track Junctions Welcome. In this tutorial, we ll discuss tracks that cross each other and how to keep trains from colliding when they reach the crossing at the same

More information

G E O S E R V E R I N S TA L L

G E O S E R V E R I N S TA L L G E O S E R V E R I N S TA L L Installation and Configuration of GeoServer 01 January 2009 TABLE OF CONTENTS 1 Goals...3 2 Java Development Kit Install:...4 3 Set JAVA_HOME...6 4 GeoServer install...7

More information

NMC Second Life Educator s Skills Series: How to Make a T-Shirt

NMC Second Life Educator s Skills Series: How to Make a T-Shirt NMC Second Life Educator s Skills Series: How to Make a T-Shirt Creating a t-shirt is a great way to welcome guests or students to Second Life and create school/event spirit. This article of clothing could

More information

Welcome to Ancestry!

Welcome to Ancestry! Welcome to Ancestry! The purpose of this worksheet is to help you get familiar with the capabilities of www.ancestry.com. If you get stuck, please ask for help. You will not be turning this in, so feel

More information

ParentZone. Your guide to accessing your child s account and their learning journey.

ParentZone. Your guide to accessing your child s account and their learning journey. ParentZone Your guide to accessing your child s account and their learning journey. Accessing ParentZone Shortly after your child has started, you will receive an email to one or both of your registered

More information

Create a CaFE Account (for those who do not have one) In order to submit entries for the FWS Annual Exhibition and/or the Online Show, you need to:

Create a CaFE Account (for those who do not have one) In order to submit entries for the FWS Annual Exhibition and/or the Online Show, you need to: Using CaFE (www.callforentry.org) to Enter FWS Exhibitions To enter calls to artists for FWS shows or any calls on CaFE, you will need to: 1. Create a CaFE account. It s free and really easy to use instructions

More information

Cricut Design Space App for ipad User Manual

Cricut Design Space App for ipad User Manual Cricut Design Space App for ipad User Manual Cricut Explore design-and-cut system From inspiration to creation in just a few taps! Cricut Design Space App for ipad 1. ipad Setup A. Setting up the app B.

More information

The original photo. The final result.

The original photo. The final result. giving a photo painted edges In this Adobe Photoshop tutorial, we re going to combine a couple of different effects. First, we ll give the photo easy-tocreate painted edges, and then we ll make it look

More information

PSoC Academy: How to Create a PSoC BLE Android App Lesson 9: BLE Robot Schematic 1

PSoC Academy: How to Create a PSoC BLE Android App Lesson 9: BLE Robot Schematic 1 1 All right, now we re ready to walk through the schematic. I ll show you the quadrature encoders that drive the H-Bridge, the PWMs, et cetera all the parts on the schematic. Then I ll show you the configuration

More information

Welcome to the Sudoku and Kakuro Help File.

Welcome to the Sudoku and Kakuro Help File. HELP FILE Welcome to the Sudoku and Kakuro Help File. This help file contains information on how to play each of these challenging games, as well as simple strategies that will have you solving the harder

More information

Okay, that s enough talking. Let s get things started. Here s the photo I m going to be using in this tutorial: The original photo.

Okay, that s enough talking. Let s get things started. Here s the photo I m going to be using in this tutorial: The original photo. add visual interest with the rule of thirds In this Photoshop tutorial, we re going to look at how to add more visual interest to our photos by cropping them using a simple, tried and true design trick

More information

Custom Mobile App Support

Custom Mobile App Support Custom Mobile App Support FBBC by Samantha Taylor App Scheduling app for Fit Body Boot Camp and Samantha Taylor Fitness. You can prebook your workouts, check in for your workout or cancel it, all through

More information

Blend Photos With Apply Image In Photoshop

Blend Photos With Apply Image In Photoshop Blend Photos With Apply Image In Photoshop Written by Steve Patterson. In this Photoshop tutorial, we re going to learn how easy it is to blend photostogether using Photoshop s Apply Image command to give

More information

Welcome to Family Dominoes!

Welcome to Family Dominoes! Welcome to Family Dominoes!!Family Dominoes from Play Someone gets the whole family playing everybody s favorite game! We designed it especially for the ipad to be fun, realistic, and easy to play. It

More information

ROOMPLAYER GUIDE COMPLETE YOUR ROOMPLAYER SETUP WITH THE ROOMPLAYER DESKTOP APP

ROOMPLAYER GUIDE COMPLETE YOUR ROOMPLAYER SETUP WITH THE ROOMPLAYER DESKTOP APP ROOMPLAYER GUIDE COMPLETE YOUR ROOMPLAYER SETUP WITH THE ROOMPLAYER DESKTOP APP HELLO Once you ve connected your Roomplayer to your home network, downloading and installing the Roomplayer desktop app is

More information

UNDERSTANDING LAYER MASKS IN PHOTOSHOP

UNDERSTANDING LAYER MASKS IN PHOTOSHOP UNDERSTANDING LAYER MASKS IN PHOTOSHOP In this Adobe Photoshop tutorial, we re going to look at one of the most essential features in all of Photoshop - layer masks. We ll cover exactly what layer masks

More information

2D Platform. Table of Contents

2D Platform. Table of Contents 2D Platform Table of Contents 1. Making the Main Character 2. Making the Main Character Move 3. Making a Platform 4. Making a Room 5. Making the Main Character Jump 6. Making a Chaser 7. Setting Lives

More information

Would You Like To Earn $1000 s With The Click Of A Button?

Would You Like To Earn $1000 s With The Click Of A Button? Would You Like To Earn $1000 s With The Click Of A Button? (Follow these easy step by step instructions and you will) - 100% Support and all questions answered! - Make financial stress a thing of the past!

More information

FAQ for City of Tacoma employees

FAQ for City of Tacoma employees General: How do I update my contact information (address, phone number, email address)? How do I change my password? Forgot password Forgot username How do I favorite or bookmark the login page? Can I

More information

Ansible Tower Quick Setup Guide

Ansible Tower Quick Setup Guide Ansible Tower Quick Setup Guide Release Ansible Tower 3.2.2 Red Hat, Inc. Mar 08, 2018 CONTENTS 1 Quick Start 2 2 Login as a Superuser 3 3 Import a License 5 4 Examine the Tower Dashboard 7 5 The Settings

More information

Table of Contents. Creating Your First Project 4. Enhancing Your Slides 8. Adding Interactivity 12. Recording a Software Simulation 19

Table of Contents. Creating Your First Project 4. Enhancing Your Slides 8. Adding Interactivity 12. Recording a Software Simulation 19 Table of Contents Creating Your First Project 4 Enhancing Your Slides 8 Adding Interactivity 12 Recording a Software Simulation 19 Inserting a Quiz 24 Publishing Your Course 32 More Great Features to Learn

More information

Getting Started with the micro:bit

Getting Started with the micro:bit Page 1 of 10 Getting Started with the micro:bit Introduction So you bought this thing called a micro:bit what is it? micro:bit Board DEV-14208 The BBC micro:bit is a pocket-sized computer that lets you

More information

More Actions: A Galaxy of Possibilities

More Actions: A Galaxy of Possibilities CHAPTER 3 More Actions: A Galaxy of Possibilities We hope you enjoyed making Evil Clutches and that it gave you a sense of how easy Game Maker is to use. However, you can achieve so much with a bit more

More information

Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Android Devices, Including the Kindle Fire

Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Android Devices, Including the Kindle Fire Tech Tips from Mr G Borrowing ebooks and Audiobooks Using OverDrive 3.2 on Android Devices, Including the Kindle Fire - 2015 The Liverpool Public Library, the larger Onondaga County system, and libraries

More information

The Basics. Introducing PaintShop Pro X4 CHAPTER 1. What s Covered in this Chapter

The Basics. Introducing PaintShop Pro X4 CHAPTER 1. What s Covered in this Chapter CHAPTER 1 The Basics Introducing PaintShop Pro X4 What s Covered in this Chapter This chapter explains what PaintShop Pro X4 can do and how it works. If you re new to the program, I d strongly recommend

More information

RAZER CENTRAL ONLINE MASTER GUIDE

RAZER CENTRAL ONLINE MASTER GUIDE RAZER CENTRAL ONLINE MASTER GUIDE CONTENTS 1. RAZER CENTRAL... 2 2. SIGNING IN... 3 3. RETRIEVING FORGOTTEN PASSWORDS... 4 4. CREATING A RAZER ID ACCOUNT... 7 5. USING RAZER CENTRAL... 11 6. SIGNING OUT...

More information

Online Courses with the Writers Workshop

Online Courses with the Writers Workshop Online Courses with the Writers Workshop Welcome Thank you for booking a course with the Writers Workshop. You ve made a good choice! We ve got passionate, expert tutors and we have a formidable record

More information

Facebook Fan Page Secrets... 3 Section 1 Social Media Optimization... 4 Set Up Your Facebook Page... 4 Section 2 Fan Page Customization...

Facebook Fan Page Secrets... 3 Section 1 Social Media Optimization... 4 Set Up Your Facebook Page... 4 Section 2 Fan Page Customization... Facebook Fan Page Secrets... 3 Section 1 Social Media Optimization... 4 Set Up Your Facebook Page... 4 Section 2 Fan Page Customization... 6 Legitimize Your URL... 6 Customize the Look of Your Page...

More information

The Joy of SVGs CUT ABOVE. pre training series 3. svg design Course. Jennifer Maker. CUT ABOVE SVG Design Course by Jennifer Maker

The Joy of SVGs CUT ABOVE. pre training series 3. svg design Course. Jennifer Maker. CUT ABOVE SVG Design Course by Jennifer Maker CUT ABOVE svg design Course pre training series 3 The Joy of SVGs by award-winning graphic designer and bestselling author Jennifer Maker Copyright Jennifer Maker page 1 please Do not copy or share Session

More information

Pass-Words Help Doc. Note: PowerPoint macros must be enabled before playing for more see help information below

Pass-Words Help Doc. Note: PowerPoint macros must be enabled before playing for more see help information below Pass-Words Help Doc Note: PowerPoint macros must be enabled before playing for more see help information below Setting Macros in PowerPoint The Pass-Words Game uses macros to automate many different game

More information

Meteor Game for Multimedia Fusion 1.5

Meteor Game for Multimedia Fusion 1.5 Meteor Game for Multimedia Fusion 1.5 Badly written by Jeff Vance jvance@clickteam.com For Multimedia Fusion 1.5 demo version Based off the class How to make video games. I taught at University Park Community

More information

33-2 Satellite Takeoff Tutorial--Flat Roof Satellite Takeoff Tutorial--Flat Roof

33-2 Satellite Takeoff Tutorial--Flat Roof Satellite Takeoff Tutorial--Flat Roof 33-2 Satellite Takeoff Tutorial--Flat Roof Satellite Takeoff Tutorial--Flat Roof A RoofLogic Digitizer license upgrades RoofCAD so that you have the ability to digitize paper plans, electronic plans and

More information

Experiment 02 Interaction Objects

Experiment 02 Interaction Objects Experiment 02 Interaction Objects Table of Contents Introduction...1 Prerequisites...1 Setup...1 Player Stats...2 Enemy Entities...4 Enemy Generators...9 Object Tags...14 Projectile Collision...16 Enemy

More information

User Guide. PTT Radio Application. ios. Release 8.3

User Guide. PTT Radio Application. ios. Release 8.3 User Guide PTT Radio Application ios Release 8.3 March 2018 1 Table of Contents 1. Introduction and Key Features... 5 2. Application Installation & Getting Started... 6 Prerequisites... 6 Download... 6

More information

In this project, you will create a memory game where you have to memorise and repeat a sequence of random colours!

In this project, you will create a memory game where you have to memorise and repeat a sequence of random colours! Memory Introduction In this project, you will create a memory game where you have to memorise and repeat a sequence of random colours! Step 1: Random colours First, let s create a character that can change

More information

Create a game in which you have to guide a parrot through scrolling pipes to score points.

Create a game in which you have to guide a parrot through scrolling pipes to score points. Raspberry Pi Projects Flappy Parrot Introduction Create a game in which you have to guide a parrot through scrolling pipes to score points. What you will make Click the green ag to start the game. Press

More information

BEST PRACTICES COURSE WEEK 14 PART 2 Advanced Mouse Constraints and the Control Box

BEST PRACTICES COURSE WEEK 14 PART 2 Advanced Mouse Constraints and the Control Box BEST PRACTICES COURSE WEEK 14 PART 2 Advanced Mouse Constraints and the Control Box Copyright 2012 by Eric Bobrow, all rights reserved For more information about the Best Practices Course, visit http://www.acbestpractices.com

More information

BLACKBOARD LEARN 9.1: BASIC TRAINING- PART 1

BLACKBOARD LEARN 9.1: BASIC TRAINING- PART 1 BLACKBOARD LEARN 9.1: BASIC TRAINING- PART 1 Beginning of Part 1 INTRODUCTION I m Karissa Greathouse, for those of you that don t know me. I think I know almost everybody in here, but some of you may not

More information

Would You Like To Earn $1000 s With The Click Of A Button?

Would You Like To Earn $1000 s With The Click Of A Button? Would You Like To Earn $1000 s With The Click Of A Button? (Follow these easy step by step instructions and you will) This e-book is for the USA and AU (it works in many other countries as well) To get

More information

Activity 6: Playing Elevens

Activity 6: Playing Elevens Activity 6: Playing Elevens Introduction: In this activity, the game Elevens will be explained, and you will play an interactive version of the game. Exploration: The solitaire game of Elevens uses a deck

More information

Editing the standing Lazarus object to detect for being freed

Editing the standing Lazarus object to detect for being freed Lazarus: Stages 5, 6, & 7 Of the game builds you have done so far, Lazarus has had the most programming properties. In the big picture, the programming, animation, gameplay of Lazarus is relatively simple.

More information

Game Design Curriculum Multimedia Fusion 2. Created by Rahul Khurana. Copyright, VisionTech Camps & Classes

Game Design Curriculum Multimedia Fusion 2. Created by Rahul Khurana. Copyright, VisionTech Camps & Classes Game Design Curriculum Multimedia Fusion 2 Before starting the class, introduce the class rules (general behavioral etiquette). Remind students to be careful about walking around the classroom as there

More information

5.0 Events and Actions

5.0 Events and Actions 5.0 Events and Actions So far, we ve defined the objects that we will be using and allocated movement to particular objects. But we still need to know some more information before we can create an actual

More information

Installation guide. Activate. Install your Broadband. Install your Phone. Install your TV. 1 min. 30 mins

Installation guide. Activate. Install your Broadband. Install your Phone. Install your TV. 1 min. 30 mins Installation guide 1 Activate Install your Broadband Install your TV 4 Install your Phone 1 min 0 mins 0 mins 5 mins INT This guide contains step-by-step instructions on how to: 1 Activate Before we do

More information

Learn what to do with results of autosomal DNA testing from AncestryDNA.

Learn what to do with results of autosomal DNA testing from AncestryDNA. When You First Get Your AncestryDNA Results Objective: Learn what to do with results of autosomal DNA testing from AncestryDNA. Tools: AncestryDNA results; ancestry.com, genesis.gedmatch.com and familytreedna.com

More information

Organizing and Customizing Content

Organizing and Customizing Content Organizing and Customizing Content JUMPSTART Session 2: Organizing and Customizing Content Welcome to this Jumpstart session on Organizing and Customizing Content. We hope you have had a chance to explore

More information

Enhanced Push-to-Talk Application for iphone

Enhanced Push-to-Talk Application for iphone AT&T Business Mobility Enhanced Push-to-Talk Application for iphone Land Mobile Radio (LMR) Version Release 8.3 Table of Contents Introduction and Key Features 2 Application Installation & Getting Started

More information