1

I'm working on an Xcode Playground and I'm having some problems with SpriteKit collisions.

GIF of Playground

The first box has a mass of 1 and the second box has a mass of 100000 (when the latter is 100, for example, everything works fine). The collisions are elastic (restitution was set to 1) and there is no friction or damping at all.

Here is the code for my scene (ColliderType is just an enum for the categories):

class GameScene: SKScene {
    private var wall: SKSpriteNode!
    private var floor: SKSpriteNode!
    private var box1: SKSpriteNode!
    private var box2: SKSpriteNode!

    override func didMove(to view: SKView) {
        self.backgroundColor = .white

        wall = SKSpriteNode(color: SKColor.black, size: CGSize(width: 10, height: self.frame.height))
        wall.position = CGPoint(x: 100, y: self.frame.height/2)
        wall.physicsBody = SKPhysicsBody(rectangleOf: wall.frame.size)
        wall.physicsBody?.isDynamic = false

        floor = SKSpriteNode(color: SKColor.black, size: CGSize(width: self.frame.width, height: 10))
        floor.position = CGPoint(x: self.frame.width/2, y: 100)
        floor.physicsBody = SKPhysicsBody(rectangleOf: floor.frame.size)
        floor.physicsBody?.isDynamic = false

        box1 = SKSpriteNode(color: SKColor.black, size: CGSize(width: 100, height: 100))
        box1.position = CGPoint(x: 300, y: floor.position.y+box1.size.height/2)
        box1.physicsBody = SKPhysicsBody(circleOfRadius: box1.frame.size.width/2)

        box2 = SKSpriteNode(color: SKColor.black, size: CGSize(width: 100, height: 100))
        box2.position = CGPoint(x: 750, y: floor.position.y+box2.size.height/2)
        box2.physicsBody = SKPhysicsBody(circleOfRadius: box2.frame.size.width/2)

        self.addChild(wall)
        self.addChild(floor)
        self.addChild(box1)
        self.addChild(box2)

        box1.physicsBody?.allowsRotation = false
        box2.physicsBody?.allowsRotation = false

        box1.physicsBody?.restitution = 1
        box2.physicsBody?.restitution = 1

        box1.physicsBody?.mass = 1
        box2.physicsBody?.mass = 100000

        box1.physicsBody?.friction = 0
        box2.physicsBody?.friction = 0

        box1.physicsBody?.linearDamping = 0
        box2.physicsBody?.linearDamping = 0

        wall.physicsBody?.categoryBitMask = ColliderType.Wall.rawValue
        box1.physicsBody?.categoryBitMask = ColliderType.Box1.rawValue
        box2.physicsBody?.categoryBitMask = ColliderType.Box2.rawValue

        box1.physicsBody?.collisionBitMask = ColliderType.Wall.rawValue | ColliderType.Floor.rawValue | ColliderType.Box2.rawValue
        box2.physicsBody?.collisionBitMask = ColliderType.Floor.rawValue | ColliderType.Box1.rawValue

        box1.physicsBody?.contactTestBitMask = ColliderType.Box2.rawValue | ColliderType.Wall.rawValue

        box1.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
        box2.physicsBody?.velocity = CGVector(dx: -50, dy: 0)
    }
}

Things I've tried:

  • Set usesPreciseCollisionDetection = true on the first box
  • Update the positions and check for collisions by myself by overriding the update method (it wasn't as optimised as SpriteKit's engine so it was very slow)
  • Make the second box slower and set physicsWorld.speed to a number higher than 1
João Gabriel
  • 73
  • 3
  • 9

1 Answers1

0

I think in general you're probably expecting too much. SpriteKit is a game engine, designed to give qualitatively reasonable behavior for scenarios involving normal-ish sorts of physics. It's not a high-precision physics simulation, and when you push it into a region where it's got to do something extreme, it's probably going to fail.

In this case, you're expecting it to simulate a huge number of perfectly elastic collisions within a small amount of time and distance (many collisions per animation frame of the simulation as the heavy block mashes the light one into a tiny gap). It's going to find the first collision in the frame, figure the impulses to apply to the blocks, and then advance to the next frame, with the effect that the big block happily mashes into the space where the small one resides. All the subsequent collisions that would prevent that are being missed in search of 60 fps.

bg2b
  • 1,939
  • 2
  • 11
  • 18
  • Thank you so much! I also though that the problem was with SpritKit's limitations... I guess I'll just think about something else then (this is for the Swift Student Challenge) – João Gabriel May 14 '20 at 11:19
  • I'm assuming this is for something like illustrating the colliding blocks to compute pi thing. You might be able to analytically do the computations of what should be happening within the frame and just use SpriteKit for the drawing/animation. Good luck! – bg2b May 14 '20 at 12:32
  • I tried to, but the update method would start to make the live view lag a lot... Also, the cool part would be to use the default physics engine to really show that computing Pi with this is possible. I've already found something else I can work on :) – João Gabriel May 14 '20 at 12:54