Fixed Update Loop

How can i make a fixed update loop like 1000 / 60 exactly.

Not like delta time which can be different each time, but a frame independent 16 ms fixed update each and every time ?

Hey!

I think you could do it by comparing the current time in millis from Date.now() in every frame and perform the update when delta time >= update period. You get the point…

Here’s how you would normally do it:

    let startTime = performance.now()	
    //const maxSubsteps = 3 
    let simulationSpeedFactor = 1//Not required, but a cool way to speed up/down a simulation in order to dynamically adjust an input buffer in a networked simulation, as used by Overwatch
	scene.registerBeforeRender(()=>{
        //let substeps = 0
		const now = performance.now()
		const delta = (now-startTime)/1000
		startTime = now
		accumulator += delta// <= 0.4 ? delta : 0.4
		while (accumulator >= 0.016*simulationSpeedFactor// && substeps <= maxSubsteps) {
            //substeps++
			accumulator -= 0.016
			world.update(0.016)
		}
	})

You can do it in your render loop as well.
You need to either clamp your delta or your accumulator, or use substeps in order to prevent the spiral of death condition. This of course depends on your game, but if you, due to lag or something else, calculates a delta of let’s say 2000-3000 ms, then there’s a chance your physics or whatever won’t be capable of catching up.

Be aware that you’ll need to interpolate transforms used in a fixed loop, as otherwise they’ll appear laggy/choppy.

2 Likes

Hi @Raggar!
I never used performance before just heard about it and was too busy to look it up so I just proposed an idea of measuring the elapsed time between two frames. Your solution is perfect! I did my homework now and I’ve read a few articles. Datetime.now is too slow for this and the only way to do it precisely is using performance.now()
Thanks a lot for your solution!

:vulcan_salute:

1 Like

Yo @Raggar … I dont quite understand how i can use your solution yet. I will keep studying it.

Actually, smooth network movement is what i am really working on. I am trying to interpolate/extrapolate network entity movement in the scene but it ALWAYS choppy/jittery.

I have tried many different Unity style mechanics for LERPING/MOVING TOWARDS but i still get choppy movement. Most of the solutions from unity for smooth multiplayer movement is ran in the FIXEDUPDATE loop. so that is why asked how to make a fixed update loop.

Would you (or anyone else) be interested in helping me with smooth network movement for my Babylon Toolkit Pro Tools ? I can pay you for time and effort. (Plus give you a Free Lifetime Pro Tools License for use with the Babylon Toolkit Pro Features… Which networking is a part of)

Please let me know :slight_smile:

hmmm… I tried this code verbatim, and it does NOT seem to run at world.update(0.016) or in my case this.tester function at FIXED interval :frowning:

            let startTime = performance.now()	
            let accumulator = 0;
            let simulationSpeedFactor = 1
            this.scene.registerBeforeRender(()=>{
                const now = performance.now()
                const delta = (now-startTime)/1000
                startTime = now
                accumulator += delta
                while (accumulator >= 0.016*simulationSpeedFactor) {
                    //substeps++
                    accumulator -= 0.016
                    this.tester(0.016)
                }
            });

Unfortunately I don’t really have that kind of time on my hands for the time being.
But there are loads of examples both as tutorials and finished code on Github.
My own implementation is more or less based on this:

But I don’t use the synced clocks for interpolation, I simply use the current time, the latency from server to client as well as the desired offset in the past - to calculate how far back in time I want to interpolate to/from.

A fixed update loop isn’t meant to run in exact intervals. It is meant as a way of running your simulation in fixed time frames, as opposed to variable time steps. If two physics bodies are colliding, there’s potentially a huge difference in response between resolving a collision with 3x16 ms fixed steps, and doing the same with a variable time step of 48 ms. This can result in anything from spring explosion, to objects clipping through walls.
Fixed time steps are usually used to make physics and animations predictable and deterministic.
One update loop might do 2 x 16 ms, while the next might only have 7 ms worth of accumulated time, and will be skipped. This also leads to the lag you’ll encounter with a fixed loop.
Here’s an example of the loop in action, showing how choppy the box appears:

And here is the same example, but this time interpolating based on previous data:

In this example I interpolate positions and orientations, but I think a better alternative would be to simple interpolate the entire matrices.

Would it possible to see this implementation. This is the kind of stuff i was trying before. Converting to babylon of course… I buffer the updates and lerp to ref and everything… but still comes out choppy. Not animations… just the raw movement of the remote entities are just fucked.

So i am obviously not doing something right.

I been trying to port stuff like this for the Snapshot buffer implementations

using UnityEngine;
using System.Collections;

public class NetworkInterpolatedTransform : MonoBehaviour 
{
    public double interpolationBackTime = 0.1; 
	
	internal struct State
	{
		internal double timestamp;
		internal Vector3 pos;
		internal Quaternion rot;
	}

	// We store twenty states with "playback" information
	State[] m_BufferedState = new State[20];
	// Keep track of what slots are used
	int m_TimestampCount;
	
	void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
	{
		// Always send transform (depending on reliability of the network view)
		if (stream.isWriting)
		{
			Vector3 pos = transform.position;
			Quaternion rot = transform.rotation;
			stream.Serialize(ref pos);
			stream.Serialize(ref rot);
		}
		// When receiving, buffer the information
		else
		{
			// Receive latest state information
			Vector3 pos = Vector3.zero;
			Quaternion rot = Quaternion.identity;
			stream.Serialize(ref pos);
			stream.Serialize(ref rot);
			
			// Shift buffer contents, oldest data erased, 18 becomes 19, ... , 0 becomes 1
			for (int i=m_BufferedState.Length-1;i>=1;i--)
			{
				m_BufferedState[i] = m_BufferedState[i-1];
			}
			
			// Save currect received state as 0 in the buffer, safe to overwrite after shifting
			State state;
			state.timestamp = info.timestamp;
			state.pos = pos;
			state.rot = rot;
			m_BufferedState[0] = state;
			
			// Increment state count but never exceed buffer size
			m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);

			// Check integrity, lowest numbered state in the buffer is newest and so on
			for (int i=0;i<m_TimestampCount-1;i++)
			{
				if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
					Debug.Log("State inconsistent");
			}
			
			//Debug.Log("stamp: " + info.timestamp + "my time: " + Network.time + "delta: " + (Network.time - info.timestamp));
		}
	}
	
	// This only runs where the component is enabled, which is only on remote peers (server/clients)
	void Update () {
		double currentTime = Network.time;
		double interpolationTime = currentTime - interpolationBackTime;
		// We have a window of interpolationBackTime where we basically play 
		// By having interpolationBackTime the average ping, you will usually use interpolation.
		// And only if no more data arrives we will use extrapolation
		
		// Use interpolation
		// Check if latest state exceeds interpolation time, if this is the case then
		// it is too old and extrapolation should be used
		if (m_BufferedState[0].timestamp > interpolationTime)
		{
			for (int i=0;i<m_TimestampCount;i++)
			{
				// Find the state which matches the interpolation time (time+0.1) or use last state
				if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount-1)
				{
					// The state one slot newer (<100ms) than the best playback state
					State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
					// The best playback state (closest to 100 ms old (default time))
					State lhs = m_BufferedState[i];
					
					// Use the time between the two slots to determine if interpolation is necessary
					double length = rhs.timestamp - lhs.timestamp;
					float t = 0.0F;
					// As the time difference gets closer to 100 ms t gets closer to 1 in 
					// which case rhs is only used
					if (length > 0.0001)
						t = (float)((interpolationTime - lhs.timestamp) / length);
					
					// if t=0 => lhs is used directly
					transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
					transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
					return;
				}
			}
		}
		// Use extrapolation. Here we do something really simple and just repeat the last
		// received state. You can do clever stuff with predicting what should happen.
		else
		{
			State latest = m_BufferedState[0];
			
			transform.localPosition = latest.pos;
			transform.localRotation = latest.rot;
		}
	}
}

Hey!
I was thinking about how to mimic the Unity’s FixedUpdate method. I don’t think there is a solution using the main thread so you have to use a webworker which will loop forever on maximum speed and you can send back messages to the main thread at fixed intervals.

index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon Template</title>

    <style>
      html,
      body {
        overflow: hidden;
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }

      #renderCanvas {
        width: 100%;
        height: 100%;
        touch-action: none;
      }
    </style>

    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
  </head>

  <body>
    <canvas id="renderCanvas" touch-action="none"></canvas>
    <!-- touch-action="none" for best results from PEP -->

    <script>
      const canvas = document.getElementById("renderCanvas");
      const engine = new BABYLON.Engine(canvas, true);

      const createScene = function () {
        const scene = new BABYLON.Scene(engine);

        BABYLON.MeshBuilder.CreateBox("box", {});

        const camera = new BABYLON.ArcRotateCamera(
          "camera",
          -Math.PI / 2,
          Math.PI / 2.5,
          15,
          new BABYLON.Vector3(0, 0, 0)
        );
        camera.attachControl(canvas, true);
        const light = new BABYLON.HemisphericLight(
          "light",
          new BABYLON.Vector3(1, 1, 0)
        );

        return scene;
      };

      const scene = createScene(); //Call the createScene function
      let frames = 0;
      let steps = 0;
      engine.runRenderLoop(function () {
        frames++;
        scene.render();
      });

      const worker = new Worker("worker.js");
      worker.addEventListener("message", (e) => {
        steps++;
        console.log("fixedUpdate: steps", steps, "frames", frames);
      });
      worker.postMessage({ deltaTime: 1 / 60 });

      window.addEventListener("resize", function () {
        engine.resize();
      });
    </script>
  </body>
</html>

worker.js

self.onmessage = function (evt) {
  const deltaTime = evt.data.deltaTime * 1000;
  let lastNow = performance.now();

  while (true) {
    const now = self.performance.now();
    if (now > lastNow + deltaTime) {
      lastNow = now;
      self.postMessage("fixedUpdate");
    }
  }
};

I’m running on 100 FPS so:
image