Sign in to follow this  

Having big problems with time-based movement (solved)

This topic is 4729 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 have tried implementing a time-based movement system into my game, but it does not work at all. I have both tried something similar to the CalculateFrameRate stuff seen in the camera tutorials at GameTutorials, and a custom GameTimer object that queries the performance and yadda yadda... but I get the same resulsts whatever I do: whenever there are little objects on screen, the movement is almost that of lightning, while if there are many, the movement is that of a crippled old man crawling on the floor without arms and legs... It's strange, because I get a smoother transitions WITHOUT using any time-based movement, but there is still a small noticable slowdown when many objects are present in the rendering pipeline. Any suggestions? [Edited by - azjerei on January 1, 2005 4:11:40 PM]

Share this post


Link to post
Share on other sites
I know.. I hope I did not step on any toes.

Found this timer code in some old thread here.

Timer.cpp

#include "main.h"
#include "Timer.h"

double CTimer::GetTime()
{
__int64 EndClock;

QueryPerformanceCounter((LARGE_INTEGER *) &EndClock);

return (double) (EndClock - m_StartClock) * m_Frequency;
}

void CTimer::Init()
{
__int64 rate;

// Fetch performance frequency.
QueryPerformanceFrequency((LARGE_INTEGER *) &rate);

// Invert.
m_Frequency = 1.0 / (double) rate;

// Fetch start time.
QueryPerformanceCounter((LARGE_INTEGER *) &m_StartClock);

m_FrameTime = 0.0f;
m_FrameStart = (float) GetTime();
m_FrameEnd = 0.0f;
m_FpsLimiter = 0.0f;
m_Fps = 0.0f;
}

void CTimer::Update()
{
// Set a cap to the framerate so it does not go over the timer frequency.
// The timer frequency is normally faster than the framerate anyhow.
do
{
m_FrameEnd = (float) GetTime();
}
while (m_FrameEnd == m_FrameStart);

// Get the elapsed time.
m_FrameTime = m_FrameEnd - m_FrameStart;

// Set end to the next start.
m_FrameStart = m_FrameEnd;

// Get the FPS.
m_FpsLimiter += m_FrameTime;

if (m_FpsLimiter >= 0.25f) {
m_FpsLimiter = 0.0f;
m_Fps = float(1.0 / m_FrameTime);
}
}



Timer.h

class CTimer
{

protected:

double m_Frequency;

__int64 m_StartClock;

float m_FrameTime;
float m_FrameStart;
float m_FrameEnd;

float m_FpsLimiter;
float m_Fps;

public:

float GetFrameTime()
{
return m_FrameTime;
}

float GetFps()
{
return m_Fps;
}

double GetTime();

void Init();
void Update();
};

extern CTimer g_Timer;



I have created a CTimer object in main.cpp

CTimer g_Timer;

In Init(), I am initializing the timer:

g_Timer.Init();

In the main loop, I update the timer:

g_Timer.Update();

Controls.cpp

bool turningL = false, turningR = false;
bool walkingF = false, walkingB = false;
bool running = false;

float turningSpeed = 50.0f;//1.5f;
float walkingSpeed = 140.0f;//5.0f;
float runningSpeed = 80.0f;//2.0f; // Lower due to division.

