Skip to content

Instantly share code, notes, and snippets.

@AchrafKassioui
Last active December 30, 2025 05:24
Show Gist options
  • Select an option

  • Save AchrafKassioui/8b9ce3e0044639154c01bf9a579faa5f to your computer and use it in GitHub Desktop.

Select an option

Save AchrafKassioui/8b9ce3e0044639154c01bf9a579faa5f to your computer and use it in GitHub Desktop.
SpriteKit Physics Determinism
/**
# SpriteKit Physics Determinism
Is SpriteKit's physics engine deterministic?
In some cases, the physics-based behavior is quite repeatable.
In other cases, the simulation yields a different outcome every run.
This is a setup to explore such behaviors.
Press one of the buttons to play a sequence.
Explore further by modifying the methods called by the buttons.
## Links
- https://stackoverflow.com/questions/38995939/spritekit-physics-giving-different-results-each-time
- https://medium.com/element84/comparing-sprite-kit-physics-to-direct-box2d-5955b6653f71
Achraf Kassioui
Created 29 Dec 2025
Updated 30 Dec 2025
*/
import SpriteKit
import SwiftUI
// MARK: View
struct SKDeterminismView: View {
let scene = SKDeterminismScene()
var body: some View {
ZStack {
SpriteView(
scene: scene,
preferredFramesPerSecond: 120,
debugOptions: [.showsFPS, .showsNodeCount]
)
.ignoresSafeArea()
VStack {
Spacer()
HStack {
Button("Bouncing Balls") {
scene.dropBouncingBalls()
}
Button("Sequence of Balls") {
scene.playSequenceOfBalls()
}
}
.buttonStyle(.borderedProminent)
}
}
.background(.black)
}
}
#Preview {
SKDeterminismView()
}
// MARK: Scene
class SKDeterminismScene: SKScene {
let ballname: String = "ball"
// MARK: didMove
override func didMove(to view: SKView) {
view.contentMode = .center
size = view.bounds.size
scaleMode = .resizeFill
backgroundColor = .darkGray
anchorPoint = CGPoint(x: 0.5, y: 0.5)
createGround()
}
// MARK: Cleanup
override func willMove(from view: SKView) {
removeAllActions()
removeAllChildren()
}
private func reset() {
removeAllActions()
enumerateChildNodes(withName: ballname, using: { ball, _ in
/// Remove the line below to keep the created balls
/// Collision count will increase, and impredictable behavior will likely happen
ball.removeFromParent()
})
}
// MARK: API
/// This sequence seems remarkably stable run after run
func playSequenceOfBalls() {
reset()
let waitDuration: TimeInterval = 0.3
let sequence1 = SKAction.sequence([
.wait(forDuration: waitDuration),
.run {
self.createCircle()
},
])
sequence1.timingMode = .linear
run(SKAction.repeat(sequence1, count: 10))
}
/// This sequence doesn't yield the same outcome every run
func dropBouncingBalls() {
reset()
let sequence2 = SKAction.sequence([
.wait(forDuration: 0.1),
.run { [weak self] in
self?.createBouncingBalls()
}
])
sequence2.timingMode = .linear
run(SKAction.repeat(sequence2, count: 1))
}
// MARK: Node Creation
private func createCircle() {
let circle = SKShapeNode(circleOfRadius: 18)
circle.name = ballname
circle.fillColor = .systemOrange
circle.lineWidth = 0
circle.physicsBody = SKPhysicsBody(circleOfRadius: 18)
circle.physicsBody?.restitution = 0.4
circle.physicsBody?.linearDamping = 0
circle.position = CGPoint(x: -10, y: 200)
addChild(circle)
}
private func createBouncingBalls() {
let circleCount = 5
let spacing: CGFloat = 60
let totalWidth = CGFloat(circleCount - 1) * spacing
let startX = -totalWidth / 2
for i in 0..<circleCount {
let circle = SKShapeNode(circleOfRadius: 18)
circle.name = ballname
circle.fillColor = .systemOrange
circle.lineWidth = 0
circle.physicsBody = SKPhysicsBody(circleOfRadius: 18)
circle.physicsBody?.restitution = 1
//circle.physicsBody?.linearDamping = 0 /// Toggle this line
let x = startX + CGFloat(i) * spacing
circle.position = CGPoint(x: x, y: 150)
addChild(circle)
}
}
private func createGround() {
let groundWidth: CGFloat = 300
let ground = SKSpriteNode(color: .gray, size: CGSize(width: groundWidth, height: 10))
ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
ground.physicsBody?.isDynamic = false
ground.position = CGPoint(x: 0, y: -300)
addChild(ground)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment