craftworkgames / MonoGame.Extended

Extensions to make MonoGame more awesome
http://www.monogameextended.net/
Other
1.44k stars 324 forks source link

Particle System: Strange behaviour when using dynamic timestep. #373

Closed michaelpittino closed 7 years ago

michaelpittino commented 7 years ago

Hello. I just found MonoGame.Extended and tried to use the particle system. I usually disable fixed timestep in my MonoGame projects because I don't like the 60 fps limitation.

Because I don't really know how to discribe the problem I made a video: https://webmshare.com/q7bnb The first execution shows the strange behaviour. The whole particle "ring" just fades upwards and then starts again. The second execution, with fixed timestep enabled, works again.

The full source is: https://gist.github.com/michaelpittino/448bca2cd62b27af3b639dce45364a69

Thanks for any help!

craftworkgames commented 7 years ago

Okay so I had a look into this and managed to reproduce the issue. The problem is this line:

this.particleEffect.Trigger(new Vector2(400, 240));

What's happening is that you're calling Trigger for every frame. With IsFixedTimeStep turned on that means you call Trigger 60 times per seconds but with it turned off the method is likely being called many thousands of times per second.

Each time Trigger is called it tries to release as many particles as specified in the Quantity parameter of the ParticleReleaseParameters but there's a limit to how many particles the particle buffer can hold (specified as the capacity of the ParticleEmitter) so when Trigger is called too many times it runs out of particles available in the buffer and has to wait until some of the existing particles die and get returned to the buffer before it can release new ones.

Anyway, as you probably gathered by now, the key to solving this issue is to limit how many times you call Trigger. To test this I created a member variable like this:

 private float _nextTrigger = 1f / 60f;

And then added this to the Update method:

        _nextTrigger -= deltaTime;

        if (_nextTrigger <= 0)
        {
            _particleEffect.Trigger(new Vector2(400, 240));
            _nextTrigger = 1f / 60f;
        }

What it does is wait a 60th of a second between each call to Trigger. This effectively makes the particles trigger at the desired 60 frames per second without changing IsFixedTimeStep.

That said, I strongly urge to to reconsider using fixed time steps. They are there for a reason and tend to make your life a lot easier. Particularly when dealing with physics systems but also any kind of animations, variable time steps can get you into a world of pain.

michaelpittino commented 7 years ago

Hi, many thanks for the answer. The point of disabling fixed timestep and vsync is to prevent the input lag you get when you draw (and update) 60 times per second. The mouse cursor feels very unnatural and unresponsive. Do you have any solution for using fixed timestep and get a near hardware-cursor feeling?

craftworkgames commented 7 years ago

My only suggestion would be to find a time step that feels right to you. Just keep in mind that any code that expects the time step to be fixed at 60 fps will need to be adjusted accordingly.

Also, read this to get a better understand if how time steps work in MonoGame.

http://rbwhitaker.wikidot.com/time-steps

Obviously if you still want to use variable time steps you can, you just need to understand how that impacts the way you write your code. It might be worth it if your game needs low input lag I guess.