//---------------------------------------------------------------
//
// void ReadKeyboardControls()
//
// Keyboard controls.
//
//---------------------------------------------------------------
void ReadKeyboardControls()
{
// Quitting the game.
if (keys[VK_ESCAPE])
PostQuitMessage(0);

// Snapping a screenshot.
if (keys['O'])
TakeScreenshot();

// Firing.
if (keys[VK_CONTROL]) {
FireWeapon();
player.g_Model.SetTorsoAnimation("TORSO_ATTACK");
}

if (!keys[VK_CONTROL]) {
player.g_Model.SetTorsoAnimation("TORSO_STAND");
player.drawMuzzleFlash = false;
}

// Turning.
if (keys['A']) {
player.g_fCameraRot += (turningSpeed * g_Timer.GetFrameTime());

if (player.g_fCameraRot >= 360.0f) player.g_fCameraRot = 0.0f + (player.g_fCameraRot - 360.0f);

if (!turningL && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningL = true;
}

if (keys['D']) {
player.g_fCameraRot -= (turningSpeed * g_Timer.GetFrameTime());

if (player.g_fCameraRot < 0.0f) player.g_fCameraRot = 360.0f + player.g_fCameraRot;

if (!turningR && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningR = true;
}

if (!keys['A']) {
if (turningL) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningL = false;
}

if (!keys['D']) {
if (turningR) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningR = false;
}

// Running.
if (keys[VK_SHIFT])
running = true;

// Walking.
if (keys['W']) {
float denominator = (walkingSpeed * g_Timer.GetFrameTime());

if (running)
denominator = (runningSpeed * g_Timer.GetFrameTime());

player.vCharMove.x += (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z += (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);
g_Camera.MoveCameraZ((float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);

if (!walkingF)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_RUN");

walkingF = true;
}

if (keys['S']) {
float denominator = (walkingSpeed * g_Timer.GetFrameTime());

if (running)
denominator = (runningSpeed * g_Timer.GetFrameTime());

player.vCharMove.x -= (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z -= (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) -(cos(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));
g_Camera.MoveCameraZ((float) -(sin(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));

if (!walkingB)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_BACK");

walkingB = true;
}

if (!keys[VK_SHIFT])
running = false;

if (!keys['W']) {
if (walkingF) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingF = false;
}

if (!keys['S']) {
if (walkingB) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingB = false;
}
}

Share this post


Link to post
Share on other sites
QueryPerformanceCounter is not the best timer with windows timeGetTime() is better,
there are basically 2 ways
heres the one i use, a fixed time step
http://www.flipcode.org/cgi-bin/fcarticles.cgi?show=63823

where to move something u use
eg man += mans_walking_speed

the other method (i think more widely used) where updates happen as fast as possible u use something like
eg man += mans_walking_speed * time_delta

Share this post


Link to post
Share on other sites
Here is the alternative code I used at first:

CalculateFrameRate()

//---------------------------------------------------------------
//
// void CalculateFrameRate()
//
// Calculate the frame rate.
//
//---------------------------------------------------------------
void CalculateFrameRate()
{
static float framesPerSecond = 0.0f;
static float lastTime = 0.0f;
static char strFrameRate[50] = {0};

static float frameTime = 0.0f;

// Get the current time in seconds.
float currentTime = timeGetTime() * 0.001f;

// Here we store the elapsed time between the current and last frame,
// then keep the current frame in our static variable for the next frame.
g_FrameInterval = currentTime - frameTime;
frameTime = currentTime;

// Increase the frame counter.
++framesPerSecond;

if (currentTime - lastTime > 0.001f)
ReadKeyboardControls();

// Now we want to subtract the current time by the last time that was stored to
// see if the time elapsed has been over a second, which means we found our FPS.
if (currentTime - lastTime > 1.0f) {
lastTime = currentTime;
sprintf(strFrameRate, "FPS: %d", int(framesPerSecond));
SetWindowText(g_hWnd, strFrameRate);
framesPerSecond = 0;
}
}



Controls.cpp

float turningSpeed = 50.0f;//1.5f;
float walkingSpeed = 140.0f;//5.0f;
float runningSpeed = 80.0f;//2.0f; // Lower due to division.

//---------------------------------------------------------------
//
// void ReadKeyboardControls()
//
// Keyboard controls.
//
//---------------------------------------------------------------
void ReadKeyboardControls()
{
// Quitting the game.
if (keys[VK_ESCAPE])
PostQuitMessage(0);

// Snapping a screenshot.
if (keys['O'])
TakeScreenshot();

// Firing.
if (keys[VK_CONTROL]) {
FireWeapon();
player.g_Model.SetTorsoAnimation("TORSO_ATTACK");
}

if (!keys[VK_CONTROL]) {
player.g_Model.SetTorsoAnimation("TORSO_STAND");
player.drawMuzzleFlash = false;
}

// Turning.
if (keys['A']) {
player.g_fCameraRot += (turningSpeed * g_FrameInterval);

if (player.g_fCameraRot >= 360.0f) player.g_fCameraRot = 0.0f + (player.g_fCameraRot - 360.0f);

if (!turningL && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningL = true;
}

if (keys['D']) {
player.g_fCameraRot -= (turningSpeed * g_FrameInterval);

if (player.g_fCameraRot < 0.0f) player.g_fCameraRot = 360.0f + player.g_fCameraRot;

if (!turningR && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningR = true;
}

if (!keys['A']) {
if (turningL) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningL = false;
}

if (!keys['D']) {
if (turningR) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningR = false;
}

// Running.
if (keys[VK_SHIFT])
running = true;

// Walking.
if (keys['W']) {
float denominator = (walkingSpeed * g_FrameInterval);

if (running)
denominator = (runningSpeed * g_FrameInterval);

player.vCharMove.x += (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z += (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);
g_Camera.MoveCameraZ((float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);

if (!walkingF)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_RUN");

walkingF = true;
}

if (keys['S']) {
float denominator = (walkingSpeed * g_FrameInterval);

if (running)
denominator = (runningSpeed * g_FrameInterval);

player.vCharMove.x -= (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z -= (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) -(cos(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));
g_Camera.MoveCameraZ((float) -(sin(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));

if (!walkingB)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_BACK");

walkingB = true;
}

if (!keys[VK_SHIFT])
running = false;

if (!keys['W']) {
if (walkingF) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingF = false;
}

if (!keys['S']) {
if (walkingB) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingB = false;
}
}



CalculateFrameRate() is located in g_Camera.Update(), the camera updating function, which is called in the main loop.

There is no difference between this method and the one mentioned above, they give the same results in the game, as mentioned at the top.

Share this post


Link to post
Share on other sites
Wow! Very complex code!

Make it cleaner...


float CalculateFrameRate(void)
{
static float fps = 0.0f;
static long frames = 0;
static long start_time = timeGetTime();
static long sample_time = 2000; // ms for new fps value

frames++;

long cur_time = timeGetTime();
long dif_time = cur_time - start_time;

if(dif_time>sample_time)
{
fps = (float)1000*frames/dif_time;
start_time = cur_time;
frames = 0;
}

return(fps);
}



However those static var are ugly...

Your problem is that you should use timing in movements not in keyboard reading!
Read keyboard every frame.
Something like this


void IdleLoop(void)
{
static long old_time = timeGetTime();

long cur_time = timeGetTime();
long elapsed_time = cur_time - old_time;
old_time = cur_time;
...

ReadKeyboard();

if("user is pressing right arrow"){
object.x += yourobject.xspeed * elapsed_time;
}
...
}



where xspeed is a float expressed in units/millisecond..this is timing.
If your timing has poor accuracy you can interpolate between different object.x values (to avoid "scattering").


// use input to change object.xtarget and smooth it
object.x += (object.xtarget - object.x)*factor; //factor is a float in (0,1)


Share this post


Link to post
Share on other sites
The ReadKeyboardInput() function is called in the main loop, so it is called as fast as need be. I only added the g_FrameInterval multiplier to the movement speeds, which should be a valid procedure.

Share this post


Link to post
Share on other sites
Heh, this is getting frustrating. I still get the same results. Here is the code as it looks now:

Main Loop

while(1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
g_Camera.Update(); // Update camera data.
RenderScene(); // Update the screen.
RenderOrtho(); // Render the orthographic stuff.
}
}



Camera Update

void CCamera::Update()
{
CVector3 vCross = Cross(m_vView - m_vPosition, m_vUpVector);

// Normalize the strafe vector
m_vStrafe = Normalize(vCross);

// Calculate our frame rate and set our frame interval for time-based movement
CalculateFrameRate();

// Each frame we rotate the camera according to how we rotate the character.
// This will be an option later on.
player.fCamPosX = player.vCharMove.x - (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) * 50.0f;
player.fCamPosY = player.vCharMove.y + 100.0f;
player.fCamPosZ = player.vCharMove.z - (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) * 50.0f;

PositionCamera(player.fCamPosX, player.fCamPosY, player.fCamPosZ,
player.vCharMove.x, player.vCharMove.y, player.vCharMove.z,
0, 1, 0);
}



CalculateFrameRate

void CalculateFrameRate()
{
static float fps = 0.0f;
static long frames = 0;
static long start_time = timeGetTime();
static long sample_time = 1000;
char strFrameRate[30] = {0};

frames++;

long cur_time = timeGetTime();
long dif_time = cur_time - start_time;

if (dif_time > sample_time) {
fps = (float) 1000 * frames / dif_time;
start_time = cur_time;
frames = 0;
sprintf(strFrameRate, "FPS: %d", int(fps));
SetWindowText(g_hWnd, strFrameRate);
}

ReadKeyboardControls();
}



ReadKeyboardControls

void ReadKeyboardControls()
{
static long old_time = timeGetTime();
long cur_time = timeGetTime();
long elapsed_time = cur_time - old_time;
old_time = cur_time;

float factor = float(elapsed_time);

// Quitting the game.
if (keys[VK_ESCAPE])
PostQuitMessage(0);

// Snapping a screenshot.
if (keys['O'])
TakeScreenshot();

// Firing.
if (keys[VK_CONTROL]) {
FireWeapon();
player.g_Model.SetTorsoAnimation("TORSO_ATTACK");
}

if (!keys[VK_CONTROL]) {
player.g_Model.SetTorsoAnimation("TORSO_STAND");
player.drawMuzzleFlash = false;
}

// Turning.
if (keys['A']) {
player.g_fCameraRot += (turningSpeed * factor);

if (player.g_fCameraRot >= 360.0f) player.g_fCameraRot = 0.0f + (player.g_fCameraRot - 360.0f);

if (!turningL && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningL = true;
}

if (keys['D']) {
player.g_fCameraRot -= (turningSpeed * factor);

if (player.g_fCameraRot < 0.0f) player.g_fCameraRot = 360.0f + player.g_fCameraRot;

if (!turningR && !walkingF && !walkingB)
player.g_Model.SetLegsAnimation("LEGS_TURN");

turningR = true;
}

if (!keys['A']) {
if (turningL) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningL = false;
}

if (!keys['D']) {
if (turningR) player.g_Model.SetLegsAnimation("LEGS_IDLE");
turningR = false;
}

// Running.
if (keys[VK_SHIFT])
running = true;

// Walking.
if (keys['W']) {
float denominator = (walkingSpeed * factor);

if (running)
denominator = (runningSpeed * factor);

player.vCharMove.x += (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z += (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);
g_Camera.MoveCameraZ((float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator);

if (!walkingF)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_RUN");

walkingF = true;
}

if (keys['S']) {
float denominator = (walkingSpeed * factor);

if (running)
denominator = (runningSpeed * factor);

player.vCharMove.x -= (float) cos(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;
player.vCharMove.z -= (float) sin(((-player.g_fCameraRot)*3.14f)/180.0f) / denominator;

g_Camera.MoveCameraX((float) -(cos(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));
g_Camera.MoveCameraZ((float) -(sin(((-player.g_fCameraRot)*3.14)/180.0f) / denominator));

if (!walkingB)
if (!running)
player.g_Model.SetLegsAnimation("LEGS_WALK");
else
player.g_Model.SetLegsAnimation("LEGS_BACK");

walkingB = true;
}

if (!keys[VK_SHIFT])
running = false;

if (!keys['W']) {
if (walkingF) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingF = false;
}

if (!keys['S']) {
if (walkingB) player.g_Model.SetLegsAnimation("LEGS_IDLE");
walkingB = false;
}
}



Game programming is maddening, especially when obvious things never work *slap code* :-/

Share this post


Link to post
Share on other sites
Ok, I noticed that there is a BIG difference between 0.01 and 0.02, so I capped the g_FrameInterval to somewhere in-between 0.1 and 0.11. It was the small numbers doing it...

Share this post


Link to post
Share on other sites

This topic is 4729 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this