Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Units, framerate, velocity and _baseDelta #1323

Open
mqnc opened this issue Dec 8, 2024 · 0 comments
Open

Units, framerate, velocity and _baseDelta #1323

mqnc opened this issue Dec 8, 2024 · 0 comments

Comments

@mqnc
Copy link

mqnc commented Dec 8, 2024

I want to migrate my game from p2.js to another physics engine (since p2 misses collisions and is no longer maintained) and matter-js seems to be the most popular. For trying things out, I wrote a bouncing ball simulation:

<!DOCTYPE html>
<html>

<head>
	<title>Matter.js Bouncing Ball</title>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.js"></script>
</head>

<body>
	<script>
		const { Engine, Render, Runner, World, Body, Bodies, Events } = Matter

		let g = 10 // m/s²
		let x = 400
		let hDrop = 180 // m
		let rBall = 10 // m
		let hBeam = 20 // m
		let hZero = 300

		const engine = Engine.create()
		engine.world.gravity.y = g

		const render = Render.create({ element: document.body, engine: engine })

		Render.run(render)

		const ball = Bodies.circle(x, hZero - hDrop - rBall, rBall, {
			restitution: 1, friction: 0, frictionAir: 0, mass: 1, inertia: 1
		})

		const ground = Bodies.rectangle(x, hZero + hBeam / 2, 200, hBeam, { isStatic: true })

		World.add(engine.world, [ball, ground])

		const dt = 1 / 120 // s

		let simulationTime = 0

		setInterval(() => {
			Engine.update(engine, dt * 1000)
			simulationTime += dt
		}, dt * 1000)

		let lastBounceSimulationTime = null
		Events.on(engine, "collisionStart", (event) => {
			console.log(`t=${simulationTime}`)
			console.log(`v=${Matter.Body.getSpeed(ball)}`)
		})

	</script>
</body>

</html>

I am working with SI units for portability across engines. As far as I understand, this should work well with matter-js.

The ball falls from a height of 180 m and with gravity set to 10 m/s², it should take exactly 6 s until it hits the ground and the impact velocity should be exactly 60 m/s.

However, it reports t=0.183 v=31.94 and the ball does not bounce at all.

After a lot of experimenting and digging in documentation and code, I found that:

  • There is a gravity scale which defaults to 0.001 (why?)
  • The delta parameter in Engine.update is not in ms, it is in the same unit that I use everywhere else.
  • There are the internal parameters Matter.Body._baseDelta and Matter.Common._baseDelta which are set to 1000 / 60 and influence velocity set/get and frictionAir and probably also other things.

Actually I first reported body.velocity until I figured that it's not really the body's velocity but the position delta during the last step. It also says "Equivalent to the magnitude of body.angularVelocity (always positive)." in the documentation which I think is a copy-paste mistake.

And I also found this Body._inertiaScale = 4; which will probably cause a hickup at some point.

After accounting for all of this, I got it working with this code:

<!DOCTYPE html>
<html>

<head>
	<title>Matter.js Bouncing Ball</title>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.20.0/matter.js"></script>
</head>

<body>
	<script>
		const { Engine, Render, Runner, World, Body, Bodies, Events } = Matter

		let g = 10 // m/s²
		let x = 400
		let hDrop = 180 // m
		let rBall = 10 // m
		let hBeam = 20 // m
		let hZero = 300

		const engine = Engine.create()
		engine.world.gravity.y = g
		engine.world.gravity.scale = 1

		const render = Render.create({ element: document.body, engine: engine })

		Render.run(render)

		const ball = Bodies.circle(x, hZero - hDrop - rBall, rBall, {
			restitution: 1, friction: 0, frictionAir: 0, mass: 1, inertia: 1
		})

		const ground = Bodies.rectangle(x, hZero + hBeam / 2, 200, hBeam, { isStatic: true })

		World.add(engine.world, [ball, ground])

		const dt = 1 / 120 // s
		Matter.Body._baseDelta = 1
		Matter.Common._baseDelta = 1

		let simulationTime = 0

		setInterval(() => {
			Engine.update(engine, dt)
			simulationTime += dt
		}, dt * 1000)

		let lastBounceSimulationTime = null
		Events.on(engine, "collisionStart", (event) => {
			console.log(`t=${simulationTime}`)
			console.log(`v=${Matter.Body.getSpeed(ball)}`)
		})

	</script>
</body>

</html>

Why are there so many magic constants sprinkled everywhere? I have two guesses:

  1. I messed something up and if I just use it right, it will all be consistent and nice.
  2. Matter-js started as a hobby project, using pixels as units and 60 fps and grew from there while staying backwards compatible.

In case (2) is right, would it be an option to introduce some sort of useSI flag that sets all magic constants to 1?

(Also, why is there no bounce in the first version?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant