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.