Collision & v-sync

Started by
9 comments, last by Buckeye 8 years, 11 months ago

Hi,

I'm struggling with a very annoying issue on AABB collision detection/ reponse in combination with Vsync.

In short:

- user presses arrow key, player moves in the right direction:


VECTOR3 movement = mPlayer.GetMovement() * mTimer.GetDelta();

- if the player collides with a wall, the player moves back to last position:


		if(collides)
		{
			mPlayer.SetPosition(mPlayer.GetLastPosition());		// move back player

All so far so good, with Vsync enabled that is.

Now when I disable Vsync, I 'think' (and debugged :)) the following happens:

- the size of the movement vector (velocity) is so small that the increase in movement does not cause a collision, but the player moves 'inside the path'/ track in the maze

booh_collision.jpg

The goal I'm trying to achieve is that the player can only move 'the' paths, which works fine with AABB / AABB collision checking between the wall parts of the player, but only with Vsync enabled (because the movement amounts are then large enough to prevent moving basically between walls).

I also played around with the AABB of the player to fix it, but it just doesn't get accurate enough (and the AABB seems fine I guess, because with Vsync on it's all good).

The width/depth between walls is 100% sure/ Always 1.0f, this is the AABB size of the player I currently use:


	mPlayer.ManualAABBUpdate(VECTOR3(-0.486f, -0.5f, -0.486f), VECTOR3(0.486f, 0.5f, 0.486f));

Any input to point me in the right direction is welcome.

I'd like so support both Vsync enabled and disabled in my game, so the user can decide.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement

While I don't know what's wrong with the collision resolving, But could you not push the play based on the collision normal and the penetration depth?

Have you looked into Bullet physics?, I found it easy to use once I spent about an hour or two looking at the demo's and online Docs and examples.

HTH

Ryan.

Never say Never, Because Never comes too soon. - ryan20fun

Disclaimer: Each post of mine is intended as an attempt of helping and/or bringing some meaningfull insight to the topic at hand. Due to my nature, my good intentions will not always be plainly visible. I apologise in advance and assure you I mean no harm and do not intend to insult anyone.

As you found already, the steps are too small and screw up your physics code. Thats the problem with variable time steps.

The article most people recommend in such a case is http://gafferongames.com/game-physics/fix-your-timestep/ . You should read it!

Thanks, I've just read the article.
From what I understand, I have a couple of options:

- don't support disabling Vsync
- go for a fixed timestep
- split up "timing" for rendering vs physics (in my case player movement)
- set a minimum deltaT

Option 1 and 4 sound best to me.
Honestly I don't how to implement option 3 anyway.

If possible I'd like to leave my main loop alone, where I currently dont do anything with delays or frametimes (other then updating my timer/ deltaT), because multiplying deltaT with each movement (physics) works fine 99% of the time (this issue is the 1st I ever encounter on timing).

Btw, my current deltaT is set up with a static fps, so I basically setup the timer for 60fps, and update each frame to calculate the correct deltaT (would this be a fixed timestep?)

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me


- don't support disabling Vsync
- go for a fixed timestep
- split up "timing" for rendering vs physics (in my case player movement)
- set a minimum deltaT

You should do all of the first 3 things you listed. The last option (which you later describe in terms of calculating a deltaT) should not be done. A "fixed" timestep is just that - it doesn't change.


I'd like to leave my main loop alone

The main loop is where you should fix the problem.


I don't how to implement option 3 anyway.

Read the linked article on "fixing your timestep" - the "Free the Physics" section.

In that section the author uses "integrate(...)" for what you would probably understand as "character->update( fixed-delta-t )"

EDIT: The idea is to completely separate the dependence of the delta-time used to update the character with the rate at which the resulting image is rendered.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Thanks.

Doing option 1 (don't support disabling vsync) actually solved the problem already (for my game in this situation).
Although I expect you to say that I'd be Lucky, because in theory it's not how it should work :)

So I'd really like to learn/ figure out how to decouple deltaT/ timing for rendering and 'physics'/ updating gameplay stuff.

I don't fully understand how to get there, but what I think is that:

- each update of the game I render

- based on elapsed time and 'set' time for rendering and/or physics/updating game, for some frames I won't call updating the game

It's a bit fuzzy still, maybe you can point me a bit in the right direction

(I've read the article multiple times, but don't understand yet how to go from there, in my code).

Here's some code snippets of my current solution:

Timer initialization (called with parameter value 60):


void CTimer::Init(const float pTargetFps)
{
	mTargetFps = pTargetFps;
	QueryPerformanceCounter((LARGE_INTEGER*)&mFrameDelay);
	QueryPerformanceFrequency((LARGE_INTEGER*)&mTicksPerSecond);

	mStartTime = GetTickCount(); // for testing only
}

Timer updating:


void CTimer::CalcFps()
{
	QueryPerformanceCounter(&mCurrentTicks);
	mSpeedFactor = (float)(mCurrentTicks.QuadPart-mFrameDelay.QuadPart) / ((float)mTicksPerSecond.QuadPart/mTargetFps);

	mFps = mTargetFps / mSpeedFactor;
	if(mSpeedFactor <= 0.0f)
		mSpeedFactor = 1.0f;

	mFrameTime = ((mCurrentTicks.QuadPart - mFrameDelay.QuadPart) * 1000 / mTicksPerSecond.QuadPart);

	mFrameDelay = mCurrentTicks;
}

Main loop:


	bool run = true;
	while(run)
	{
		_game.mInput.UpdateKeys();
	
		while(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			if(msg.message == WM_QUIT) run = false;
		}
		_game.mTimer.CalcFps(); // updates deltaT
	
		if(active)
		{
			if(_game.mD3d.CheckDevice())
			{	
				// Game updating, including player, audio etc.
				if(!_game.Update())
				{
					run = false;
				}

				// Rendering
				if(!_game.Render())
				{
					_game.mD3d.MsgBoxError(err_render);	
					run = false;
				}
			}
		}
	}

Part of game updating, moving the player:


		// GetMovement returns vector3 based on current direction
		VECTOR3 movement = mPlayer.GetMovement() * mTimer.GetDelta();
		mPlayer.Move(movement);

		bool collides = PlayerCollidesWall(mPlayer);

		if(collides)
		{
			mPlayer.SetPosition(mPlayer.GetLastPosition());		// move back player
			mPlayer.SetDirection(0);
		}

Does 'decoupling' mean that in my main loop I have to keep 'track of time', and basically desynchronize the calling of Update() and Render()?

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me


Does 'decoupling' mean that in my main loop I have to keep 'track of time', and basically desynchronize the calling of Update() and Render()?

Not sure "desynchronize" is the right term.

Something similar to the following (this is from an app with some stuff removed) - for illustration of the principle


    __int64 prevTimeStamp = 0;
    QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);
    long long physicsDelta = 0;
    long long physicsDT = __int64( float(cntsPerSec) / 60.0f ); // 1/60th second - pick something suitable
       while( msg.message != WM_QUIT ... [main loop] )
       {
            ...
            // in the main loop
            __int64 currTimeStamp = 0;
            QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
            physicsDelta += (currTimeStamp - prevTimeStamp);
            float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;

            while (physicsDelta >= physicsDT)
            {
                float updateDT = float(physicsDT) / float(cntsPerSec);
                if (bPause)
                {
                    updateDT = 0;
                }
                physicsDelta -= physicsDT;
                UpdateVehicle(updateDT);
            }
            UpdateScene(dt); // update objects in the scene that aren't physics delta time critical - lights on/off, windmills rotating, etc.
            Render();
            // Prepare for next iteration: The current time stamp becomes
            // the previous time stamp for the next iteration.
            prevTimeStamp = currTimeStamp;
        }

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Not allowing disabling of V-sync isn't really an option, as that's something someone can force on/off in their videocard.

It's been a while, but I've finally implemented it and tested it out, so for no luck.

updateDT seems to stay 0 or very low, resulting in now movements anymore in my game.

Any ideas?


int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine,
				   int nCmdShow)
{

// load and initialize the game

	// TESTING DELTA T - GAME UPDATING (WITHOUT VSYNC)
	__int64 cntsPerSec = 0;
	QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);
	float secsPerCnt = 1.0f / (float)cntsPerSec;
	
	__int64 prevTimeStamp = 0;
	QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);

	long long physicsDelta = 0;
	long long physicsDT = __int64(float(cntsPerSec) / 60.0f);	// 1/60th second to update game

	// CONTINUE

	bool run = true;
	while(run)
	{
		_game.mInput.UpdateKeys();
	
		while(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			if(msg.message == WM_QUIT) run = false;
		}
		//_game.mTimer.CalcFps();
	
		if(active)
		{
			if(_game.mD3d.CheckDevice())
			{	
				/**** TESTING DELTA T - TIMING FOR GAME UPDATING (WITHOUT V-SYNC) *****/
				__int64 currTimeStamp = 0;
				QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
				physicsDelta += (currTimeStamp - prevTimeStamp);
				float dt = (currTimeStamp - prevTimeStamp) * secsPerCnt;
				/***** CONTINUE ****/

				// Game updating, including player, audio etc.
				
				/****** TESTING DELTA-T ******/
				while(physicsDelta >= physicsDT)
				{
					float updateDT = float(physicsDT) / float(cntsPerSec);
					physicsDelta -= physicsDT;

					if(!_game.Update(updateDT))
					{
						run = false;
					}
				}
				prevTimeStamp = currTimeStamp;

				// Rendering
				if(!_game.Render())
				{
					_game.mD3d.MsgBoxError(err_render);	
					run = false;
				}
			}
		}
	}
	return (INT)msg.wParam;
}

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Problem solved already.
Was a mix up between using "old" deltaT and new method.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement