1 Reply
      Latest reply on Feb 6, 2020 1:06 AM by hamimate
      hamimate Level 1 Level 1 (0 points)

        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,

        • make a fieldBitMask, to the path that is iterated by it and repel the ball to not go back
        • apply some kind of force/ impulses on the ball from touchesBegan/ touchesMoved method to keep it from going back
        • something that can be handled in update method
        • a lifesaver from dev forums, who is coding even on the weekend

         

        Supporting Code snippets for better understanding,

         

        //getting current touch position by using UIEvent methods
        override func touchesBegan(_ touches: Set, with event: UIEvent?) {
          guard let touch = touches.first else {return}
          let location = touch.location(in: self)
          lastTouchPoint = location
          }
          override func touchesMoved(_ touches: Set, with event: UIEvent?) {
          guard let touch = touches.first else {return}
          let location = touch.location(in: self)
          lastTouchPoint = location
          }
          override func touchesEnded(_ touches: Set, with event: UIEvent?) {
          lastTouchPoint = nil
          }
          override func touchesCancelled(_ touches: Set, 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)
                }
            }
        
        
        • Re: make SKPhysicsBody unidirectional
          hamimate Level 1 Level 1 (0 points)

          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)
            }
            }
            }