0
\$\begingroup\$

We're using a game to control some electronics, sending commands on each Update.

We've tried setting the target framerate to 60:

QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 60;

However, when running, looking at the stats, the FPS sometimes spikes way above 60. I fear that once I build the game, it will run even faster and send commands faster than the controller receiving the commands can handle.

If possible, I would like to prevent the FPS from going above a certain value as reliably as possible (currently, with targetFrameRate at 60, it sometimes spikes to over 100, which hasn't been a problem yet).

I would prefer not to move the code that sends the messages to a corountine with WaitForSecondsRealtime for fear of possible problems that might create...

\$\endgroup\$
6
  • \$\begingroup\$ Presumably you tried vSyncCount values other than zero? \$\endgroup\$
    – DMGregory
    Commented Feb 17, 2022 at 12:06
  • \$\begingroup\$ @DMGregory According to the documentation, if vSyncCount is not 0, targetFramerate is ignored: docs.unity3d.com/ScriptReference/… "If this setting is set to a value other than 'Don't Sync' (0), the value of Application.targetFrameRate will be ignored." \$\endgroup\$ Commented Feb 17, 2022 at 12:21
  • \$\begingroup\$ Right, because vSyncCount itself limits the framerate to a fraction of the vertical sync rate of the display, and limiting is what you want here. \$\endgroup\$
    – DMGregory
    Commented Feb 17, 2022 at 12:23
  • \$\begingroup\$ @DMGregory well, I tried now, it doesn't seem to do any better, at least according to the stats window... kinda feels like it doesn't have much of an effect \$\endgroup\$ Commented Feb 17, 2022 at 12:34
  • \$\begingroup\$ That's odd. You should be seeing the framerate limited to 60, 30, even 15 fps as you increase that number, with the extra time taken up by WaitForTargetFPS. Is it possible this code isn't getting called? Have you tried configuring this in the project settings window instead, rather than via script? \$\endgroup\$
    – DMGregory
    Commented Feb 17, 2022 at 12:42

3 Answers 3

1
\$\begingroup\$

When you want to limit the rate in which your electronics receive messages, then instead of trying to solve this indirectly by limiting the framerate, I would recommend a more direct approach.

I assume that your code currently looks something like this:

class ElectronicsController: MonoBehaviour {

     void Update() {
          SendUpdateToDevice();
     }
}

I would solve this problem by adding two variables to the class: The desired time between updates and the last time the device was updated. Then I would add a check to Update to see how much time elapsed since the last update and if it is less than the current time plus the update interval. I would only do an update to the device when that's the case, and then also update the time of the last update:

class ElectronicsController: MonoBehaviour {

     public double updateInterval;
     private double lastUpdate;

     void Update() {
          if (lastUpdate < Time.realtimeSinceStartupAsDouble + updateInterval) {
              SendUpdateToDevice();
              lastUpdate = Time.realtimeSinceStartupAsDouble;
          }
     }
}

Note that this solution will result in less updates being sent than the update interval would imply, because each update where the time since the last update does not precisely matches the update interval will result in a couple milliseconds being lost. So it can be more understood as a guaranteed minimum time between updates rather than a consistent update interval.

I know nothing about your particular use-case, so I don't know how important it is for you to have a consistent update rate for your electronics. But if you need more consistency, then I would recommend this slightly more complicated code instead.

class ElectronicsController: MonoBehaviour {

     public double updateInterval;
     private double lastUpdate;

     void Start() {
         lastUpdate = Time.realtimeSinceStartupAsDouble
     }

     void Update() {

          bool needUpdate = false;

          while (lastUpdate < Time.realtimeSinceStartupAsDouble + updateInterval) {
              lastUpdate += updateInterval;
              needUpdate = true;
          }
          if (needUpdate) {
              SendUpdateToDevice();
          }
     }
}

You might notice that it does not set the last update to a new value but adds the interval to it instead. This results in more consistent time-keeping because excess time carries over to the next interval. The code will only skip updates if the game gets too slow to provide the desired update interval (this is the purpose of that while loop).

But also note that it might happen occasionally with this code that there are two updates which are in very short succession. This would happen if the new lastUpdate time is very close to the next update. You did not tell us if your electronics have some kind of command buffer which might overflow if it receives too many messages over time or if two fast messages are already a problem.

\$\endgroup\$
0
0
\$\begingroup\$

Why not use Unity's FixedUpdate function instead of update? This is based on physics updates per second which is found under settings. Set it to the desired "fps" and move your update logic into fixed update.

void FixedUpdate() { //your code here }

written on phone, will clean up later

\$\endgroup\$
5
  • \$\begingroup\$ Note that FixedUpdate can be called multiple times back-to-back in a single frame, without delays in between, which could trigger the exact problem OP is concerned about: getting several commands too close together for the controller to handle them. \$\endgroup\$
    – DMGregory
    Commented Feb 18, 2022 at 14:42
  • \$\begingroup\$ Except the fixed update has a fixed time step so even if it fires back to back due to low fps, it is still spaced out chronologically to allow the electronics controller time to process, and the sending of commands should also be in fixed update. The op is complaining of high fps, not low fps. \$\endgroup\$ Commented Feb 19, 2022 at 13:27
  • \$\begingroup\$ No, that's a misunderstanding of how FixedUpdate is implemented. Its calls are not spaced out at fixed intervals in wall clock time. They're spaced out equally in game time. You can verify this by creating a System.Diagnostics.Stopwatch and logging the high-precision real time intervals when FixedUpdate gets invoked. Each fixed update increments the game time variables (Time.time and friends) by a fixed amount, but the calls can occur in a back-to-back cluster in wall clock time, followed by a longer delay, then another cluster. See my previous answers on this topic for more details. \$\endgroup\$
    – DMGregory
    Commented Feb 19, 2022 at 13:31
  • 1
    \$\begingroup\$ I mention this because there was previously a comment recommending that OP try FixedUpdate, and they rejected the suggestion based on the point above about the possibility of back-to-back calls (even if that's not the norm at high frame rates). The suggestion was subsequently deleted by its author, which is why I'm relaying the point to you here. \$\endgroup\$
    – DMGregory
    Commented Feb 19, 2022 at 13:36
  • \$\begingroup\$ Ah, I see. Thanks for the lesson. Unity docs does not clearly state this info. \$\endgroup\$ Commented Feb 19, 2022 at 13:41
0
\$\begingroup\$

I usually do something like this:

const float interval = 1f/60f;
private float x = interval;

void Update()
{
    x -= Time.deltaTime; // or use real delta time
    if (x <= 0)
    {
        // do something
        // reset x here or use another if statement outside of this one
        x += interval;
    }
}

so if you want to call a method 60 times per second then set x to 1f / 60f and as I know it works pretty well if the fps is more than or equal to 60

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .