How to detect in touchesEnded which nodes are still being pressed - sprite-kit

I've been stuck on this problem for days. What I have is multiple SKSpriteNode's, one for a left arrow, right arrow and up arrow. When I hold down the right arrow I want my character to continue moving right while its being held down, on the other hand if you press the up Arrow then you will only jump once regardless of if you hold it down.
So my problem for example is when i hold the right arrow and then i press the up arrow, touchesEnded is called and it stops my character from moving right even though I still have my finger on the right arrow
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
for (UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
if (CGRectContainsPoint(rightArrow.frame, location)){
[wizard setTexture:[SKTexture textureWithImageNamed:#"wizardRight"]];
didTouchRightArrow = YES;
isLookingRight = YES;
isLookingLeft = NO;
rightArrow.alpha = 0.5;
NSLog(#"Touching right");
}
if (CGRectContainsPoint(leftArrow.frame, location)){
[wizard setTexture:[SKTexture textureWithImageNamed:#"wizardLeft"]];
isLookingRight = NO;
isLookingLeft = YES;
didTouchLeftArrow = YES;
leftArrow.alpha = 0.5;
NSLog(#"Touching left");
}
if (CGRectContainsPoint(upArrow.frame, location)){
didTouchUpArrow = YES;
upArrow.alpha = 0.5;
NSLog(#"Touching up");
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
if (rightArrow.alpha != 1.0){
rightArrow.alpha = 1.0;
}
if (leftArrow.alpha != 1.0){
leftArrow.alpha = 1.0;
}
if (upArrow.alpha != 1.0){
upArrow.alpha = 1.0;
for (UITouch *touch in touches){
CGPoint location = [touch locationInNode:self];
if (CGRectContainsPoint(rightArrow.frame, location)){
NSLog(#"Touching right");
didTouchRightArrow = YES;
} {
NSLog(#"Not touching right");
didTouchRightArrow = NO;
}
if (CGRectContainsPoint(leftArrow.frame, location)){
NSLog(#"Touching Left");
didTouchLeftArrow = YES;
} else {
NSLog(#"not touching left");
didTouchLeftArrow = NO;
}
didTouchUpArrow = NO;
}
This may not be the right way to approach the problem, but in touchesEnded I am trying to see if the touch is still in the desired Rect.

You need a way to identify the different nodes which are registering a touch. There is more than one way to do this but I have always found using the name property of a node to be the simplest and easiest to work with.
You already have the right idea by using the BOOLs to register the touch states.
I wrote some code to handle what you are trying to accomplish:
#import "GameScene.h"
#implementation GameScene {
SKSpriteNode *node0;
SKSpriteNode *node1;
BOOL node0touch;
BOOL node1touch;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];
node0 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(100, 100)];
node0.name = #"node0";
node0.position = CGPointMake(100, 300);
[self addChild:node0];
node1 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(100, 100)];
node1.name = #"node1";
node1.position = CGPointMake(400, 300);
[self addChild:node1];
node0touch = false;
node1touch = false;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:touchLocation];
if([node.name isEqualToString:#"node0"])
node0touch = true;
if([node.name isEqualToString:#"node1"])
node1touch = true;
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:touchLocation];
if([node.name isEqualToString:#"node0"])
node0touch = false;
if([node.name isEqualToString:#"node1"])
node1touch = false;
}
}
-(void)update:(CFTimeInterval)currentTime {
if(node0touch)
NSLog(#"node0 touch");
if(node1touch)
NSLog(#"node1 touch");
}

Related

SpriteKit - TouchesMoved continues to report touch outside the sprite?

I have a sprite using a custom SKSpriteNode class added to a scene.
I want to do this effect: suppose I have 3 sprites side by side. I want the sprite to scale a little bit if it is touched or while the finger is touching it and as soon as the finger is not touching it, it should scale back to the original size.
What I mean is this: suppose the user gives a big finger slide from left to right, starting on the first sprite and ending past the last sprite. As the finger is sliding, I want the first sprite to scale up as soon as the finger enters its frame. As the finger continues to slide to right and reaches the second sprite, I want the first one to detect that the finger is not on its area and scale down to its original size. At the same time, the second sprite would scale up, because now the finger is inside its area. At any time the finger leaves the surface during the slide, just after passing the last sprite.
My problem is that I want to do this using logic inside the sprite class but when I implement touchesBegan, touchesMoved, etc., on that sprite's class it is not working because touchesMoved continues to report the finger even if it is not inside its area.
This is my touch logic inside the sprite's class:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
SKAction *scaleUp = [SKAction scaleTo:1.2 duration:0.2];
[self runAction:scaleUp];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
// CGPoint convertPT = [self convertPoint:touchLocation fromNode:self.parent];
NSLog(#"%#", NSStringFromCGPoint(touchLocation));
// if (CGRectContainsPoint(self.frame, touchLocation)) {
// NSLog(#"this is never fired?");
// }
}
The NSLog line will always print a location even if the finger is outside the layer...
I want TouchesMoved to stop firing after the finger is outside the sprite.
How do I do that?
I put this in a sub class and got good results.
You will still need some logic for when the begins outside the rect from within the game scene class.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
SKAction *scaleUp = [SKAction scaleTo:1.2 duration:0.2];
[self runAction:scaleUp withKey:#"action"];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self.parent];
if (![self containsPoint:loc]) {
SKAction *scaleDown = [SKAction scaleTo:1.0 duration:0.1];
[self runAction:scaleDown];
}
else if ([self containsPoint:loc]) {
SKAction *scaleUp = [SKAction scaleTo:1.2 duration:0.1];
[self runAction:scaleUp];
}
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self removeActionForKey:#"action"];
SKAction *scaleDown = [SKAction scaleTo:1.0 duration:0.1];
[self runAction:scaleDown];
}
I havent got the subclass method working to my liking, but I did get it working well from the main scene -- a mess of if statements ... but does the job
// add a ivar to scene enforce one zoom spite at a time, SKSpriteNode *_currentZoomRect;
- (void) addSomeBlocks
{
for (int i = 1; i <= 3; i++) {
SKSpriteNode *rect = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(70, 70)];
rect.position = CGPointMake(100 * i , 160);
rect.name = #"inner";
[self addChild:rect];
}
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:loc];
for (SKNode *n in nodes) {
if ([n.name isEqualToString:#"inner"]) {
_currentZoomRect = (SKSpriteNode *) n;
SKAction *scaleUp = [SKAction scaleTo:2.0 duration:0.05];
[n runAction:scaleUp withKey:#"action"];
}
}
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:loc];
for (SKNode *n in nodes) {
if ([n.name isEqualToString:#"inner"]) {
if (_currentZoomRect) {
// can only allow one zoom at a time
if (n == _currentZoomRect && !n.hasActions) {
SKAction *scaleUp = [SKAction scaleTo:2.0 duration:0.05];
[n runAction:scaleUp];
}
else if (n != _currentZoomRect) {
SKAction *scaleDown = [SKAction scaleTo:1.0 duration:0.05];
[_currentZoomRect runAction:scaleDown];
SKAction *scaleUp = [SKAction scaleTo:2.0 duration:0.05];
[n runAction:scaleUp];
_currentZoomRect = (SKSpriteNode *) n;
}
}
else {
SKAction *scaleUp = [SKAction scaleTo:2.0 duration:0.05];
[n runAction:scaleUp];
_currentZoomRect = (SKSpriteNode *) n;
}
}
}
if (![nodes count] && _currentZoomRect) {
SKAction *scaleDown = [SKAction scaleTo:1.0 duration:0.05];
[_currentZoomRect runAction:scaleDown];
_currentZoomRect = nil;
}
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_currentZoomRect) {
SKAction *scaleDown = [SKAction scaleTo:1.0 duration:0.05];
[_currentZoomRect runAction:scaleDown];
_currentZoomRect = nil;
}
}

Wrong sprite moving when touched

I have 20 sprites in my scene which I've added to a NSMutableArray. My problem is that when I drag one sprite over another, the other also moves. How do I restrict the movement of untouched sprites?
Please help me with code (I am new to Cocos2d).
if( (self=[super init])) {
collection=[[NSMutableArray alloc]init];
CCLayer *base=[CCSprite spriteWithFile:#"Base.png"];
base.position=ccp(512,384);
[self addChild:base];
x=0;
for(int i=1;i<=7;i++)
{
CCSprite *hole=[CCSprite spriteWithFile:#"ball.png"];
hole.position=ccp(140+x,318);
hole.tag=i;
[self addChild:hole];
hole.visible=YES;
[collection addObject:hole];
x=x+75;
}
self.isTouchEnabled=YES;
}
return self;
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"count:%i",[collection count]);
UITouch *touch=[touches anyObject];
CGPoint location=[touch locationInView:[touch view]];
location=[[CCDirector sharedDirector]convertToGL:location];
location=[self convertToNodeSpace:location];
for(CCSprite *s in collection)
{
if(CGRectContainsPoint([s boundingBox], location))
s.position=ccp(location.x,location.y);
return;
}
}
You can do that:
Declare this in interface .h file
CCSprite *mSpriteOnHand;
CGPoint mLastPos;
Inside init assign it to nil.
mSpriteOnHand = nil;
In touchesBegan method check like this
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
for(CCSprite *s in collection)
{
if(CGRectContainsPoint([s boundingBox], location))
{
mLastPos = s.position;
s.position=ccp(location.x,location.y);
mSpriteOnHand = s;
break;
}
}
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[touches anyObject];
CGPoint location=[touch locationInView:[touch view]];
location=[[CCDirector sharedDirector]convertToGL:location];
location=[self convertToNodeSpace:location];
if(mSpriteOnHand)
{
mSpriteOnHand.position = location;
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(mSpriteOnHand)
{
mSpriteOnHand.position = mLastPos;
mSpriteOnHand = nil;
}
}

Sprite disappearing when I am trying to move it

Im trying to get my sprite to move on touch but seems to disappear on touch then reappear on second touch . I do not know how to fix this to get my sprite to move at the direction I tap. I have been trying to figure this out for a while but seems I am out of luck. I am hoping someone can point me at the right direction.
CGSize winSize = [[CCDirector sharedDirector] winSize];
player = [CCSprite spriteWithFile:#"Player.png"
rect:CGRectMake(0, 0, 27, 40)];
player.position = ccp(player.contentSize.width/2, winSize.height/2);
[self addChild:player z:1];
(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:YES];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
-(void)setPlayerPosition:(CGPoint)position {
player.position = position;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint playerPos = player.position;
CGPoint diff = ccpSub(touchLocation, playerPos);
if (abs(diff.x) > abs(diff.y)) {
if (diff.x > 0) {
playerPos.x += contentSize_.width;
} else {
playerPos.x -= contentSize_.width;
}
} else {
if (diff.y > 0) {
playerPos.y += contentSize_.height;
} else {
playerPos.y -= contentSize_.height;
}
}
The syntax of your touch function seems to be different.
try this code instead
- (void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
CGPoint playerPos = player.position;
}

Particle engine problem

i am implementing particle engine and here is my code:
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] ))
{
self.isTouchEnabled = YES;
emitter = [[CCParticleMeteor alloc] init];
emitter.texture = [[CCTextureCache sharedTextureCache] addImage:#"stars.png"];
emitter.position = ccp( 240, 160 );
[self addChild:emitter];
}
return self;
}
-(BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self ccTouchesMoved:touches withEvent:event];
return YES;
}
-(BOOL) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
CGPoint point = [[CCDirector sharedDirector] convertToGL:location];
emitter.position = ccp(point.x , point.y);
return YES;
}
And Here is the Screen shot:
Here is i want to need:
1-I want to change the direction of effect( Means flame move upward but i want downward).
2-I want to change the color of flame.
Please help if you can.....
1) A quick look at the particle system documentation gives you either angle or gravity to play with - try setting these to different values i.e.
emitter.angle = <the angle you want>
2) Edit stars.png to be the colour you want?

iPhone - Gestures on UIPickerView and UIWebView

I'm making an iPhone app in which the user can do gestures (left and right swipes) to flick through tabs. My problem is that some of the pages have views such as the pickerview, webview, textfields and buttons. The swipes don't work on these. Is there any way to have global gestures?
For reference, my gesture code example:
//Swipe between tabs
#define mindrag 100
CGPoint mystartTouchPosition;
BOOL isProcessingListMove;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint newTouchPosition = [touch locationInView:self.view];
if(mystartTouchPosition.x != newTouchPosition.x || mystartTouchPosition.y != newTouchPosition.y) {
isProcessingListMove = NO;
}
mystartTouchPosition = [touch locationInView:self.view];
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint currentTouchPosition = [touch locationInView:self.view];
// If the swipe tracks correctly.
double diffx = mystartTouchPosition.x - currentTouchPosition.x + 0.1; // adding 0.1 to avoid division by zero
double diffy = mystartTouchPosition.y - currentTouchPosition.y + 0.1; // adding 0.1 to avoid division by zero
if(abs(diffx / diffy) > 2.5 && abs(diffx) > mindrag)
{
// It appears to be a swipe.
if(isProcessingListMove) {
// ignore move, we're currently processing the swipe
return;
}
if (mystartTouchPosition.x < currentTouchPosition.x) {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:1];
return;
}
else {
isProcessingListMove = YES;
self.tabBarController.selectedViewController = [self.tabBarController.viewControllers objectAtIndex:3];
return;
}
}
else if(abs(diffy / diffx) > 1)
{
isProcessingListMove = YES;
[super touchesMoved:touches withEvent:event];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
isProcessingListMove = NO;
[super touchesEnded:touches withEvent:event];
}
// End of swipe
Any input is appreciated.
You can subclass UIImagePickerController and UIWebView and add these gesture capturing views to them, so long as you gesture detection can still pass touches to the underlying views of course.