I currently have all of my key press code in my various SKScene classes. I only want something to happen every time the key is pressed. The key needs to be re-triggered to perform the action again. I'm using a boolean array to keep track of this. This may not be optimal, but it's what I thought of at the time. I was looking to create a class to manage all of this, but key event methods keyUp and keyDown are created in SKScene classes. Is there a global way to handle keys presses so that I don't have to recreate this code in every scene in my game?
-
Singleton with NSNotifications. – sangony Jul 08 '15 at 12:15
-
What do you want to happen when a key is pressed? Do you want a method to be called in the current scene (e.g., GameScene) or do you just want to have access to that boolean array from every scene? – 0x141E Jul 08 '15 at 16:39
-
Right now this is mainly for menu item selection. For instance, when the user presses down, the indictor should move to the next menu item. – 02fentym Jul 08 '15 at 17:15
2 Answers
You can subclass SKView
and perform all your key press handling in a centralized location. This approach has several benefits: 1. since all key press logic is in a single class, changes to the logic are made to that class instead of in all of your scenes and 2) it removes device-specific input logic from the multiple scenes. If you decide to port your app to iOS, for example, you can simply change the interface from the keyboard/mouse to touches in a single file.
The following is an implementation of a key press handler in a custom subclass of SKView
. It uses a protocol/delegate design pattern to send key press messages to the various scenes. Delegation is a convenient way for an object (in this case a class) to communication with other classes and the protocol defines how this communication will take place.
- Create a subclass of SKView
CustomSKView.h
@protocol KeyPressedDelegate;
@interface CustomSKView : SKView
@property (weak) id <KeyPressedDelegate> delegate;
@end
@protocol KeyPressedDelegate
- (void) upArrowPressed;
- (void) downArrowPressed;
@end
CustomSKView.m
All the key press handling logic resides here. The details of how key presses are handled are hidden from the code that acts on the key presses.
@implementation CustomSKView:SKView {
// Add instance variables here
}
// This is called when the view is created.
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Allocate and initialize your instance variables here
}
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
// Add code to handle a key down event here
if (self.delegate) {
switch (theEvent.keyCode) {
case 126:
[self.delegate upArrowPressed];
break;
case 125:
[self.delegate downArrowPressed];
break;
default:
break;
}
}
}
@end
- Adopt the protocol
GameScene.h
#import "CustomSKView.h"
@interface GameScene : SKScene <KeyPressedDelegate>
@end
- Implement the delegate method in all scenes that require key press info
GameScene.m
@implementation GameScene
-(void)didMoveToView:(SKView *)view {
((CustomSKView *)view).delegate = self;
}
- (void) upArrowPressed {
NSLog(@"Up Arrow Pressed");
}
- (void) downArrowPressed {
NSLog(@"Down Arrow Pressed");
}
@end
- In your main .xib file, set the class for your
SKView
to the custom class:

- 12,613
- 2
- 41
- 54
-
I'm a little bit confused by the protocol/delegate portion. Is the class that I'm using as a subclass a singleton? – 02fentym Jul 09 '15 at 04:37
-
Yes, the `CustomSKView` class is a singleton. You can add all your key press handling logic in that class and call the appropriate delegate method based on which key was pressed (see the `upArrowPressed` and `downArrowPressed` methods that I just added). – 0x141E Jul 09 '15 at 06:45
-
Since `GameScene.m` has to create and implement those methods anyways, what's the difference from just using the `keyUp` and `keyDown` methods that I had before? Like I said before, I'm not really clear on the entire point of delegates/protocols and I think I'm missing a big piece of what you're explaining. – 02fentym Jul 09 '15 at 06:57
-
-
Thank you, that was very helpful! I've run it as a test project and now I'm currently working at incorporating features that I'll need for my actual project. For the part where you said "Add instance variables here", I've never seen the `@implementation` part of a class used like that. I usually declare them in the `@interface` portion or the .h file. How do I initialize them? – 02fentym Jul 10 '15 at 23:46
-
I've added an `init` method where you can initialize instance variables. Here's a explanation of the difference between properties (defined in the `@interface` section of a .h or .m file) and instance variables. http://stackoverflow.com/questions/843632/is-there-a-difference-between-an-instance-variable-and-a-property-in-objecti – 0x141E Jul 11 '15 at 02:53
-
Wow, I am just fully understanding the power of this method. This will save me a lot of code! I actually won't even need to implement any instance variables to keep track of what key has been pressed (at least I don't think I will) because of those `[self.delegate
]` calls. I have one final question, I think and I'm thinking that the answer is no. Could an SKSpriteNode be a delegate of CustomSKView protocol? – 02fentym Jul 11 '15 at 04:36 -
Yes, if you subclass `SKSpriteNode`, you can make it a delegate of the `CustomSKView`. – 0x141E Jul 11 '15 at 04:39
-
Yeah, I've subclassed `SKSpriteNode`. I've basically copied everything that you had in `GameScene.m` to my `SKSpriteNode` subclass except for the `didMoveToView` method. I also assigned my .h file to be a `
`. What am I missing so that I get a response when an arrow key is pressed? – 02fentym Jul 11 '15 at 04:44 -
I did not put the `((CustomSKView *)view).delegate = self;` part in my `SKSpriteNode` subclass because it's not a view. I'm pretty sure this is the missing piece, but I'm not sure what to write. – 02fentym Jul 11 '15 at 04:46
-
You will need to override an `init` method of the `SKSpriteNode` subclass and add that code to it. – 0x141E Jul 11 '15 at 04:47
-
Yeah, I tried that as well, but I got an error saying `Use of undeclared identifier 'view'`. Any ideas? – 02fentym Jul 11 '15 at 04:54
-
You can also set the sprite to be the delegate of the view in the scene's `didMoveToView`. After creating the sprite, assign it as the delegate with `((CustomSKView *)view).delegate = customSprite;` – 0x141E Jul 11 '15 at 05:23
-
Hmm...I did exactly what you suggested. It's not calling the `downArrowPressed` and `upArrowPressed` methods in the sprite's class. – 02fentym Jul 11 '15 at 06:12
-
Since you can have only one delegate at a time (in this setup), make sure you aren't setting the delegate to one of your scenes. – 0x141E Jul 11 '15 at 06:24
-
Ok cool...thanks man. I didn't add my sprite to the scene, which is why it didn't work. Thank you so much for everything! i've learned a lot! – 02fentym Jul 11 '15 at 06:32
I recommend to using Singleton pattern to store and fetch your keys. Because you can reach from your SKScene and than create or update any time.
http://www.galloway.me.uk/tutorials/singleton-classes/ this singleton intro is fast and short.

- 1,090
- 13
- 26
-
Yup, I was going to do that anyways. I have a couple of singleton classes right now for things like game data, etc. – 02fentym Jul 08 '15 at 15:50