iosswiftsprite-kitskspritenodeskphysicsbody

make SKPhysicsBody unidirectional


I have a SKSpriteNode as a ball, it's been given all the SKPhysicsBody properties move around in all direction. What I want now is to make it unidirectional (only move in that direction it hasn't move to before and not go back in to a path it had move upon). Currently I have following thoughts on this the problem,

Supporting Code snippets for better understanding,

//getting current touch position by using UIEvent methods
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else {return}
        let location = touch.location(in: self)
        lastTouchPoint = location
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else {return}
        let location = touch.location(in: self)
        lastTouchPoint = location
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        lastTouchPoint = nil
    }
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        lastTouchPoint = nil
    }

//ball created
    func createPlayer(){
        player = SKSpriteNode(imageNamed: "player")
        player.position = CGPoint(x: 220, y: 420)
        player.zPosition = 1

    //physics for ball
    player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
    player.physicsBody?.allowsRotation = false
    player.physicsBody?.linearDamping =  0.5


    player.physicsBody?.categoryBitMask = collisionTypes.player.rawValue
    player.physicsBody?.contactTestBitMask = collisionTypes.finish.rawValue
    player.physicsBody?.collisionBitMask = collisionTypes.wall.rawValue

    addChild(player)
}

//unwarp the optional property, calclulate the postion between player touch and current ball position
override func update(_ currentTime: TimeInterval) {
    guard isGameOver == false else { return }
    if let lastTouchPosition = lastTouchPoint {
        //this usually gives a large value (related to screen size of the device) so /100 to normalize it
        let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
        physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
    }
}

Solution

  • Well it was a combination little hacks in touchesBegan/ touchesMoved and update func,

    First you need to catch on which touch occurred, get it's name (in my case I made nodes which had alpha of 0, but become visible upon moving over them i.e alpha 1). In touchesBegan, touchesMoved as follow

                            guard let touch = touches.first else {return}
                            let location = touch.location(in: self)
                            lastTouchPoint = location
    
                            let positionInScene = touch.location(in: self)
                            let touchedNode = self.atPoint(positionInScene)
    
                            if let name = touchedNode.name
                            {
                                if name == "vortex"
                                {
                                    touching = false
                                    self.view!.isUserInteractionEnabled = false
                                    print("Touched on the interacted node")
                                }else{
                                    self.view!.isUserInteractionEnabled = true
                                    touching = true
                                }
                            }
                        }
    

    Second use a BOOL touching to track user interactions, on the screen by using getting a tap recogniser setup, as follow

    func setupTapDetection() {
            let t = UITapGestureRecognizer(target: self, action: #selector(tapped(_:)))
            view?.addGestureRecognizer(t)
        }
    
    @objc func tapped(_ tap: UITapGestureRecognizer) {
            touching = true
        }
    

    Finally in update put checks as follow,

            guard isGameOver == false else { return }
            self.view!.isUserInteractionEnabled = true
    
            if(touching ?? true){
                if let lastTouchPosition = lastTouchPoint {
                    //this usually gives a large value (related to screen size of the device) so /100 to normalize it
                    let diff = CGPoint(x: lastTouchPosition.x - player.position.x, y: lastTouchPosition.y - player.position.y)
                    physicsWorld.gravity = CGVector(dx: diff.x/100, dy: diff.y/100)
                }
            }
        }