blueicecream
blueicecream

Reputation: 125

The right 1/3 of a button created using a SKSpriteNode is not detecting touches

In my game, I am creating numerous buttons. I am finding that many of the buttons are not detecting when a user touches down/up on the RIGHT side of the button. In other words, the button responds correctly when touched on the LEFT side of the button, but not the RIGHT side. In short, if you sliced the button into three parts, the right 1/3 of the button does not respond while the other 2/3 of the button work perfectly. Below is the code that I have used to implement my buttons. Can anyone tell me what I might be doing wrong? I did notice when I first started using Sprite Kit that my buttons would not respond at all when the SKSpriteNode::zPosition property was NOT set to 1 or greater...once I set this property my buttons started working. This zPosition problem occurred whether or not the button sprite node was the only sprite node in the scene or not which makes me think that the problem is not an overlap issue either. Got any ideas?

I have extended the SKSpriteNode class to have an additional method GESpriteNode::setGestureHandler:withKey:, which have the following signatures:

- (void) setGestureHandler: (void (^)(SKNode *node, GEGestureRecognizer *recognizer))handler withKey: (NSString *)key;
- (void) removeGestureHandlerWithKey: (NSString *)key;

And, implemented like so (where _event is an NSMutableDictionary):

- (void) setGestureHandler: (void (^)(SKNode *node, GEGestureRecognizer *recognizer))handler withKey: (NSString *)key {
    if (key != nil) {
        [_events setObject: [handler copy] forKey: key];
    }
}

- (void) removeGestureHandlerWithKey: (NSString *)key {
    if (key != nil) {
        [_events removeObjectForKey: key];
    }
}

- (void) touchesBegan: (NSSet *)touches withEvent: (UIEvent *)event {
    if (![self isHidden]) {
        void (^handler)(SKNode *, GEGestureRecognizer *);

        handler = [_events objectForKey: GETouchDownGestureHandlerKey];

        if (handler != nil) {
            GEGestureRecognizer *recognizer = [[GEGestureRecognizer alloc] initWithScene: [self scene] withTouches: touches];
            if (recognizer != nil) {
                handler(self, recognizer);
            }
        }
    }
}

- (void) touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event {
    if (![self isHidden]) {
    void (^handler)(SKNode *, GEGestureRecognizer *);

        handler = [_events objectForKey: GETouchUpGestureHandlerKey];

        if (handler != nil) {
            GEGestureRecognizer *recognizer = [[GEGestureRecognizer alloc] initWithScene: [self scene] withTouches: touches];
            if (recognizer != nil) {
                handler(self, recognizer);
            }
        }
    }
}

They are used as follows:

GESpriteNode *spriteNode = [GESpriteNode spriteNodeWithImageNamed: NODE_BUTTON_1];
[spriteNode setAlpha: ALPHA_OPAQUE];
[spriteNode setGestureHandler: ^(SKNode *node, GEGestureRecognizer *recognizer) {
    [node setScale: SCALE_95];
} withKey: GETouchDownGestureHandlerKey];
[spriteNode setGestureHandler: ^(SKNode *node, GEGestureRecognizer *recognizer) {
[node setScale: SCALE_100];
} withKey: GETouchUpGestureHandlerKey];
[spriteNode setHidden: NO];
[spriteNode setName: NODE_BUTTON_1];
[spriteNode setPosition: CGPointMake(384.0f, 387.0f)];
[spriteNode setScale: SCALE_100];
[spriteNode setUserInteractionEnabled: YES];
[spriteNode setZPosition: 1];
[self addChild: spriteNode];

My GEGestureRecognizer.h class is defined as so:

#import <SpriteKit/SpriteKit.h>

extern NSString * const GETouchDownGestureHandlerKey;
extern NSString * const GETouchUpGestureHandlerKey;

@interface GEGestureRecognizer : NSObject

// public properties
@property (nonatomic, strong) SKScene *scene;

// public methods
- (instancetype) initWithScene: (SKScene *)scene withTouches: (NSSet *)touches;
- (CGPoint) locationInScene: (SKScene *)scene;

@end

And, its GEGestureRecognizer.m is defined as:

#import "GEGestureRecognizer.h"

#pragma mark -
#pragma mark Constants

NSString * const GETouchDownGestureHandlerKey = @"GETouchDownGestureHandlerKey";
NSString * const GETouchUpGestureHandlerKey = @"GETouchUpGestureHandlerKey";

@interface GEGestureRecognizer () {

    @private
        SKScene *_scene;
        NSSet *_touches;

}

@end

@implementation GEGestureRecognizer

@synthesize scene = _scene;

- (instancetype) initWithScene: (SKScene *)scene withTouches: (NSSet *)touches {
    if ((self = [super init])) {
        _scene = scene;
        _touches = touches;
    }
    return self;
}

- (CGPoint) locationInScene: (SKScene *)scene {
    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat k = 0;

    for (UITouch *touch in _touches) {
        const CGPoint point = [touch locationInNode: scene];
        x += point.x;
        y += point.y;
        k++;
    }

    if (k > 0) {
        return CGPointMake(x/k, y/k);
    }

    return CGPointZero;
}

@end

Upvotes: 1

Views: 286

Answers (2)

Pieter
Pieter

Reputation: 17705

Recreating the GestureRecognizer on each event seems rather inefficient, the locationInScene does not make much sense either when multiple touches occur (no idea what you're trying to achieve there)

That said, if your buttons only started to work when you change the zPosition, I assume you do have an overlap issue. Since there are other nodes involved (whether visible or not) at (default) zPosition 0, you'd have to show more code to get help.

Upvotes: 0

Dvole
Dvole

Reputation: 5795

What exactly is this code doing? Why do you increase x and y values? Why do you need k value? I think you are offsetting the location of touch, try logging all touch locations and drawing them on screen.
There are native methods to detect touch in nodes and translate coordinates to scene locations.

Upvotes: 0

Related Questions