skip to Main Content

What i’m trying to do: Make a SKSpriteNode follow a UIBezierPath where the endpoint is the position of the users touch.

The problem: When the user touches i send the location to the player node which handles moving it self. However this only works when the player node is ‘pointing’ upwards, for example at the first touch. Other touches make the player move to the wrong point.

Image describing the problem: http://i.stack.imgur.com/nleXj.png
I have added some explaining things in photoshop so here is an explanation:
– Thick > is the SKSpriteNode that i’m moving.
– Purple squares is the exact location of the touches, numbered in order (actually SKSpriteNodes added for debug purposes)
– Thin arrows is the position of the player when the touches occurred.

I believe the issue is somewhere in the conversion between the player nodes coordinate system and the scenes coordinate system.
For example: The first touch (when player zRotation hasn’t been changed) moves the player to the correct position. The second touch however is to the left in the player nodes coordinate system, but then it basically goes to left in the scenes coordinate system as if the touch was rotated with the node if it were rotated to point upwards.

The code:
GameScene.m:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    for (UITouch *touch in touches) {
        [self playRiddle];

        CGPoint touchPoint = [touch locationInNode:_player];
        [_player moveTo:touchPoint];

        SKSpriteNode *n = [SKSpriteNode spriteNodeWithColor:[UIColor purpleColor] size:CGSizeMake(10, 10)];
        n.position = [self convertPoint:touchPoint fromNode:_player];
        [_worldNode addChild:n];
    }
}

Player.m:

- (void)moveTo:(CGPoint)point {
    _moving = YES;
    [self removeActionForKey:@"move"];

    CGVector vector = [self convertAngleToVector:[self shipOrientation]];
    CGPoint controlPoint = CGPointMake(vector.dx, vector.dy);

    UIBezierPath *path = [self createPathWindEndPoint:point controlPoint:controlPoint];
    SKAction *move = [SKAction followPath:[path CGPath] asOffset:YES orientToPath:YES speed:15];
    SKAction *done = [SKAction runBlock:^{
        _moving = NO;
    }];
    [self runAction:[SKAction sequence:@[move, done]] withKey:@"move"];
}

- (UIBezierPath *)createPathWindEndPoint:(CGPoint)endpoint controlPoint:(CGPoint)cp {    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointZero];

    [path addQuadCurveToPoint:endpoint controlPoint:cp];

    //Draw path
    SKShapeNode *line = [SKShapeNode node];
    line.path = [path CGPath];
    [line setStrokeColor:[UIColor darkGrayColor]];
    line.position = self.position;
    [self.parent addChild:line];

    return path;
}

- (CGVector)convertAngleToVector:(CGFloat)radians {
    CGVector vector;
    vector.dx = cos(radians) * 25;
    vector.dy = sin(radians) * 25;
    return vector;
}

- (CGFloat)shipOrientation {
    return self.zRotation + M_PI_2;
}

The _player nodes parent is a SKNode that is added to the GameScene.
I’ve tried a lot of stuff with [node convertPoint: ..] but with no success. Would love some pointers in the right direction.

Thank you!

2

Answers


  1. If you are looking to convert a points between a node and its parent, then try this code:

    CGPoint myPoint = [self convertPoint:myNode.position fromNode:myNode.parent];
    

    You should also take a look at Converting Between View and Scene Coordinates.

    Login or Signup to reply.
  2. I suggest that you use absolute scene coordinates instead of relative node coordinates to create the path to follow. Here’s an example of how to do that:

    GameScene

    1) In touchesBegan, make the following change to convert the touch location to scene coordinates

    CGPoint touchPoint = [touch locationInNode:self];
    

    Player.m

    2) Change the following to create the path in scene coordinates (see comments)

    - (void)moveTo:(CGPoint)point {
        _moving = YES;
        [self removeActionForKey:@"move"];
    
        CGVector vector = [self convertAngleToVector:[self shipOrientation]];
        // Offset the control point by the node's position
        CGPoint controlPoint = CGPointMake(vector.dx+self.position.x, vector.dy+self.position.y);
    
        UIBezierPath *path = [self createPathWindEndPoint:point controlPoint:controlPoint];
        // Use absolute path
        SKAction *move = [SKAction followPath:[path CGPath] asOffset:NO orientToPath:YES speed:15];
    
        SKAction *done = [SKAction runBlock:^{
            _moving = NO;
        }];
        [self runAction:[SKAction sequence:@[move, done]] withKey:@"move"];
    }
    
    - (UIBezierPath *)createPathWindEndPoint:(CGPoint)endpoint controlPoint:(CGPoint)cp {
        UIBezierPath *path = [UIBezierPath bezierPath];
        // Use scene coordinates
        [path moveToPoint:self.position];
    
        [path addQuadCurveToPoint:endpoint controlPoint:cp];
    
        //Draw path
        SKShapeNode *line = [SKShapeNode node];
        line.path = [path CGPath];
        [line setStrokeColor:[UIColor darkGrayColor]];
        // Line relative to the origin
        line.position = CGPointZero;
        [self.parent addChild:line];
    
        return path;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search