Sprite Kit physicsBody.resting behavior
Asked Answered
P

1

7

I am using Swift and Sprite Kit to develop a game on XCode Beta 6. In order to detect if all nodes are sleeping, i check their physicsBody.resting property. In update method i print out the result.

import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {

    var hero:SKSpriteNode!

    override func didMoveToView(view: SKView) {
        self.physicsWorld.gravity = CGVectorMake(0, 0)
        self.physicsWorld.contactDelegate = self
        self.physicsBody = SKPhysicsBody(edgeLoopFromRect:self.frame)


        hero = SKSpriteNode(imageNamed: "Spaceship")
        hero.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
        hero.zPosition = 10.0
        hero.physicsBody = SKPhysicsBody(circleOfRadius: hero.size.width/2)
        hero.physicsBody.allowsRotation = false
        hero.physicsBody.linearDamping = 0.5
        self.addChild(hero)
    }

    override func update(currentTime: CFTimeInterval) {
        if hero.physicsBody.resting {
            println("resting")
        } else {
            println("moving")
        }
    }
}

To my surprise, the results are:

moving resting moving (n times the same) moving resting

So why the hero is moving, although i didn't do anything. The node moves N times and takes a break(resting), after that goes on moving.

Can anyone explain that behaviour? Is that a bug or do i miss something? Thanks in advance.

Proximity answered 24/8, 2014 at 11:48 Comment(0)
V
14

If you examine the velocity of a physics body, you'll see that it is indeed moving but at a rate that is not perceivable. That's why the resting property is not set. A more reliable way to check if a SKPhysicsBody is at rest is to test if its linear and angular speeds are nearly zero. Here's an example of how to do that:

func speed(velocity:CGVector) -> Float {
    let dx = Float(velocity.dx);
    let dy = Float(velocity.dy);
    return sqrtf(dx*dx+dy*dy)
}

func angularSpeed(velocity:CGFloat) -> Float {
    return abs(Float(velocity))
}

// This is a more reliable test for a physicsBody at "rest"
func nearlyAtRest(node:SKNode) -> Bool {
    return (self.speed(node.physicsBody.velocity)<self.verySmallValue
        && self.angularSpeed(node.physicsBody.angularVelocity) < self.verySmallValue)
}

override func update(_ currentTime: TimeInterval) {
    /* Enumerate over child nodes with names starting with "circle" */
    enumerateChildNodesWithName("circle*") {
        node, stop in
        if (node.physicsBody.resting) {
            println("\(node.name) is resting")
        }
        if (self.nearlyAtRest(node)) {
            println("\(node.name) is nearly resting")
        }
    }
}
Verbal answered 24/8, 2014 at 20:11 Comment(7)
Thanks for your replay. Your approach looks great. I am developing a turn-based game, which needs Networking. Will this way be reliable as well? Because i have to make sure the positions of balls are identically displayed in each player's device.Proximity
Yes, it will work with a game played across a network.Verbal
I believe sqrtf(dxdx+dy+dy) should be sqrtf(dxdx+dy*dy)Elamitic
Just a small comment - the call to sqrtf is unnecessary since you can just compare the result to the square of verySmallValue instead (which can be precomputed).Ljoka
Guys: what is a reasonable verySmallValue in this case? Since my inspection of velocity and angular velocity shows 0.0Manpower
@Manpower I used 1e-5 but I suggest you use a value that makes sense for your specific app.Verbal
For better preformance don’t do the sqrt. Test against speed**2 is just as validMicroelement

© 2022 - 2024 — McMap. All rights reserved.