Search code examples
iosswiftsprite-kitskspritenodeskphysicsworld

How to drag and flick a node in SpriteKit while gravity is present?


With my current code, the node is extremely laggy, and moves or teleports in random directions for some reason when its flicked. How can i fix this, and also can someone explain why it is teleporting and moving to random places in the scene.

Also, is there anyway to allow the node to be moved only when it is dragged from its position, rather than being at the gesturerecognizer's coordinates at all times?


    override func didMove(to view: SKView) {
    let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.pan(_:)))
    

    view.addGestureRecognizer(gestureRecognizer)
    circleNode.physicsBody = SKPhysicsBody(circleOfRadius: 20)
    self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    self.addChild(circleNode)
}


@objc func pan(_ recognizer: UIPanGestureRecognizer) {
   

    
    if recognizer.state == .changed {

        self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        var location = recognizer.location(in: self.view!)
        location = self.convertPoint(fromView: location)
        
        
        circleNode.position = location
        
        
        
        
    }
    
    if recognizer.state == .ended {
        self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
        let transformerX = 1024/self.view!.frame.size.width
        let transformerY = 768/self.view!.frame.size.height
        let velocity = recognizer.velocity(in: self.view)
        circleNode.physicsBody?.applyForce(CGVector(dx: velocity.x * transformerX, dy: velocity.y * transformerY))



    }
    
    
    
}

   


Solution

  • enter image description hereHere is some code I was playing around with. I'm able to drag and flick a spear (spear Image) and also "pop" a pig head. This is the whole GameScene.Remove the code you don't need. :)

     import SpriteKit
     import CoreMotion
    
    class GameScene: SKScene, SKPhysicsContactDelegate  {
    
      enum CollisionTypes: UInt32{
        case spear = 1
        case wall = 2
        case head = 4
      }
    
    
      var touchPoint: CGPoint = CGPoint()
      var touching: Bool = false
    
    override func didMove(to view: SKView) {
        self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
        //Add contact delegate
        physicsWorld.contactDelegate = self
        self.backgroundColor = .white
        
        self.addChild(spearNode)
        self.addChild(headNode)
    }
    
    
    
    lazy var spearNode: SKSpriteNode = {
        let node = SKSpriteNode(imageNamed: "spear2")
        node.name = "Spear"
        node.physicsBody = SKPhysicsBody(texture: node.texture!,
                                         size: CGSize(width: node.frame.width  , height: node.frame.height))
        node.position = CGPoint(x:self.frame.midX , y:self.frame.midY)
        node.physicsBody?.affectedByGravity = true
        node.physicsBody?.allowsRotation = false
        node.size = CGSize(width: node.frame.width , height: node.frame.height )
        node.physicsBody?.categoryBitMask = CollisionTypes.spear.rawValue
        node.physicsBody?.contactTestBitMask = CollisionTypes.head.rawValue
        node.physicsBody?.collisionBitMask = CollisionTypes.head.rawValue
        return node
    }()
    
    lazy var headNode: SKSpriteNode = {
        let node = SKSpriteNode(imageNamed: "Pig")
        node.name = "Pig"
        node.physicsBody = SKPhysicsBody(texture: node.texture!,
                                         size: CGSize(width: node.frame.width  , height: node.frame.height))
        node.position = CGPoint(x:self.frame.midX , y:self.frame.maxY - 100)
        node.physicsBody?.affectedByGravity = true
        node.physicsBody?.allowsRotation = false
        node.size = CGSize(width: node.frame.width / 2  , height: node.frame.height / 2 )
        node.physicsBody?.categoryBitMask = CollisionTypes.head.rawValue
        return node
        
    }()
    
    func didBegin(_ contact: SKPhysicsContact){
        guard let nodeA = contact.bodyA.node else {return}
        guard let nodeB = contact.bodyB.node else {return}
        
        print("Contacted")
        
        if nodeA.name == "Pig" && nodeB.name == "Spear"{
            nodeA.removeFromParent() 
        }
        
        if nodeA.name == "Spear" && nodeB.name == "Pig"{    
            nodeB.removeFromParent()
        }
    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first!
        let location = touch.location(in:self)
        
        if spearNode.frame.contains(location) {
            touchPoint = location
            touching = true
        }
    }
    
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first!
        let location = touch.location(in: self)
        touchPoint = location
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        touching = false
    }
    
    
    override func update(_ currentTime: TimeInterval) {
        physicsWorld.gravity = CGVector(dx:0, dy: -9.8)
        
        if touching {
            let dt:CGFloat = 1.0/60.0
            let distance = CGVector(dx: touchPoint.x-spearNode.position.x, dy: touchPoint.y-spearNode.position.y)
            let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
            spearNode.physicsBody!.velocity=velocity
        }
      }
    }