Archived

This topic is now archived and is closed to further replies.

Achieving smooth movement at varying frame rates?

This topic is 5577 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I''m having trouble getting my tile engine to move smoothly at any frame rate. Currently it runs at 70fps on my machine and can move characters/scroll ultra smooth. At first I thought I would just lock the frame rate at ~30 fps since after all its just a little 2D game. But the problem with that was I would have to increase the amount of pixels that are needed to move everything in order to counteract the snails pace they moved at cutting the frame rate like that.. and that made it look terribly jumpy. So then I read the article on time based movement and checked the forums for any more advice and tried to impliment it but none of it seemed to work. I can''t even get the articles suggestions to work on my code. So that brought me back to square one. Now I have a fair idea of what I need, but am unsure how to go about doing it. My guess is that I need to find the number of frames that need to pass in order to move 1 pixel. Then in my input processing keep adding that value to a number and when that number is greater then 1 move by 1 pixel. Is this the right solution? Can someone show some code on how that could be done?

Share this post


Link to post
Share on other sites
Okay, this is mostly for 2d...

Hmmm, I don't think you want to go attaching movement updates to the input code, as it depends (as far as I know) on how fast a particular PC can read data from the keyboard. It may be fairly standard but you may get fluctuations from PC to PC, or even on the same PC.

I have done this, and the first thing you need is a class that acts as a timer, you instantiate this object and then call some update function each frame of your game. This timer function then tracks the last time you called it (i.e the last frame) and the polls for the current time, then it finds the difference between the two.

You then have to find the frames per second from this value. Now, this is where I have to show you using a particular method, I use QueryPerformanceCounter/QueryPerformanceFrequency. You use ...Counter to get the amount of "Ticks Per Second" or the amount of times the internal timer in that particular PC ticks over in one second.

Now we have the amount of ticks per second and the amount of time that has past since the last frame. Yep, now we can work out how many frames per second we are getting, something like

m_fFramesPerSec = ((float)m_qwTicksPerSec / (float)m_llElapsedTime);

That takes the amount of ticks that frame transistion and finds the amount of FPS using.

Now you can find the seconds per frame by using

m_dSecsPerFrame = (1.0f / m_dFramesPerSec);

Pretty coo. Anyway, now that you can find this, you need some kind of accessor function for the timer class to get the seconds per frame. This is where it gets interesting.

If you want to move and object at a certain speed in your game, I suggest you give it a velocity in the direction you want to move it, say 60 pixels per second. You also need a variable to "count" the seconds per frame, something like m_fMoveTimer, and each frame which calls the function in which this is updated you do something like this to update the position of your character...

m_fMoveTimer += m_pMyTimer->GetSecondPerFrame()
m_nMoveThisFrame = m_fObjPosX + ((float)m_nMoveVel * m_fMoveTimer)

Note: This is if the object is moving right when the topleft of the screen is 0,0

m_nMoveVel == The velocity in pixels
m_fObjPosX == Current object position

As you may have noticed, m_fObjPosX is a float, I use floats to represent position even in 2D as it results in really smooth movement (depending of framerate of course )

If you are going to add things like acceleration then it's alittle bit more tricky, but not by much it's the same principals.

I may have missed something, but that's how I do it.

Good luck.



[edited by - Redline on September 8, 2002 10:35:52 AM]

Share this post


Link to post
Share on other sites
Yeah in my input code its just setting flags according to what key is down or up, something like bMovingleft = true for now. Then the update is done later, but it was just something I threw together to test out the tile engine and i''m gonna have to rewrite it later.

Redline - I tried what you said and its similar to what I was trying with timeGetTime() and GetTickCount(). I''m still having problems though and from what I can tell its from what those particular methods return. PerformanceCounter functions return a LARGE_INTEGER type and the other two return DWORDS. So when I tried casting them to floats, I checked what values I was actually getting and they kept fluctuating between positive and negative, apparently because they were overflowing. So then I tried doubles and still getting weird results when trying to calculate certain values

For instance when I did
m_fFramesPerSec = ((float)m_qwTicksPerSec / (float)m_llElapsedTime);
and checked the value I would get 0, but when I just did like
wsprintf( str, "FPS: %d", int(m_qwTicksPerSec / m_llElapsedTime) );
it would work fine. All of these weird little quirks got me pulling my hair out and stuff because I can''t get working on the fun parts until I finish this (relatively) simple problem. =/

Share this post


Link to post
Share on other sites
Heh, well I got it working. *hooray* I took a much simpler approach to finding it.

in the main loop:

DWORD dwTime = timeGetTime() - dwFrameTime;
if ( dwTime > 1000 )
{
dwFrames = dwFrameCount;
dwFrameCount = 0;
dwFrameTime = timeGetTime();
//global value to scale movement to
fMoveVal = (PixelsToMovePerSecond) / (float)dwFrames;
}

dwFrameCount++;

//.. rest of the main loop


It was mainly a problem with the code for processing input and moving things. I patched it up to use this new float value too fast and overlooked a few things. Thanks for the insight!


Share this post


Link to post
Share on other sites
Try this class - the performance counter is much more accurate.

Just call JMTimer::init() at the start of your program, and JMTimer::update() at the beggining of each game loop. Then, you can use JMTimer::GetDeltaSeconds() to get the elapsed time in seconds. For movement, just multiply that value by the velocity (as distance/seconds).

In JMTimer.h -

      
#pragma once

#include <windows.h>
#include <time.h>

class JMTimer {
protected:
static LARGE_INTEGER m_iFrequency;
static LARGE_INTEGER m_iLastQuery;
static LARGE_INTEGER m_iDelta;
JMTimer();
public:
static void init();
static void update();
static double GetDeltaSeconds();
static double GetDeltaMillis();
};


In JMTimer.cpp -

  
#include "JMTimer.h"

LARGE_INTEGER JMTimer::m_iFrequency;
LARGE_INTEGER JMTimer::m_iLastQuery;
LARGE_INTEGER JMTimer::m_iDelta;

void JMTimer::init() {
QueryPerformanceFrequency(&m_iFrequency);
update();
}

void JMTimer::update() {
LARGE_INTEGER kTempTimer;
QueryPerformanceCounter(&kTempTimer);
m_iDelta.QuadPart = kTempTimer.QuadPart - m_iLastQuery.QuadPart;
m_iLastQuery.QuadPart = kTempTimer.QuadPart;
}

double JMTimer::GetDeltaSeconds() {
return (double)(m_iDelta.QuadPart) / (double)(m_iFrequency.QuadPart);
}

double JMTimer::GetDeltaMillis() {
return (double)(m_iDelta.QuadPart) / (double)(m_iFrequency.QuadPart) * 1000;
}


Note that EVERYTHING is static. You can call any of the functions from anywhere in the code, as long as you include JMTimer.h, and get the proper elapsed time. Be careful not to call update twice in one game loop, or it will be inaccurate.

[edited by - deyja on September 9, 2002 10:43:04 AM]

[edited by - deyja on September 9, 2002 10:43:51 AM]

Share this post


Link to post
Share on other sites