4

I have a ball, which I fire into the air:

enter image description here

And it is initialised like so:

func initBallNode() {
    // TODO: Create ball node
    ballNode = SKSpriteNode(imageNamed: "Ball1") // Create a ball
    ballNode.zPosition = ZPositions.ball
    let offsetFromCorner: CGFloat = 20 // Offset from bottom-left of screen
    ballNode.position = CGPoint(x: frame.minX + ballNode.size.width / 2 + offsetFromCorner, y: frame.minY + ballNode.size.height / 2 + offsetFromCorner)
    ballNode.physicsBody = SKPhysicsBody(circleOfRadius: ballNode.size.width / 2)
    ballNode.physicsBody?.affectedByGravity = true
    ballNode.physicsBody?.angularDamping = 1 // <-- Does not set a minimum-speed

    addChild(ballNode)
}

How can I reduce the minimum speed/velocity of the ball so that it rolls less and comes to a complete stop earlier?

Is there a physics property to handle this, without affecting the mass of the object (making it fall earlier)?


If you have any questions, please ask!

Maximelc
  • 2,384
  • 1
  • 21
  • 17
George
  • 25,988
  • 10
  • 79
  • 133

2 Answers2

0

You could increase the friction of the physicsBody. By default the friction is 0.2, but it can range between 0.0 and 1.0

JohnV
  • 981
  • 2
  • 8
  • 18
  • Doesn't setting the friction to `1` make it roll as it should (rolling with the surface), and `0` just make it slide (and I am just trying to make it stop rolling earlier)? Does it make it _stop rolling_ earlier? – George Oct 25 '18 at 11:55
  • Currently, my ball gets slower and then there is a point where it appears still, but the position shows that it still rolls for another 5-10 seconds. I need to detect when it stops properly. – George Oct 25 '18 at 11:56
  • @George_E `friction` is the first thing I would try out. I'm not able to test the code, but I thought that increasing the friction would make it stop earlier... – JohnV Oct 25 '18 at 11:59
  • Do I need to set the friction to one for the ball **and** the floor? Is it additive, or does it just pick the highest friction is SpriteKit? – George Oct 25 '18 at 12:00
  • what doe you mean the position shows that it rolls for another 5-10 seconds? If you ball is not moving, and your position is moving, then you are not properly outputting the position – Knight0fDragon Oct 25 '18 at 14:40
  • @Knight0fDragon I can see the ball still rolling for another 5 seconds, but at such a slow pace I want it to stop, therefore by setting a minimum. – George Oct 25 '18 at 15:39
  • Then just set the velocity to 0,0 – Knight0fDragon Oct 25 '18 at 15:45
  • @Knight0fDragon That's what I have done in my solution. – George Oct 25 '18 at 16:06
0

This is still not an answer. This has issues that still go along with it, described in the edit(s) below the original answer.


Due to stored properties in Swift extensions not being allowed (which results in a small bit in Objective-C), this was made quite a bit harder. However, after some research and testing, I have come up with the solution that works well for now:

import SpriteKit


// Key to access the stored properties
private var minimumSpeedAssociatedKey: UInt8 = 0

extension SKPhysicsBody {

    // Minimum speed for each node
    var minimumSpeed: CGFloat? {
        get { return objc_getAssociatedObject(self, &minimumSpeedAssociatedKey) as? CGFloat }
        set(newValue) { objc_setAssociatedObject(self, &minimumSpeedAssociatedKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) }
    }

    // Update the speed for each node
    func updateSpeed() {
        guard let safeMinimumSpeed = minimumSpeed else {
            assert(false, "You have tried to update an object speed, without setting the minimum for the node: \"\(self.node?.description ?? "Unavailable")\".")
        }
        let currentSpeed = sqrt(pow(velocity.dx, 2) + pow(velocity.dy, 2))
        if currentSpeed < safeMinimumSpeed && currentSpeed > 0 {
            angularVelocity = 0
            velocity = CGVector(dx: 0, dy: 0)
        }
    }

}

Place that in a Swift file in your project. Then, you can set the minimum speed:

ballNode.physicsBody?.minimumSpeed = 30

Then, override the update function like so:

override func didFinishUpdate() {
    ballNode.physicsBody?.updateSpeed()
}

As a result, the ball now no longer rolls lower than a velocity of 30, and it stops instead. The stop is still almost unnoticeable, as the ball before was just going extremely slowly over a 5-second period. This is the results I was looking for.

I hope this helps people, and if you found a better solution (or Apple adds a new property), please post your own answer. :)


Edit 1:

I have now got a varying terrain. However, this causes strange physics when the ball rolls back when it hits a hill. This is therefore not a fully solved issue.

George
  • 25,988
  • 10
  • 79
  • 133
  • @Knight0fDragon Updated the answer. Thanks! I have looked at the image provided by Apple about when the updates occur, but how do I know which update to use? – George Oct 25 '18 at 16:10
  • @Knight0fDragon Refering to [this](https://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=2ahUKEwjXrLS5_aHeAhVKyoUKHRmrAOkQjRx6BAgBEAU&url=https%3A%2F%2Fdeveloper.apple.com%2Flibrary%2Farchive%2Fdocumentation%2FGraphicsAnimation%2FConceptual%2FSpriteKit_PG%2FIntroduction%2FIntroduction.html&psig=AOvVaw1DLuUAY2mlBwlODHumL5Zq&ust=1540570219512637), just to be clear. – George Oct 25 '18 at 16:10