Setting up buttons in SKScene Setting up buttons in SKScene ios ios

Setting up buttons in SKScene


you could use a SKSpriteNode as your button, and then when the user touches, check if that was the node touched. Use the SKSpriteNode's name property to identify the node:

//fire button- (SKSpriteNode *)fireButtonNode{    SKSpriteNode *fireNode = [SKSpriteNode spriteNodeWithImageNamed:@"fireButton.png"];    fireNode.position = CGPointMake(fireButtonX,fireButtonY);    fireNode.name = @"fireButtonNode";//how the node is identified later    fireNode.zPosition = 1.0;    return fireNode;}

Add node to your scene:

[self addChild: [self fireButtonNode]];

Handle touches:

//handle touch events- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    UITouch *touch = [touches anyObject];    CGPoint location = [touch locationInNode:self];    SKNode *node = [self nodeAtPoint:location];    //if fire button touched, bring the rain    if ([node.name isEqualToString:@"fireButtonNode"]) {         //do whatever...    }}


I've made my own Button-Class that I'm working with.SKButton.h:

#import <SpriteKit/SpriteKit.h>@interface SKButton : SKSpriteNode@property (nonatomic, readonly) SEL actionTouchUpInside;@property (nonatomic, readonly) SEL actionTouchDown;@property (nonatomic, readonly) SEL actionTouchUp;@property (nonatomic, readonly, weak) id targetTouchUpInside;@property (nonatomic, readonly, weak) id targetTouchDown;@property (nonatomic, readonly, weak) id targetTouchUp;@property (nonatomic) BOOL isEnabled;@property (nonatomic) BOOL isSelected;@property (nonatomic, readonly, strong) SKLabelNode *title;@property (nonatomic, readwrite, strong) SKTexture *normalTexture;@property (nonatomic, readwrite, strong) SKTexture *selectedTexture;@property (nonatomic, readwrite, strong) SKTexture *disabledTexture;- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected;- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled; // Designated Initializer- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected;- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled;/** Sets the target-action pair, that is called when the Button is tapped. "target" won't be retained. */- (void)setTouchUpInsideTarget:(id)target action:(SEL)action;- (void)setTouchDownTarget:(id)target action:(SEL)action;- (void)setTouchUpTarget:(id)target action:(SEL)action;@end

SKButton.m:

#import "SKButton.h"#import <objc/message.h>@implementation SKButton#pragma mark Texture Initializer/** * Override the super-classes designated initializer, to get a properly set SKButton in every case */- (id)initWithTexture:(SKTexture *)texture color:(UIColor *)color size:(CGSize)size {    return [self initWithTextureNormal:texture selected:nil disabled:nil];}- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected {    return [self initWithTextureNormal:normal selected:selected disabled:nil];}/** * This is the designated Initializer */- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled {    self = [super initWithTexture:normal color:[UIColor whiteColor] size:normal.size];    if (self) {        [self setNormalTexture:normal];        [self setSelectedTexture:selected];        [self setDisabledTexture:disabled];        [self setIsEnabled:YES];        [self setIsSelected:NO];        _title = [SKLabelNode labelNodeWithFontNamed:@"Arial"];        [_title setVerticalAlignmentMode:SKLabelVerticalAlignmentModeCenter];        [_title setHorizontalAlignmentMode:SKLabelHorizontalAlignmentModeCenter];        [self addChild:_title];        [self setUserInteractionEnabled:YES];    }    return self;}#pragma mark Image Initializer- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected {    return [self initWithImageNamedNormal:normal selected:selected disabled:nil];}- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled {    SKTexture *textureNormal = nil;    if (normal) {        textureNormal = [SKTexture textureWithImageNamed:normal];    }    SKTexture *textureSelected = nil;    if (selected) {        textureSelected = [SKTexture textureWithImageNamed:selected];    }    SKTexture *textureDisabled = nil;    if (disabled) {        textureDisabled = [SKTexture textureWithImageNamed:disabled];    }    return [self initWithTextureNormal:textureNormal selected:textureSelected disabled:textureDisabled];}#pragma -#pragma mark Setting Target-Action pairs- (void)setTouchUpInsideTarget:(id)target action:(SEL)action {    _targetTouchUpInside = target;    _actionTouchUpInside = action;}- (void)setTouchDownTarget:(id)target action:(SEL)action {    _targetTouchDown = target;    _actionTouchDown = action;}- (void)setTouchUpTarget:(id)target action:(SEL)action {    _targetTouchUp = target;    _actionTouchUp = action;}#pragma -#pragma mark Setter overrides- (void)setIsEnabled:(BOOL)isEnabled {    _isEnabled = isEnabled;    if ([self disabledTexture]) {        if (!_isEnabled) {            [self setTexture:_disabledTexture];        } else {            [self setTexture:_normalTexture];        }    }}- (void)setIsSelected:(BOOL)isSelected {    _isSelected = isSelected;    if ([self selectedTexture] && [self isEnabled]) {        if (_isSelected) {            [self setTexture:_selectedTexture];        } else {            [self setTexture:_normalTexture];        }    }}#pragma -#pragma mark Touch Handling/** * This method only occurs, if the touch was inside this node. Furthermore if  * the Button is enabled, the texture should change to "selectedTexture". */- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    if ([self isEnabled]) {        objc_msgSend(_targetTouchDown, _actionTouchDown);        [self setIsSelected:YES];    }}/** * If the Button is enabled: This method looks, where the touch was moved to. * If the touch moves outside of the button, the isSelected property is restored * to NO and the texture changes to "normalTexture". */- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {    if ([self isEnabled]) {        UITouch *touch = [touches anyObject];        CGPoint touchPoint = [touch locationInNode:self.parent];        if (CGRectContainsPoint(self.frame, touchPoint)) {            [self setIsSelected:YES];        } else {            [self setIsSelected:NO];        }    }}/** * If the Button is enabled AND the touch ended in the buttons frame, the * selector of the target is run. */- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {    UITouch *touch = [touches anyObject];    CGPoint touchPoint = [touch locationInNode:self.parent];    if ([self isEnabled] && CGRectContainsPoint(self.frame, touchPoint)) {        objc_msgSend(_targetTouchUpInside, _actionTouchUpInside);    }    [self setIsSelected:NO];    objc_msgSend(_targetTouchUp, _actionTouchUp);}

An example: To initialize a button, you write the following lines:

    SKButton *backButton = [[SKButton alloc] initWithImageNamedNormal:@"buttonNormal" selected:@"buttonSelected"];    [backButton setPosition:CGPointMake(100, 100)];    [backButton.title setText:@"Button"];    [backButton.title setFontName:@"Chalkduster"];    [backButton.title setFontSize:20.0];    [backButton setTouchUpInsideTarget:self action:@selector(buttonAction)];    [self addChild:backButton];

Furthermore you need the 'buttonAction' method in your class.* No warranty that this class is working right in every case. I'm still quite new to objective-c. *

If you think having to do this is annoying and pointless you can disable the check in the build settings by setting 'Enable strict checking of objc_msgSend Calls' to 'No'


For people writing their games in Swift!I have rewritten the essential parts of Graf's solution to a swift class. Hope it helps:

import Foundationimport SpriteKitclass FTButtonNode: SKSpriteNode {    enum FTButtonActionType: Int {        case TouchUpInside = 1,        TouchDown, TouchUp    }    var isEnabled: Bool = true {    didSet {        if (disabledTexture != nil) {            texture = isEnabled ? defaultTexture : disabledTexture        }    }    }    var isSelected: Bool = false {    didSet {        texture = isSelected ? selectedTexture : defaultTexture    }    }    var defaultTexture: SKTexture    var selectedTexture: SKTexture    required init(coder: NSCoder) {        fatalError("NSCoding not supported")    }    init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {        self.defaultTexture = defaultTexture        self.selectedTexture = selectedTexture        self.disabledTexture = disabledTexture        super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())        userInteractionEnabled = true        // Adding this node as an empty layer. Without it the touch functions are not being called        // The reason for this is unknown when this was implemented...?        let bugFixLayerNode = SKSpriteNode(texture: nil, color: nil, size: defaultTexture.size())        bugFixLayerNode.position = self.position        addChild(bugFixLayerNode)    }    /**    * Taking a target object and adding an action that is triggered by a button event.    */    func setButtonAction(target: AnyObject, triggerEvent event:FTButtonActionType, action:Selector) {        switch (event) {        case .TouchUpInside:            targetTouchUpInside = target            actionTouchUpInside = action        case .TouchDown:            targetTouchDown = target            actionTouchDown = action        case .TouchUp:            targetTouchUp = target            actionTouchUp = action        }    }    var disabledTexture: SKTexture?    var actionTouchUpInside: Selector?    var actionTouchUp: Selector?    var actionTouchDown: Selector?    weak var targetTouchUpInside: AnyObject?    weak var targetTouchUp: AnyObject?    weak var targetTouchDown: AnyObject?    override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)  {        let touch: AnyObject! = touches.anyObject()        let touchLocation = touch.locationInNode(parent)        if (!isEnabled) {            return        }        isSelected = true        if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {            UIApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self, forEvent: nil)        }    }    override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!)  {        if (!isEnabled) {            return        }        let touch: AnyObject! = touches.anyObject()        let touchLocation = touch.locationInNode(parent)        if (CGRectContainsPoint(frame, touchLocation)) {            isSelected = true        } else {            isSelected = false        }    }    override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {        if (!isEnabled) {            return        }        isSelected = false        if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {            let touch: AnyObject! = touches.anyObject()            let touchLocation = touch.locationInNode(parent)            if (CGRectContainsPoint(frame, touchLocation) ) {                UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)            }        }        if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {            UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)        }    }}