20.9 C
Canberra
Thursday, October 23, 2025

SpriteKit dragging a node with a SKPhysicsJointSpring is there a solution to get the spring to trace its anchor level tightly however damp quickly?


A big mechanic of a venture I’m engaged on goes to contain dragging objects together with your fingers and having the thing being dragged work together correctly with physics, pushing different objects away.

I created this proof of idea with 5 inexperienced containers meant to be hit by a purple field that follows the finger location (in debug mode marked in blue) and makes use of a spring joint.

The tough bit right here is that I would like the physics physique for the purple field I’m transferring to not be static. So I must in some way transfer it in a method the physics simulation can react to. I’ve heard of individuals making use of the fingers motion as velocity to the physics node however I don’t anticipate this to work practically as easily as if I might simulate a really tight rubber band which damps shortly in some way.

Points with transferring with velocity:

  • When you push the purple field into an object that object pushes again and because of this the purple field is now not below your finger and is regularly additional away.

Points with spring:

  • Would not really feel strongly sufficient connected to the dragging node
  • Oscillations/vibrations/loops and so on

See gif and code.

Are there values of damping/frequency/friction/mass that may get this to really feel good? As a be aware I’m not certain why however the blue drag deal with node does collide with the inexperienced containers despite the fact that it’s static and set to not collide… Undecided why.

class GameScene: SKScene {
    let field = {
        let node = DraggableNode(shade: .purple, measurement: CGSize(width: 50, top: 50))
        node.place = CGPointMake(150, 150)
        node.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, top: 50))
        node.physicsBody?.allowsRotation = false
        node.physicsBody?.affectedByGravity = false
        node.physicsBody?.isDynamic = true
        return node
    }()
    
    override func didMove(to view: SKView) {
        physicsBody = SKPhysicsBody(edgeLoopFrom: body)
        addChild(field)
        
        // Add smaller containers to collide with
        let targetCount = 5
        let targetSize = CGSize(width: 30, top: 30)
        let targetY = measurement.top * 0.75
        for i in 0..<targetCount {
            let fraction = (CGFloat(i) + 1) / CGFloat(targetCount + 1)
            let targetX = measurement.width * fraction
            let goal = SKSpriteNode(shade: .inexperienced, measurement: targetSize)
            goal.place = CGPoint(x: targetX, y: targetY)
            goal.physicsBody = SKPhysicsBody(rectangleOf: targetSize)
            goal.physicsBody?.affectedByGravity = false
            goal.physicsBody?.allowsRotation = false
            addChild(goal)
        }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        guard let contact = touches.first else { return }
        let location = contact.location(in: self)
        field.dragPosition = location
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        guard let contact = touches.first else { return }
        let location = contact.location(in: self)
        field.dragPosition = location
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        field.endDrag()
    }
    
    override func touchesCancelled(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        field.endDrag()
    }
}

class DraggableNode: SKSpriteNode {
    let debug: Bool = true
    
    var dragPosition: CGPoint = .zero {
        didSet {
            updateDragConnection()
        }
    }
    
    personal var dragHandleNode: SKNode? = nil
    personal var dragJoint: SKPhysicsJointSpring? = nil
    
    personal func updateDragConnection() {
        if dragHandleNode == nil {
            beginDrag()
        }
        
        dragHandleNode?.place = dragPosition
    }
    
    func beginDrag() {
        assert(dragHandleNode == nil && dragJoint == nil)
        guard let scene = self.scene else {
            assertionFailure("Have to be added to the scene for this to work")
            return
        }
        
        let deal with: SKNode
        if debug {
            deal with = SKSpriteNode(shade: .blue, measurement: CGSize(width: 10, top: 10))
        } else {
            deal with = SKNode()
        }
        deal with.physicsBody = SKPhysicsBody(circleOfRadius: 1)
        deal with.physicsBody?.isDynamic = false
        deal with.physicsBody?.affectedByGravity = false
        deal with.physicsBody?.allowsRotation = false
        // TODO: I meant this to imply that the blue dot wouldn't collide with another objects
        deal with.physicsBody?.collisionBitMask = 0
        deal with.physicsBody?.contactTestBitMask = 0
        dragHandleNode = deal with
        scene.addChild(deal with)
        deal with.place = dragPosition
        
        guard let handleBody = deal with.physicsBody else {
            assertionFailure("Surprising subject. Deal with has no physics physique")
            return
        }
        guard let nodeBody = self.physicsBody else {
            assertionFailure("Draggable node should have a physics physique")
            return
        }
        
        let distinction = CGPointMake(dragPosition.x - self.dragPosition.x, dragPosition.y - self.dragPosition.y)
        
        let joint = SKPhysicsJointSpring.joint(withBodyA: handleBody, bodyB: nodeBody, anchorA: dragPosition, anchorB: CGPointMake(place.x - distinction.x, place.y - distinction.y))
        // Try at fast dampening and being tied to the thing
        joint.damping = 1.8
        joint.frequency = 2.5
        scene.physicsWorld.add(joint)
        dragJoint = joint
    }
    
    func endDrag() {
        guard let scene = self.scene else { return }
        if let joint = dragJoint {
            scene.physicsWorld.take away(joint)
            dragJoint = nil
        }
        dragHandleNode?.removeFromParent()
        dragHandleNode = nil
    }
}

// A pattern SwiftUI making a GameScene and sizing it
// at 300x400 factors
struct ContentView: View {
    var scene: SKScene {
        let scene = GameScene()
        scene.measurement = CGSize(width: 300, top: 400)
        scene.scaleMode = .fill
        return scene
    }
    
    var physique: some View {
        SpriteView(scene: scene, debugOptions: .showsPhysics)
            .body(width: 300, top: 400)
            .ignoresSafeArea()
    }
}

#Preview {
    ContentView()
}

With velocity the purple field you’re "dragging" is shortly away out of your finger

SpriteKit dragging a node with a SKPhysicsJointSpring is there a solution to get the spring to trace its anchor level tightly however damp quickly?

With Spring there are undesirable oscillations/loops/jiggles

With Spring there are undesirable oscillations/loops/jiggles

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

[td_block_social_counter facebook="tagdiv" twitter="tagdivofficial" youtube="tagdiv" style="style8 td-social-boxed td-social-font-icons" tdc_css="eyJhbGwiOnsibWFyZ2luLWJvdHRvbSI6IjM4IiwiZGlzcGxheSI6IiJ9LCJwb3J0cmFpdCI6eyJtYXJnaW4tYm90dG9tIjoiMzAiLCJkaXNwbGF5IjoiIn0sInBvcnRyYWl0X21heF93aWR0aCI6MTAxOCwicG9ydHJhaXRfbWluX3dpZHRoIjo3Njh9" custom_title="Stay Connected" block_template_id="td_block_template_8" f_header_font_family="712" f_header_font_transform="uppercase" f_header_font_weight="500" f_header_font_size="17" border_color="#dd3333"]
- Advertisement -spot_img

Latest Articles