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,
  • 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)
        }
    }
Answered by hamimate in 405089022

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

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)
  }
  }
  }
make SKPhysicsBody unidirectional
 
 
Q