Search code examples
swiftsprite-kitgame-physicsskphysicsbody

Friction in SpriteKit


I'm working with Swift, SpriteKit and Xcode 6, I have a circle node on my screen and I can change the node's position with my finger, but I would like to implement a friction effect to this node, but I don't now how to do so, here's my code :

class PlayScene: SKScene
{    
let firstCircle = SKSpriteNode(imageNamed: "Circle")

// theses 3 variables allows me to move the node according to my finger, but my finger don't have to touch the node
var departCircleLocation = CGPoint()
var departTouchLocation = CGPoint()
var currentTouchLocation = CGPoint()

override func didMoveToView(view: SKView)
{
    addChild(firstCircle)
}

override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
    for touch: AnyObject in touches
    {
        departTouchLocation = touch.locationInNode(self) 
    }

    departCircleLocation = firstCircle.position
}

override func touchesMoved(touches: NSSet, withEvent event: UIEvent)
{
    for touch: AnyObject in touches
    {
        currentTouchLocation = touch.locationInNode(self)
    }

    moveCircle()
}

func moveCircle()
{
    firstCircle.position.x = departCircleLocation.x - departTouchLocation.x + currentTouchLocation.x
    firstCircle.position.y = departCircleLocation.y - departTouchLocation.y + currentTouchLocation.y
}
}

I tried to put this into my code but it doesn't worked :

firstCircle.physicsBody = SKPhysicsBody(circleOfRadius: 7)
firstCircle.physicsBody?.affectedByGravity = false
firstCircle.physicsBody?.friction = 0.4

I don't want gravity because the game is viewed from above

Can someone explain to me how I could implement friction to this node ? I want for example that when I slide up with a certain speed and when I release my finger, the node continue to go up a little amount of time, but if I put my finger on the screen again, the friction stop immediately, for example:

If I put my finger 50pixels under the node, it will do nothing, but if I move my finger (which is 50pixels under the node) to 80pixels under the node, the node will move from 30 pixels down, and this works in any x and y directions, I can move the node without touching it, but it will follow the path if my finger, now if for example I move my finger from 30 pixels down really quickly and the I release my finger, the node will continue to go down due to the friction force, but my finger is no longer on the screen (the effect looks like when we scroll a website on an iphone, there is a little bit of friction when you release your finger due to the speed of your swipe) but I can't use UISwipeGesture because it only detects up, right, down and left directions, not every directions like I want. I hope that you can understand what I'm saying


Solution

  • Here's an example of how to move a sprite based on touch. This approach uses the velocity property of the physics body to move the sprite, so it will interact appropriately with other physics bodies in the scene and be affected by damping, friction, etc.

    First, define the scale and damping properties. The scale controls the rate at which the circle moves toward the location of the touch. A larger value will move the circle at a faster rate. The damping property is used to slow the sprite when the touch ends. A smaller value will slow the sprite at a faster rate.

    let scale:CGFloat = 2.0
    let damping:CGFloat = 0.98
    
    var point:CGPoint?
    

    Save the location when a touch begins. The sprite will move toward this location.

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {        
        if let touch = touches.anyObject() as UITouch? {
            let location = touch.locationInNode(self)
            point = location
        }
    }
    

    Update the location when the touch moves

    override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
        if let touch = touches.anyObject() as UITouch? {
            let location = touch.locationInNode(self)
            point = location
        }
    }
    

    Reset the touch location when the touch ends

    override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        point = nil
    }
    

    Move the sprite toward the specified location

    func moveNodeToPoint(sprite:SKSpriteNode, point:CGPoint) {
        var dx:CGFloat = (point.x - sprite.position.x) * scale
        var dy:CGFloat = (point.y - sprite.position.y) * scale
        sprite.physicsBody?.velocity = CGVectorMake(dx, dy)
    }
    

    This is called before each frame is rendered. It calls the function that updates the location of the sprite if a touch is active, else it slows the sprite over time

    override func update(currentTime: CFTimeInterval) {
        if (point != nil) {
            moveNodeToPoint(firstCircle, point: point!)
        }
        else {
            // This will slow the circle over time when after the touch ends
            let dx = firstCircle.physicsBody!.velocity.dx * damping
            let dy = firstCircle.physicsBody!.velocity.dy * damping
            firstCircle.physicsBody!.velocity = CGVectorMake(dx, dy)
        }
    }