简体   繁体   中英

Animation frame rate vs game fps

I'm a beginner programmer just starting out with MonoGame. I was implementing some code for running an animation and came to a problem I don't know how to solve. This is the code I used in my AnimationManager class:

public void Update(GameTime gameTime)
{
    timeElapsed += (float)gameTime.ElapsedGameTime.TotalSeconds;

    if (timeElapsed > timeToUpdate)
    {
        timeElapsed -= timeToUpdate;
        //Tried to just reset timeElapsed to zero, still doesn't work
        //timeElapsed = 0;
        if (frameIndex < rectangles.Count -1)
        {
            frameIndex++;
        }
        else if (isLooping)
        {
            frameIndex = 0;
        }
    }
}

The problem is I animate at 24 frames per second and when the game runs at 60 or 30 fps and on each update code checks is it time to draw a new frame of animation, you get some remainder of frames, because you tried to draw 24 images per second, evenly, with 30 even game frames per second. So there's that remainder of 6 frames. The result of this is that approx 6 frames of animation gets drawn twice, some are skipped a whole animation gets about 25% longer. Is there a way to fix this? Can I maybe move this code to Draw call and then cap just the draw calls at 48 fps for 60 fps game, so each animation frame will be drawn at every second draw call. I don't know how would I go about doing this if it's a reasonable solution at all?

And how will VSync affect all of this at the end. Will it mess up animation frames, even if this problem is solved.

EDIT: forgot to say it's a 2d game, hand drawn animation.

Generally when you're doing this kind of stuff you don't need to worry about VSync or the fact that your game runs at 60 or 30 fps. That's all pretty much irrelevant.

The thing that matters is that you know the desired frames per second of your animation and how long it takes for a single game frame.

From that you can calculate the length of time in a single animation frame . For example, you'd end up with something like this:

var animationFrameTime = 1f / 24f;   // 24 frames per second

So far so good.

The next thing you need to do is to start accumulating time somewhere. There's a few different ways to think about this. I like to think about it in terms of how much time is left until the next frame .

var gameFrameTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
timeUntilNextFrame -= gameFrameTime;

Then you need to increment the frameIndex when you've run out of frame time.

if(timeUntilNextFrame <= 0)
{
    frameIndex++;
    timeUntilNextFrame += animationFrameTime;
}

Notice the important bit here. I've added the animationFrameTime to the timeUntilNextFrame . This way, if there's any left over time from the previous frame, it'll get added to the next frame, keeping the animation smooth.

There's really not much more to it than that. I've left out the rectangles.Count and isLooping bits, but it should be easy enough to add those back in. You might also want to initialize timeUntilNextFrame to animationFrameTime for the first frame.

Just a word of warning with this code, if the game is running slowly for some reason (or perhaps it's paused maybe) the gameFrameTime might end up having a very large value causing some strange behavior. I'm not entirely certain if that can ever happen in MonoGame / XNA but it's worth considering at least.

It turned out this is a well known problem. Basicaly its mathematicaly imposible to jam 24 animation fps in 30 game fps because they are not independent from each other. Interpolation is the only way to fix this problem. It's the same thing that's being done whenever you watch 24 fps movie on a 60 hz monitor.

https://en.wikipedia.org/wiki/Three-two_pull_down

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM