Set touchable area of SKSpriteNode - ios

Is there a property of SKSpriteNode that lets you manually set its touchable area?
I have a sprite textured with a PNG, it only seems to detect touches on the opaque portion of the PNG. So a small circle inside a large blank canvas actually has a tiny touchable area.

Create an SKNode that is the size you would like the touchable area to be. Add your textured sprite as a child of the new SKNode. Check to see if the new SKNode was touched instead of if the textured sprite was touched.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"childName"])
{
NSLog(#"You touched to child of sprite");
}
}
-(void)sprite
{
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"spriteImageName"];
[self addChild:sprite];
SKSpriteNode *spriteChild = [SKSpriteNode spriteNodeWithImageNamed:#"childImageName"];
spriteChild.name = #"childName";
[sprite addChild:spriteChild];
}

Related

Unexpected SpriteKit batch drawing results

I am currently working on a iOS game using SpriteKit and Objective-C. I've recently been trying to optimize my draw calls, but I've run into an issue where I am either misunderstand the batch draw operation and/or I am receiving some unexpected results. Additionally I have read the Apple documentation regarding batch drawing, I'm just afraid I may have misinterpreted some of the guidelines.
So here is the rundown: (assume that all atlases are preloaded and that self is a SKScene)
This works as you would expect. The first touch adds +1 to both the node and draw count. Subsequent touches add to the node count but not the draw count.
/*Code Block 1:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:enemyTouch];
}
When I start adding two sprites from different atlases to the parent I start to get too many draw calls. What I expected: on the first touch I would get +2 nodes and +2 draw calls and subsequent touches produce +2 nodes and +0 draw calls. What I got: on the first touch I got +2 nodes and +2 draw calls and subsequent touches produced +2 nodes and +2 draw calls.
/*Code Block 2:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:touch1];
SKSpriteNode *touch2 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas2"] textureNamed:#"texture2"]];
[touch2 setPosition:location];
[self addChild:touch2];
}
I tried one additional step of adding a child SKNode *extraLayer to the scene and adding one sprite to the scene and one sprite to the extraLayer. This produced the desired results of +2 nodes and +0 draw calls I wanted above.
/*Code Block 3:*/
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
SKSpriteNode *touch1 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas1"] textureNamed:#"texture1"]];
[touch1 setPosition:location];
[self addChild:touch1];
SKSpriteNode *touch2 = [SKSpriteNode spriteNodeWithTexture:[[SKTextureAtlas atlasNamed:#"TouchAtlas2"] textureNamed:#"texture2"]];
[touch2 setPosition:location];
[self.extraLayer addChild:touch2];
}
The question: Is this in fact the way batching draw calls needs to be handled? All sprites whose textures come from common atlases need to be children of the same parent, but additionally that parent has no children who's textures come from a different atlas? Or is there a way of making the method of block 2 batch correctly?

Detecting touches in a triangular grid of buttons?

In the photo below, each triangle is a separate subclassed SKShapeNode. How do you recognize which triangle is touched? Currently in the scene's toucheBegan: method, touching the grid detects two triangles since their frame's are square.
I managed to solve this problem by setting the UIBezierPath path that draws the triangle as a property on the Triangle class. In the scene's toucheBegan: method, I check if the touch is contained within the Triangle's touchableArea UIBezierPath property.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint position = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:position];
for (TrianglePiece *triangle in nodes) {
if ([triangle isKindOfClass:[TrianglePiece class]]) {
position = [touch locationInNode:triangle];
if ([triangle.touchableArea containsPoint:position]) {
// Perform logic here.
}
}
}
}

run animation on button in xcode spriteKit Game

I want to add an animation to button in sprite Kit, such as the play button found in Candy Crush Saga.
Link for Candy Crush Saga: https://www.youtube.com/watch?v=KAMUWIqYN24
Do I use an image sequence? Or do I scale the size of the button?
I think it's better to use SKAction to scale the button by using those methods assuming your button is a SKSpriteNode
+ scaleXTo:y:duration:
+ scaleYTo:duration:
for example
SKAction * yScale=[SKAction scaleYTo:0.4 duration:.5]
SKAction * xScale=[SKAction scaleXTo:0.5 duration:.5]
and you repeat the action for ever
[buttonSprite runAction:[SKAction repeatActionForever:[SKAction sequence:#[xScale, yScale]]]];
EDIT: you need to give a name to your SKSpriteNode button like this
buttonSprite = #"NAME";//to identify the button in touchesBegan
And the implement
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"NAME"]) {
//DO SOMETHING
}
}

Increase region where SKNode can be pressed

I'm wondering if there's an easy way that I could take an SKNode and increase the region in which it is pressed.
For example, I am currently checking if a node is clicked like so:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:positionInScene];
if ([node.name isEqualToString:TARGET_NAME]) {
// do whatever
}
}
}
If the node drawn on the screen is something like 40 pixels by 40 pixels, is there a way that if a user clicks within 10 pixels of the node, it would render as being clicked?
Thanks!
You could add an invisible sprite node as a child to the visible node. Have the child node's size be larger than the visible node's.
For example, on OSX this would work in a scene:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKSpriteNode *visibleNode = [[SKSpriteNode alloc] initWithColor:[NSColor yellowColor] size:CGSizeMake(100, 100)];
visibleNode.name = #"visible node";
visibleNode.position = CGPointMake(320, 240);
SKSpriteNode *clickableNode = [[SKSpriteNode alloc] init];
clickableNode.size = CGSizeMake(200, 200);
clickableNode.name = #"clickable node";
[visibleNode addChild:clickableNode];
[self addChild:visibleNode];
}
return self;
}
-(void)mouseDown:(NSEvent *)theEvent
{
CGPoint positionInScene = [theEvent locationInNode:self];
SKNode *node = [self nodeAtPoint:positionInScene];
NSLog(#"Clicked node: %#", node.name);
}
The clickable node extends 50px outwards from the edges of the visible node. Clicking within this will output "Clicked node: clickable node".
The node named "visible node" will never be returned by the call to [self nodeAtPoint:positionInScene], because the clickable node overlays it.
The same principle applies on iOS.

Can ´t detect UItouch

I have a large image (1024, 1496) as a background image of my layer.So I started to show the button of the image.
In the time of application I show the top of the image, and some CCSprites.
id move = [CCMoveBy actionWithDuration:2 position:ccp(0,-746)];
[layer runAction:move];
My problem is that I can't detect touch on the CCSrites because their position stay the same ex. (20, 1200), and the UITouch between the (1024,746).
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch * touch in [event allTouches]){
for (CCSprite *book in books) {
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
if (CGRectContainsPoint([book boundingBox], location))
NSLog(#"Touch");
else{
NSLog(#"NO Touch"); }
}
}
}
Any idea how to solve this problem??
Just convert the sprite touch to world coordinates. You can do this with your own method by calculating the screen position based on zooming and sprite size, or use methods within Cocos to convert to world coordinates. I tend to do the former.

Resources