2
\$\begingroup\$

In my game, I have particles that should spawn at random intervals but with an average rate of x per second.

However, the frame-rate varies from around 60 fps all the way to around 3000 fps. I would like to maintain a variable frame-rate to ensure the game looks as smooth as possible, but this means I cannot simply create particles with probability x each frame:

if (random.Next(100) > x)
{
    // Create particle... 
} 

How can I maintain a constant "real time" drop-rate when the frame rate fluctuates?

Note that I have a variable dt which is the number of seconds since the last frame.

\$\endgroup\$
3
  • \$\begingroup\$ 3000? What kind of hardware are you targeting? Can the human eye see the difference? (This is not relevant to your current issue; I'm just wondering if it's really worth it...) \$\endgroup\$
    – Vaillancourt
    Jul 7, 2015 at 21:29
  • \$\begingroup\$ Well 3000 is unneccessary, but I want to support 144hz monitors properly. I don't want to set the fixed rate that high though, or low-end PCs will struggle to keep up. \$\endgroup\$
    – sdgfsdh
    Jul 7, 2015 at 21:30
  • \$\begingroup\$ Kk, I can understand the variable dt, and that's enough for the question :) \$\endgroup\$
    – Vaillancourt
    Jul 7, 2015 at 21:46

4 Answers 4

1
\$\begingroup\$

While this is not a perfect solution, you can say something like:

if (random.NextDouble() / x < dt)
{
    // Create particle... 
}

How this works:

For now, let's assume that you want an average of 1/second (so / x does nothing). If it's been one second since the last frame, you want an average of one particle to appear. random.NextDouble() always generates a number such that 0 <= n < 1, so it will always be less than dt in this case, causing a particle to always appear. If it's been 1/60th of a second since the last frame, dt will be 1/60. random.NextDouble() has a 1/60 chance to be less than 1/60, so on average there will be 1 particle every 60 frames, or 1/second.

Now, let's say that you want 2/second. If it's been 1/60th of a second since the last frame, dt will be 1/60. random.NextDouble() / x (where x == 2) has a 1/30 chance to be less than 1/60, so on average there will be 2 particles every 60 frames, or 2/second.

\$\endgroup\$
2
  • \$\begingroup\$ This solution only works when x * dt is small. Suppose it were larger than 1; there should be a chance for 2 particles to spawn in one update. \$\endgroup\$
    – sdgfsdh
    Jul 9, 2015 at 8:09
  • \$\begingroup\$ @sdgfsdh Yup, that's why I said it's not a perfect solution. \$\endgroup\$ Jul 9, 2015 at 17:09
0
\$\begingroup\$

Would this idea of an accumulator of probability work?

float acc -> particle count accumulator

float x   -> particles per second
float dt  -> number of seconds since last frame

update( dt )
{
  // increase the chance of a particle spawned this update
  acc += x * dt;

  // This case where you run at a very low frame rate, or that you want 
  // A LOT of particles per second
  while ( acc > 1.0 )
  {
    // [spawn a particle]
    acc -= 1.0; // done with this particle
  }

  // now this is to take care of a "random" but still constant over time.
  if ( acc > 0.0 )
  {
    // as the accumulator increases in value, the more chances there is for a 
    // particle to spawn
    float random_value = rnd.next( 0.0, 1.0 ); // random float between 0 and 1
    if ( random_value < acc )
    {
      // [spawn a particle]
      acc -= 1.0;
      // now acc is negative, there will be no chances of spawning until it gets 
      // back above 0.0 which will happen over time. 
    }
  }
}

Keep in mind: totally not tested, maybe very flawed... dunno! I'm just suggesting stuff from the top of my head.

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

One simple way would be to adjust the chance of the drop by dt. So instead of accumulators etc. you could do a simple multiplication: drop if dt * random <= threshold.

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

Add or subtract a random number from x, and use timers to spawn particles once per second

if(timer >= 1)
{
  int y=//random number
  int xnew = x + y;
  //clear timer
  for(int i; i <= xnew; i++)
  {
    //span particle
  }
}

This will cause the particles to spawn very close together, and then stop for about a second. To space them out more (but not randomly spaced) you can make an array consisting of [1/x, (1+x)/x, (2+x)/x,...(x-1)/x] and

if(timer in array)
{
//spawn particles
}
\$\endgroup\$

You must log in to answer this question.

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