user3491793
user3491793

Reputation: 61

Using gestures with SpriteKit to Drag, rotate and scale multiple sprite separately at the same time

I have created an iOS app in which I need to be able to move, rotate and scale a sprite ( I am using Apple's Sprite Kit) at the same time. For the most part I have this working. I currently can touch with 1 finger and move the sprite, and if I use two fingers I can scale and rotate the sprite. To do this I am using UIPanGestureRecognizer, UIPinchGestureRecognizer and UIRotateGestureRecognizer. That works fine. What I would like is, while I am dragging, rotating and scaling a one sprite with my right hand, I can take my left hand and drag rotate and scale a different sprite independently of the other sprite.

Currently I am using iOS gestures to move, rotate and scale the sprites. I used code very close to what I found on Ray Wenderlich's website in his Drag and Drop Sprites tutorial for Sprite Kit. http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites. The part I am using is near the bottom when he start to use UIPanGestureRecognizers instead of just the touch method.

Like I said the Gestures work fine on one sprite at a time. How do I make it work on more than one sprite?

For instance for the UIPanGesturRecognizer I add the code below:

- (void)didMoveToView:(SKView *)view {
      UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc]         initWithTarget:self action:@selector(handlePanFrom:)];
      [[self view] addGestureRecognizer:gestureRecognizer];

}

Then I have a method for that called gestureRecognizer below:

- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {

    CGPoint touchLocation = [recognizer locationInView:recognizer.view];

    touchLocation = [self convertPointFromView:touchLocation];

    [self selectNodeForTouch:touchLocation]; // This just returns the node that has been touched


} else if (recognizer.state == UIGestureRecognizerStateChanged) {

    CGPoint translation = [recognizer translationInView:recognizer.view];
    translation = CGPointMake(translation.x, -translation.y);
    [self panForTranslation:translation];
    [recognizer setTranslation:CGPointZero inView:recognizer.view];

} else if (recognizer.state == UIGestureRecognizerStateEnded) {

   [_selectedNode removeAllActions];

}

}

Finally there is the method that moves the sprite:

- (void)panForTranslation:(CGPoint)translation {

if([[_selectedNode name] isEqualToString:kAnimalNodeName]) {

    CGPoint position = [_selectedNode position];
    // Set variable for the point to move selected node
    CGPoint movePoint = CGPointMake(position.x + translation.x, position.y + translation.y);
    [_selectedNode setPosition:newPos];

}

}

Now the example code is showing only the methods for the UIPanGestureRecognizer but I also have similar methods for the rotate and pinch gestures. All of this code is in my scene class.

Thank you for the help.

Upvotes: 3

Views: 3626

Answers (2)

MattDevOhYeah
MattDevOhYeah

Reputation: 36

I'm also using that tutorial to do something similar. The sample code relies on an instance variable, selectedNode, that represents the one node that is selected.

To make this work for multiple nodes, I would recommend using an NSMutableArray of selectedNodes, or subclassing SKSpriteNode to store whether or not it is currently "selected". Good luck!

Upvotes: 0

maelswarm
maelswarm

Reputation: 1070

Well, the tutorial you posted pretty much shows how to do it...

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint positionInScene = [touch locationInNode:self];
    [self selectNodeForTouch:positionInScene];
}

- (void)selectNodeForTouch:(CGPoint)touchLocation {
    //The below statement assigns touchedNode from a sprite that contains touchLocation
    SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];

  //2
if(![_selectedNode isEqual:touchedNode]) {
    [_selectedNode removeAllActions];
    [_selectedNode runAction:[SKAction rotateToAngle:0.0f duration:0.1]];

    _selectedNode = touchedNode;

        //the below if statement determines what SKNode is to be given a SKAction
    if([[touchedNode name] isEqualToString:kAnimalNodeName]) {
    SKAction *sequence = [SKAction sequence:@[[SKAction rotateByAngle:degToRad(-4.0f) duration:0.1],
                                                  [SKAction rotateByAngle:0.0 duration:0.1],
                                                  [SKAction rotateByAngle:degToRad(4.0f) duration:0.1]]];
        [_selectedNode runAction:[SKAction repeatActionForever:sequence]];
        }
    }
}

So if you want to apply the action to multiple nodes, simply give them the same name. I suggest naming your nodes, put them in an array, and then iterate through them checking if they have the same name. If you have any further questions please comment.

UPDATE: 1

- (void)panForTranslation:(CGPoint)translation {

    //Once again you would do the same thing. Just give the nodes the same name. 
    if([[_selectedNode name] isEqualToString:kAnimalNodeName]) {

        CGPoint position = [_selectedNode position];
        // Set variable for the point to move selected nodes
        CGPoint movePoint = CGPointMake(position.x + translation.x, position.y + translation.y);
        [_selectedNode setPosition:newPos];

     }

Upvotes: 1

Related Questions