Having big problems with time-based movement (solved)

Started by
10 comments, last by azjerei 19 years, 3 months ago
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]
Advertisement
Code, please?

And btw, this doesn't really belong in the OpenGL forum.
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;	}}
I've used the code in a little test and everything seemed to work fine. Are you sure you update the timer only once in every frame?
I put the timer update in the main loop, in WinMain. I thought that was a good idea.
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
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.
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 itobject.x += (object.xtarget - object.x)*factor; //factor is a float in (0,1) 

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.
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* :-/

This topic is closed to new replies.

Advertisement