Want to drag a SKSpriteNode (follow your finger movements) with SKPhysics (collision enabled)
2 failed solutions:
1)
a) Solution: Change the position of the SKSpriteNode
b) Problem: The SKSpriteNode will "magically" pass through a wall unexpectedly when user drags the spriteNode quickly through the wall (Physics seems not to work)
c) code:
@property (nonatomic) CGPoint translationBy;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
[self panForTranslation:translation];
}
- (void)panForTranslation:(CGPoint)translation {
CGPoint position = [_selectedNode position];
if([[_selectedNode name] isEqualToString:PLAYER_NAME]) {
[_selectedNode setPosition:CGPointMake(position.x + translation.x, position.y + translation.y)];
}
}
2) After searching around here found this advice by @LearnCocos2D "When you use physics, stick to moving the physics body through force, impulse or changing the body velocity directly." So I tried:
a) Solution: Apply impulse on the sprite node.
b) Problem: The sprite node will keep on moving even if I didn't drag the sprite node. (Behaving like a floating boat. Expect it to act like a car once the drag is stopped the sprite node will stop; once drag begins it will follow the finger.)
c) Code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
[self selectNodeForTouch:positionInScene];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
CGPoint translation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
CGVector forceVector = CGVectorMake(translation.x, translation.y);
[self.player.physicsBody applyImpulse:forceVector];
//[self.player.physicsBody setVelocity:CGVectorMake(0, 0)];
}
-->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Updated:
From the comment: "i) You could determine the distance of the node to the finger location, then set the velocity every update: so that it will exactly be on the finger's position the next frame. ii) If both finger and node position are identical this will cancel out any movement, otherwise the node will stick to your finger while performing all physics collisions and will continue to push objects between itself and the finger in cases where it is obstructed."
a) Question: How to disable flick?
b) Problem: The sprite node will move very far away with a minor (accidental) flick.
c) code:
@property (nonatomic) CGPoint translationBy;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
CGPoint previousPosition = [touch previousLocationInNode:self];
// 1) You could determine the distance of the node to the finger location,
CGPoint xyTranslation = CGPointMake(positionInScene.x - previousPosition.x, positionInScene.y - previousPosition.y);
if (fabsf(positionInScene.x - previousPosition.x) < kPlayerMovementThreshold) {
xyTranslation.x = 0;
}
if (fabsf(positionInScene.y - previousPosition.y) < kPlayerMovementThreshold) {
xyTranslation.y = 0;
}
self.translationBy = xyTranslation;
}
static const CGFloat kPlayerMovementSpeed = 55.0f;
static const CGFloat kPlayerMovementThreshold = 1.0f;
-(void)update:(CFTimeInterval)currentTime{
// ii) then set the velocity every update: so that it will exactly be on the finger's position the next frame.
CGFloat dx = self.translationBy.x*kPlayerMovementSpeed;
CGFloat dy = self.translationBy.y*kPlayerMovementSpeed;
CGVector velocityVector = CGVectorMake(dx,dy);
[self.player.physicsBody setVelocity:velocityVector];
}