I'm working on an Xcode Playground and I'm having some problems with SpriteKit collisions.
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:
usesPreciseCollisionDetection = true
on the first boxphysicsWorld.speed
to a number higher than 1I 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.