You’ll learn how to maintain both versions in a single Xcode project and how to continue development of your game without copying and pasting heaps of code to keep both versions up to date.

It’s recommended that you have some familiarity with Mac OS X Development if you choose to work through this tutorial. However, one of the wonderful features of Sprite Kit is that you don’t need to have a lot of prior experience to turn out a great app.

Getting Started

Download the starter project for this tutorial here, and build and run on your iPhone to try it out.

The starter project has a few changes from the original in order to showcase several differences between the iOS and OS X versions of the game.

Accelerometer Support

This project borrows accelerometer support from the Space Game Starter Kit which lets the player move their character up and down the screen by tilting the device, like so:

More Destructive Power

The ninja star has now been imbued with magical ninja powers of epic destruction!

Ok, maybe not that epic, but this version uses a nice particle effect to give the ninja star some visual impact.

Small Technical Improvements

Vicki Wenderlich lent her talents to the game and added a new tiled background which makes the game more aesthetically pleasing. As well, the status bar is hidden and the app now supports iPad resolutions.

Working with Projects and Targets

A project file ties all of your working files together, but what determines how your project is compiled is a target.

A single project can contain multiple targets. This lets you compile your project in several different ways, depending on which files are associated with a specific target and the specific build settings of the target. This should give you a clue as to how you’re going to set up this project to compile for OS X!

Let’s take a look at this project’s targets. Open the SpriteKitSimpleGame project, and at the top of the left panel, select your project to show the project settings. Then select SpriteKitSimpleGame in the project window.

Right now there are two targets, one for iOS and the other for iOS unit tests, as shown below:

Alternatively, you can select SpriteKitSimpleGame from the Target list if the project and target list are both expanded:

When you build an application, you build it for the device or devices supported by the target. To see which devices the current target supports, click the SpriteKitSimpleGame drop down next to the Stop button at the top-left of the window, as shown below:

Adding a Build Target for Mac OS X

Select the OS X\Application\SpriteKit Game template and click Next, as shown below:

Finally, type in the product name as SpriteKitSimpleGameMac and click Finish, like so:

Note: If you plan to release your app on the Mac App Store, your bundle identifier must be registered separately on developer.apple.com. Profiles and provisioning are arranged separately for iOS and Mac OS X developer programs.

What do you think will happen when you try to build and run your project using this new target?

A — The game will run perfectly — and that’s the end of the tutorial!

B — The game will fail to compile with numerous errors.

C — The game will compile but crash on launch.

D — The game will compile and run but won’t be the game you expected.

D then you are correct, just like a new project it will run perfectly but using the standard game template.

Don’t worry, this will soon be your amazing ninja game but first you will need to clean up this project so it is a little easier to work with.
[/spoiler]

Structuring Your Files for Multiple Targets

Now that you have a Mac OS X build target, you’ll be modifying and adding files to make the app work under OS X. Keeping track of these files can be difficult as time goes on, so it’s best to set up a system right now to help keep all the files organized.

Minimize all of your groups and add a new group named SharedResources as shown below:

This group will be the main location for your game and will contain resources that are common to both Mac OS X and iOS targets.

While you’re at it, create a separate group named Testing and move the test modules into it, like so:

Now that you have your new organized structure for the various files in your project, you’ll need to move the files into the appropriate locations.

Expand SharedResources and SpriteKitSimpleGame. Click and drag the Particles and Sounds groups from SpriteKitSimpleGame to SharedResources.

Next, drag over the sprites.atlas folder, MyScene.h, MyScene.m, GameOverScene.h and GameOverScene.m. Your file structure should look like the one shown below:

Delete the Spaceship.png file — you won’t need that any longer. This is just a boilerplate file that is automatically added when you create a game with the Sprite Kit template.

All the shared files of your Sprite Kit game now reside in the SharedResources group. Everything left in the SpriteKitSimpleGame relates to launching and managing the game on iOS.

Expand the SpriteKitSimpleGameMac group. You’ll need to remove the example game files from this group before you progress any further.

Delete MyScene.h, MyScene.m and Spaceship.png file from the SpriteKitSimpleGameMac group and select Move to Trash. Your file list should look like so:

Note: You may have noticed that the Mac version of your game does not contain a ViewController class; instead, it only has an AppDelegate. The UIViewController class is part of UIKit which is not available on Mac OS X. Instead, the AppDelegate creates an instance of NSWindow which will present your Sprite Kit scene.

As a final check, your fully-expanded file list should look like the following:

You’ve removed unnecessary files from the project and organized it neatly. Now it’s time to modify your targets to let the compiler know which files to include with each target.

Adding Target Membership

Expand the Frameworks group and select UIKit.framework, like so:

Expand the right Utilities Panel select File Inspector, like so:

About halfway down the File Inspector you’ll see the Target Membership section. This is where you select the targets that will use this file.

UIKit is only available on the iOS platform, so leave it unchecked on your Mac OS X targets, as shown below:

The Cocoa Framework is only available on Mac OS X so ensure it’s checked for your Mac targets and unchecked for your iOS targets like so:

The Sprite Kit Framework is available to iOS and Mac OS X so set the target membership as below:

Each individual file in your project has its own target membership with the exception of texture atlases. The atlas itself is the only place you need to set a target membership and all contained textures will be automatically included.

However, classes work a little differently. You can’t set a target membership on a .h file — instead, you must set the target membership on the .m file.

Armed with your new-found understanding of target membership, work through each file in your SharedResources group and make sure both SpriteKitSimpleGame and SpriteKitSimpleGameMac are ticked on each file. In total, you should need eight ticks to get the job done.

Next, work through each file in your SpriteKitSimpleGame group and make sure that onlySpriteKitSimpleGame is ticked for each — they should all be set correctly at this point, but it’s good to check.

Finally, work through each file in SpriteKitSimpleGameMac group and make sure that only SpriteKitSimpleGameMac files are ticked. Again, you shouldn’t have to change any but it never hurts to check.

Now that your project is properly set up for your iOS and Mac targets, you can get down to what you’re good at — writing code!

Getting the Game to Build and Run

As it stands right now, your project will still build and run without issue for iOS. The changes you just made have no real effect on the existing game. However, if you build and run the Mac target, you’ll see a bunch of errors. That’s because you haven’t yet accounted for the differences between iOS and OS X.

Build and run your project using the SpriteKitSimpleGameMac target; what do you see?

You’ll receive the error Module ‘CoreMotion’ not found. Mac OS X doesn’t have a CoreMotion class or its equivalent; you’ll have to work around this issue and use the keyboard to control player movement. However, your primary goal is to get the project to a buildable state before you worry about implementation details like that.

But how will you fix this? You can’t just remove the line of code referring to CoreMotion, otherwise the iOS version will break. You can’t work around this by using an if statment, since the compiler will still check each line of the code and throw an error if it doesn’t recognize something.

Open MyScene.m and replace:

@import CoreMotion;

with the following code:

#if TARGET_OS_IPHONE
@import CoreMotion;
#endif

Unlike a regular if statement, an #if is performed by the preprocessor. TARGET_OS_IPHONE returns TRUE if the current target is iOS.

Note: If you are plan to use an #if statement to check if the target OS is Mac OS X then the preferred method to check this is !TARGET_OS_IPHONE.

TARGET_OS_MAC seems to work — but the problem is that it also returns TRUE for iOS.

This might seem odd, but Apple uses !TARGET_OS_IPHONE in their example projects that contain multiple targets, so if it is a glitch, it’s most likely one they don’t plan to fix.

Now you will need to find the remaining code related to CoreMotion and surround it with #if statements.

If you build this against an iOS target, all of the above #if statements will return TRUE, so the app compiles just as it did before. In contrast, if you build this against a Mac OS X target all of the above #if statements will return FALSE and none of the above blocks will be compiled.

Take a look at the touchesEnded:withEvents: method in MyScene.m. Since the current version of Mac OS X doesn’t support touch screens, this method is meaningless. The Mac OS X version of this game will use mouse clicks instead as a perfectly adequate substitute for screen touches.

To avoid adding a bunch of boilerplate code, you’ll now create a class that inherits from SKScene to help you handle both screen touches and mouse clicks!

Adding Event Handlers

Select your SharedResources group.

On the menu bar select File \ New \ File… as shown below:

Select Objective-C Class from either the iOS or OS X category and click Next.

Name the file SKMScene and make it a subclass of SKScene.

Place the file directly under your project folder, make sure both iOS and Mac targets are ticked in the target area and click Create.

That’s a fair bit of code, but if you read through from the top, it makes a lot of sense. Touching the screen or the end of a touch event calls the methods in the #if TARGET_OS_IPHONE block. You then create a CGPoint containing the pixel location of the touch and calla the relevant screenInteraction method.

Pressing a mouse button or releasing the mouse button calls the methods in the #else section. Similar to above, you create a CGPoint containing the pixel location of the touch and call the relevant screenInteraction method.

The advantage of using this subclass is that both touch and click events call a screenInteraction method. The screenInteraction methods have no code as you’ll override these in your subclass.

Open MyScene.h and add the following class declaration just under #import :

#import "SKMScene.h"

Next, update the superclass in the @interface line to SKMScene as shown below:

@interface MyScene : SKMScene

This ensures your game scene inherits from your SKMScene subclass. You can now substitute your subclasses for the touch events.

In MyScene.m find the following line:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

…and replace it with the following:

-(void)screenInteractionEndedAtLocation:(CGPoint)location {

Next, delete the following lines from the method as you won’t need them any longer:

Build and run your project using the Mac OS X target; it should compile and run without too many issues:

Congratulations — you’re now running your Sprite Kit game on your Mac! You’ll notice that it has a few bugs:

Some Macs have a noticeable pause the first time you click on the screen.

The particle effect appears to be a little broken on some Macs.

Not everything is sized correctly on the screen.

The background music has gone noticeably silent.

You’ll tackle each of these bugs in turn — and you’ll learn about a few of the common bugs you’ll encounter when you convert apps between platforms.

Correcting Pre-loading Issues

This bug may not raise its head on all systems, but when it does it definitely points to a performance issue. “First-time-through” bugs like this usually stem from the initial load of resources into memory.

Texture atlases are the first resource type that springs to mind, but since this app doesn’t contain animations or large complex images, it’s safe to assume the problem is somewhere else.

The sound effects are the next most likely candidate as the sound files don’t get loaded until the user clicks on the screen.

To fix this, add the following instance variable to MyScene.m:

SKAction *_playPewPew;

Next, add the following line to initWithSize: inside the if statement:

You’ve resolved all of the bugs from the Mac conversion, but you still haven’t solved the issue of game controls in the Mac version of the game.

Using the Keyboard

The ninja’s movements in the iOS version of the app are controlled by tilting the device. These movements are processed by CoreMotion, and the game loop calls updatePlayerWithTimeSinceLastUpdate: to calculate the new player location for the current frame.

Responding to key presses requires a slightly different approach using the available methods to listen for keypress events.

Add the following code to updatePlayerWithTimeSinceLastUpdate: in MyScene.m just before the #endif statement:

#else
-(void)keyDown:(NSEvent *)theEvent {
}

This updates to the method to respond to a keypress as well. Note that there’s a corresponding keyUp to handle the release of the key to handle events that only last for the duration of the keypress.

You don’t want to respond to just any keypress; you can find out which key was pressed using the passed-in NSEvent.

Add the following code between the curly braces of the keyDown: method you just added:

Here you extract the pressed characters from the event without any modifier keys. This means key combinations like Command + S will be ignored. As well, you check that the keypress is only one character in length to filter out any other unwanted events. You’ll dump the key pressed out to the console.

Build and run your project; press a few keys while the game is running and you’ll see the keys pressed show up in the debug area, similar to the following example:

Since you’ll use the up and down arrow keys to move your player sprite, press those keys and see what you get in the console:

Hmm, that looks a little odd. The arrow keys are part of the group known as function keys, so they don’t have a proper character representation. But don’t fret: there’s an easy way to detect when function keys are pressed.

NSEvent is your best friend when it comes to managing keyboard and mouse inputs on the Mac. This tutorial merely introduces NSEvent; it’s highly recommended that you check out the full NSEvent class reference.

For now, take a quick look at the section of NSEvent documentation that deals with the function keys enum. The keys you’re concerned with are NSUpArrowFunctionKey and NSDownArrowFunctionKey.

Go back to MyScene.m and find the keyDown: method you just added.

Comment out the NSLog statement and paste the following code immediately below that:

Where to Go From Here?

Now that you have a good understanding of the work required to convert your iOS targeted project to a iOS / Mac targeted project, you’ll definitely want to create any new Sprite Kit games with cross-platform capabilities right from the start. I have made a cross-platform Sprite Kit project starter template on Github that might be useful – you can clone and use for your own games.

To learn more about making games that work on both iOS and OS X (especially related to scene sizes, image sizes, and aspect ratios, and UIKit vs. Cocoa Touch considerations), check out the upcoming second edition of our book iOS Games by Tutorials, where we go into more detail.

If you have any comments or questions, feel free to join the discussion below!