Danderssen
Danderssen

Reputation: 423

iOS Sprite Kit: How to make Sprite follow Touches

I'm new to Sprite Kit and I'm wondering how you can make a sprite follow the touches. For example, my player sprite is on the bottom of the screen. When I tap on the top of the screen, the player sprite should move to the touch point with a certain speed - and if I move my finger it should always be pointing towards the touch point. This is how I tried to implement it:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    for (UITouch *touch in touches) {

        CGPoint location = [touch locationInNode:self];

        CGPoint diff = rwSub(location, self.player.position);
        CGPoint norm = rwNormalize(diff);

        SKAction *act = [SKAction moveByX:norm.x * 10 y:norm.y * 10 duration:0.1];
        [self.player runAction:[SKAction repeatActionForever:act] withKey:@"move"];


    }
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
    for (UITouch *touch in touches) {

        CGPoint location = [touch locationInNode:self];

        CGPoint diff = rwSub(location, self.player.position);
        CGPoint norm = rwNormalize(diff);

        SKAction *act = [SKAction moveByX:norm.x * 10 y:norm.y * 10 duration:0.1];
        [self.player runAction:[SKAction repeatActionForever:act] withKey:@"move"];
    }

}

However, the sprite moves very laggy when moving the finger. Is there a way I can make the movement nice and smooth?

Any help would be greatly appreciated!

EDIT: I think I have found a solution I modified the touchesMoved function:

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    for (UITouch *touch in touches) {

        [self.player removeActionForKey:@"move"];
        CGPoint location = [touch locationInNode:self];

        CGPoint diff = rwSub(location, self.player.position);
        CGPoint norm = rwNormalize(diff);

        [self.player setPosition: rwAdd(self.player.position, rwMult(norm, 2))];
        SKAction *act = [SKAction moveByX:norm.x * 10 y:norm.y * 10 duration:0.01];
        [self.player runAction:[SKAction repeatActionForever:act] withKey:@"move"];
        }
    }

}

Upvotes: 2

Views: 4173

Answers (1)

Geri Borbás
Geri Borbás

Reputation: 16588

I'd bind the UITouch to the Sprite on touchesBegan, unbind on touchesEnded. Then on every update approach the UITouch position with a one pole filter.

No need for actions at all, also you don't need to implement touchesMoved this way. The whole stuff become more encapsulated.


Or use SKPhysicsJointSpring. Create a node for the touch, then a spring joint that connects your sprite and the touch node. Then adjust touch node position only.


Sprite

@interface ApproachingSprite : SKSpriteNode
@property (nonatomic, weak) UITouch *touch;
@property (nonatomic) CGPoint targetPosition;
-(void)update;
@end

@implementation ApproachingSprite

-(void)update
{
    // Update target position if any touch bound.
    if (self.touch)
    { self.targetPosition = [self.touch locationInNode:self.scene]; }

    // Approach.
    CGFloat filter = 0.1; // You can fiddle with speed values
    CGFloat inverseFilter = 1.0 - filter;
    self.position = (CGPoint){
        self.targetPosition.x * filter + self.position.x * inverseFilter,
        self.targetPosition.y * filter + self.position.y * inverseFilter,
    };
}

@end

Scene

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*) event
{ self.sprite.touch = [touches anyObject]; }

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*) event
{ self.sprite.touch = nil; }

-(void)update:(CFTimeInterval) currentTime
{ [self.sprite update]; }

Upvotes: 3

Related Questions