dimforge / rapier.js

Official JavaScript bindings for the Rapier physics engine.
https://rapier.rs
Apache License 2.0
421 stars 58 forks source link

React physics step is twice as fast as Nodejs physics step #112

Closed Triuman closed 2 years ago

Triuman commented 2 years ago

Package: @dimforge/rapier3d-compat Version: v0.7.3

I have a client(React) and a server(Nodejs) which are running the same world configuration. But in the server I have to set the World.timestep double the amount I set in the client so that they have the same speed.

Does anyone know why this is happening?

jcyuan commented 2 years ago

what is your screen refresh Hz of your client, and server? where did you call your world.step at? in a rAF?

Triuman commented 2 years ago

I'm running both in my laptop and the screen refresh rate is 300hz. I call world.step in setTimeout as below:

  step = () => {
    this.world.step();

    setTimeout(step, 16);
  }
  step();

I called it also in setInterval before and got the same result.

jcyuan commented 2 years ago

you can do something like this:

lastTime = performance.now();

step() => {
       t = lastTime - performance.now()
       console.log(t);
}

to check if your frame interval is the same or not, because imo if the world works then it works, unless your environment provides different calling frequency, otherise a little hard to imagine why...

Triuman commented 2 years ago

You are right! It gave me 30.5 ms even though I set the interval for 16 ms. I did this before I think with Date.now() and got 16ms.

I had no idea this would happen in Nodejs. Please tell me if you have any idea. But I will do a research anyways.

Thanks a lot!

cristw commented 2 years ago

Is there a way to have a fixed time step? Like have a fixed delta time and custom loop.

Triuman commented 2 years ago

I will do something like that at the end but I need to figure out why my machine is running setInterval very unreliably.

LeXXik commented 2 years ago

@Cri5-W
You can implement a fixed timestep as described here: https://gafferongames.com/post/fix_your_timestep/

jcyuan commented 2 years ago

@Cri5-W yes, as @LeXXik suggested, you are supposed to call your world.step in the callback with your fixed timestamp value set for your world.

......

integrate(): void {
    world.timestep = 0.016;
    world.step(); 
}

world.timestep this value just tells to Rapier the frame delta, it does nothing with fixed-step-calling, so only if your world.step is called in such solution it will then work.

jcyuan commented 2 years ago

here simply show my code as an example:

private stepPhysicsWorld(delta: number): void {
        const queue = this._curCase!.eventQueue;
        const hooks = this._curCase!.hooks;

        if (!this.fixedTimeStepEnabled) {
            this._world.timestep = delta;
            this._world.step(queue, hooks);
        } else {
            this._accumulator += delta;
            let substepIndex = 0;
            this._world.timestep = FixedTimeStep;

            while (substepIndex++ < this.maxSubSteps && this._accumulator > FixedTimeStep) {
                this._world.step(queue, hooks);
                this._accumulator -= FixedTimeStep;
            }
        }
    }

and then call this method in your game engine's update callback.

Triuman commented 2 years ago

Thank you both! Now, I implemented that using setImmediate and it works pretty well.

  step = () => {
    const now = performance.now();
    if(!this.lastStepTime)
      this.lastStepTime = now;
    let dt = now - this.lastStepTime;

    while(dt > this.updateInterval) {
      this.physicsWorld?.step();
      dt -= this.updateInterval;
      this.lastStepTime = now - dt;
    }

    setImmediate(this.step);
  }
cristw commented 2 years ago

I don't understand. Why is there a timestep property in the world object?

The value of the timestep property by default has a value close to 1/60. Does that imply that the frequency should be 60 steps per second?

I'm just new to these physics concepts.

Edit:

Ok, I think I just solved my problem.

I asked in the dimforge discord server and a person told me that by changing the world timestep property to the desired deltaTime then it would be pretty much it.

My problem was that it wasn't working when I did that but I realized that I assigned the timestep to 1000/30 instead of 1/30 or 30 times per second because that's how I use it to set the interval in milliseconds in the setInterval in javascript and the timestep property in rapier has to be in seconds.

jcyuan commented 2 years ago

i think you should set the timestep property for your world otherwise your world will alwasy use the default timestep to accumulate:

this.physicsWorld?.timestep = this.updateInterval;
jcyuan commented 2 years ago

I don't understand. Why is there a timestep property in the world object?

The value of the timestep property by default has a value close to 1/60. Does that imply that the frequency should be 60 steps per second?

I'm just new to these physics concepts.

Edit:

Ok, I think I just solved my problem.

I asked in the dimforge discord server and a person told me that by changing the world timestep property to the desired deltaTime then it would be pretty much it.

My problem was that it wasn't working when I did that but I realized that I assigned the timestep to 1000/30 instead of 1/30 or 30 times per second because that's how I use it to set the interval in milliseconds in the setInterval in javascript and the timestep property in rapier has to be in seconds.

the world will use this value you specify to accumulate the internal elapsed physics time.

Triuman commented 2 years ago

i think you should set the timestep property for your world otherwise your world will alwasy use the default timestep to accumulate:

this.physicsWorld?.timestep = this.updateInterval;

I set it like below since timestep is in seconds.

this.updateInterval = 16; //ms
this.physicsWorld.timestep = this.updateInterval / 1000; //sec