Create a scene helper class for iOS / Mac cross platform SpriteKit games

There was a lot of interest in my post giving a few vague suggestions for converting a sprite kit game from iOS to Mac. Since then I've refined my methods a bit and now work with a project which is targeted for both iOS and Mac to make it even easier to implement and test.

Even if you never plan to release your game on Mac OS X, the below is still useful. The iOS simulator is painful at times however running SpriteKit natively on the Mac is simply beautiful, it makes testing a lot more fun.

Getting Started

The first thing you need to do is make a project containing both a target for iOS and for Mac OS.

  1. Create a new Xcode project
  2. Select "Sprite Kit Game" from the iOS templates
  3. Give it a name
  4. Go to "File", "New", "Target..."
  5. Select "Sprite Kit Game" from the Mac templates

You now have a template containing both two targets. If you are familiar with both Mac and iOS development it shouldn't take you too long to work out what is going on here. Have a look at your schemes and project settings.

I like to arrange my folders so that my Mac only code is in a "Mac" folder, the iOS only code is in a "iOS" folder and the shared game is in a "Shared" folder.

You can control which target has access to which game files via the "Target Membership" settings in the file inspector on the right.

Platform specific code

In iOS you are probably familiar with performing different code specific you wether you are running the application on an iPhone or iPad. You will run in to the same problems when coding for both Mac and iOS.

There are a lot of differences in the way you code between both platforms, even which classes you are using, so in most cases a standard IF statement won't be enough and you will need pre-processor #IF statements.

Example:

#if TARGET_OS_IPHONE
#endif

There is also a TARGET_OS_MAC but you will probably find that iOS devices respond to this as well, so it is a lot easier to just check !TARGET_OS_IPHONE.

It's a different world

Before going any further it is worth noting that you are working in two different worlds governed by two different HIG documents. What you know in iOS may not apply to Mac development. 

You are also working with a touch screen device without keyboard and a mouse and keyboard operated device. This is a simple fact but it will show in your game if you don't pay attention to it. I had an app rejected at review once for the reason my start screen said "Tap anywhere to begin" instead of "Click", a rather embarrassing mistake but one I make sure to prepare better for now.

Ok, enough of the lecture, you all want the helper class right?

A scene helper class to get you started

This is a very simple class but I want you guys to take it and add your own code to it. If your game uses a onscreen DPAD on iOS then it makes no sense do the same on Mac, you would need to monitor key presses instead.

This class helps for all other cases, it allows you to single methods for handling both screen clicks and screen taps. Great for games where screen presses translate easily in to screen clicks.

Create a class called SKMScene that inherits from SKScene.

Put the below in SKMScene.h:

// Created by Neil North on 6/02/2014.
// Copyright (c) 2014 Neil North. All rights reserved.
//

#import

@interface SKMScene : SKScene


//Screen Interactions
-(void)screenInteractionStartedAtLocation:(CGPoint)location;
-(void)screenInteractionEndedAtLocation:(CGPoint)location;

@end

And the below in the SKMScene.m file:

// Created by Neil North on 6/02/2014.
// Copyright (c) 2014 Neil North. All rights reserved.
//

#import “SKMScene.h”

@implementation SKMScene

-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
/* Overridden by Subclass */

}
return self;
}

#if TARGET_OS_IPHONE
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self screenInteractionStartedAtLocation:positionInScene];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self screenInteractionEndedAtLocation:positionInScene];
}

- (void)touchesCancelled:(NSSet *)touches
withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self screenInteractionEndedAtLocation:positionInScene];
}
#else
-(void)mouseDown:(NSEvent *)theEvent {
CGPoint positionInScene = [theEvent locationInNode:self];
[self screenInteractionStartedAtLocation:positionInScene];
}

- (void)mouseUp:(NSEvent *)theEvent
{
CGPoint positionInScene = [theEvent locationInNode:self];
[self screenInteractionEndedAtLocation:positionInScene];
}

- (void)mouseExited:(NSEvent *)theEvent
{
CGPoint positionInScene = [theEvent locationInNode:self];
[self screenInteractionEndedAtLocation:positionInScene];
}
#endif

-(void)screenInteractionStartedAtLocation:(CGPoint)location {
/* Overridden by Subclass */
}

-(void)screenInteractionEndedAtLocation:(CGPoint)location {
/* Overridden by Subclass */
}

@end

That's it, now all you have to do is make sure all your scenes inherit from SKMScene instead of SKScene and implement the two screenIntereaction methods.

There's a lot more to this process and I'm only just starting to learn to make cross platform games myself but I hope the above tips have helped you on your journey. Give it a go and see what you think.

You can also download a sample project on GitHub.

Some great SpriteKit resources

In order to start getting some games on the store quickly with my challenge I'm having to cram a lot of SpriteKit knowledge.

It seems everyone has a different idea of how things are done and surprisingly some of the most experienced iOS developers I've seen are still using old methods of doing things, possibly because they are coming fresh from Cocos2D.

Right now I have 5 different sample projects open with different implementations of an entity class, as I observe each of them I have been using ideas I've liked and trying to write the code as robust as possible, at times it sort of feels like music, like a different person would have a completely different interpretation of what is good or bad but neither are technically wrong.

It's an interesting world of 2D development to be working in, here are a few tools I've found along the way which have been useful:

  • SKPhysicsBody Generator  - This website is great, you can drag in an image then draw a polygon and get the code straight away to put it in your app. I even took this a little further by making a simple spreadsheet tool which prepares the results for a plist and method to loop through creating the physics body from a plist file.
  • Kobold Kit - I haven't used it myself by this looks promising and I will probably be implementing in at least some of my apps when it reaches version 1.0.
  • iOS Games by Tutorials - This book has been a fantastic learning reference, I've picked up a lot of useful information and their additional tools are great. Easily worth the price. (Disclaimer: I am affiliated with the Ray Wenderlich Team).
  • Cartoon Smart on Udemy - I love the work these guys do, while their Obj-C is still back in Cocos2D land, their examples are fairly top notch and its really good to learn something from a different perspective than just RW.
  • Apportable -  While I am yet to try it, Apportable sounds like a high quality and reliable way to convert your Sprite Kit projects in to a format usable on the android platform. If it works then you have just increased your market size with minimal effort.