0

So I made an SKSpriteNode like this:

SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:@"ball.png"];
ball.size = CGSizeMake(40, 40);
ball.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
ball.name = @"ball";
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
[self addChild:ball];

And I can move it around like this:

-(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:@"ball"]) {
        draggedBall = [self nodeAtPoint:[[touches anyObject]locationInNode:self]];
        draggedBallStartPosition = draggedBall.position;
     }

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([[touches anyObject]locationInNode:self].x -draggedBall.position.x < 50
    && [[touches anyObject]locationInNode:self].x -draggedBall.position.x > -50) {
        draggedBall.position = [[touches anyObject]locationInNode:self];
        draggedBall.physicsBody.affectedByGravity = NO;

    }
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    float dx = draggedBall.position.x - draggedBallStartPosition.x;
    float dy = draggedBall.position.y - draggedBallStartPosition.y;
    NSLog(@"DX: %f DY: %f",dx,dy);
    [draggedBall.physicsBody applyImpulse:CGVectorMake(dx, dy)];
    draggedBall.physicsBody.affectedByGravity = YES;
    draggedBall = nil;
}

draggedBall is an SKNode ivar. So what I want to do is swipe the ball and when it releases it continues to go in that direction. The quicker you swipe the greater the impulse. Now I tried (and failed) to do that, but this obviously doesn't work. Somehow the last direction of the ball needs to be detected, then the amount of time between starting to go in that direction and releasing the ball. And depending on how long that amount of time is, affect the velocity of the ball. Since this is a game-mechanic that is frequently used, I figure that there is an standardized way of doing this. Anyway, thanks for helping.

Squid
  • 315
  • 1
  • 11

1 Answers1

0

I'm unfamiliar with a standard way of doing this, however I use the following approach to create a vector from a flick anywhere on the screen and then apply it to a nodes physics body. Maybe you can adopt the touch logic to suit your requirements.

The following is all in an SKScene.m subclass, however it could be implemented in any SKNode subclass.

Start with a scalar that can be tweaked later on during testing.

#define FLICK_SCALAR 0.5

The equation to be used needs time and point I wrap it in a struct along with a flag to indicate start timing. So at the top of the .m put the following before the @implementation and any class extension.

typedef struct TouchData {
CGPoint point;
NSTimeInterval time;
BOOL hasStarted;// flick has started
} TouchData;

Then in a class extension after the above I add the following property.

@property TouchData touchOriginData;

The overridden touch methods have the following implementation.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// store the start data
for (UITouch *touch in touches) {
    // testing flick gesture
    _touchOriginData.point = [touch locationInNode:self];
    _touchOriginData.time = [touch timestamp];
    _touchOriginData.hasStarted = NO;
    }
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//NSLog(@"touches moved");
if (!_touchOriginData.hasStarted) {
    // must be moving for the first time so start timing now
    for (UITouch *touch in touches) {
        _touchOriginData.time = [touch timestamp];
        _touchOriginData.hasStarted = YES;
        }
    }
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// store the end data
TouchData touchEndData;
touchEndData.time = 0;
touchEndData.point.x = 0.0;
touchEndData.point.y = 0.0;
touchEndData.hasStarted = NO;
for (UITouch *touch in touches) {
    touchEndData.point = [touch locationInNode:self];
    touchEndData.time = touch.timestamp;
}
if (_touchOriginData.hasStarted) {
    // calculate the impulse vector from TouchData structs
    NSTimeInterval timeTaken = touchEndData.time - _touchOriginData.time;
    CGFloat vector_x = (touchEndData.point.x - _touchOriginData.point.x) / timeTaken * FLICK_SCALAR;
    CGFloat vector_y = (touchEndData.point.y - _touchOriginData.point.y) / timeTaken * FLICK_SCALAR;
    CGVector impulseVector = CGVectorMake(vector_x, vector_y);
    // here is where you can now apply an impulse to a 
    // physics body of, in your case, a ball node.
    // get the ball node (ball)
    SKNode *ball = [self childNodeWithName:@"ball"];
    // apply the impulse
    [ball.physicsBody applyImpulse: impulseVector];

    }
}

NOTE - I also set multiTouchEnabled to NO for the scene, somewhere at the start of its lifecycle.

Bamsworld
  • 5,670
  • 2
  • 32
  • 37
  • Okay, this is great, but what If I make a snake-like motion before flicking the ball, so I first drag the ball around in random directions and then release it? Wouldn't it go somewhere I don't want it to? – Squid Apr 03 '15 at 14:46
  • I see your point (no pun intended). The code only will produce a vector based on from when touch starts moving to where it ends over time taken. hmmm Im thinking the touch logic will need to know when to turn the hasStarted boolean to yes. – Bamsworld Apr 03 '15 at 14:55
  • I think I figured it out, I just don't know how to code it yet. Whenever you flick it, It is a linear movement. You can just test whether dx and dy increase linearly constantly, then if they do, set hasStarted to yes – Squid Apr 03 '15 at 14:57
  • So you're saying calc acceleration of dx and dy and if above a certain threshold that could be tweaked set hasStarted to yes. This may require an additional property of type TouchData named say touchPreviousMoved and then it should be able to be worked out. – Bamsworld Apr 03 '15 at 15:06
  • We can do it like this: add the slope of dx dy to TouchData and a margin. If the slope exceeds that margin it means it is not a swipe but a drag. If the user releases, there is always going to be a swipe, but depending on how the user moved the ball, the vector changes. – Squid Apr 03 '15 at 15:33