Collision precision and deltaT

Started by
5 comments, last by cozzie 9 years ago
Hi,
I've been struggling quite a while with a very annoying precision issue.
Here's my case:

- player/ mesh gets a predicted change in position
- collision is checked
- if no collision, player is moved (change position)

Now in step 1 the velocity is calculated using deltaT, and in step 3 the same. But due to precision changes (I think), in rare occasions the player gets stuck in corners where they shouldn't (don't think it matters, but I do aabb collision checks).

What's your advice on this?

I also tried to switch to move always and undo last movement when collision occurs, but although possible, isn't optimal because after collision checking there are several other parameters that 'decide' if the player should move and in what direction.

I also tried to use a "local deltaT", saving it in the beginning of the update function and reuse it for steps1 and 3. But no luck so far.

Any input is appreciated

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

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

Advertisement


... due to precision changes (I think) ...

I understand it's rare which makes it difficult, but I would suggest, before you make changes to your code, etc., that you determine exactly what the problem is. Fixing a problem is ever so much easier when you know what it is.

Memory is cheap compared to debugging time and effort. So, if you can detect the "stuck" condition, you can save the state of the character (or 10 or 15 previous states) before it's moved, and data to tell you how it got into the "stuck" condition. When the condition occurs, examine the data and determine if your guess at the problem is correct.

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. I can make a function that saves all relevant data (positions etc) and activate it with a assigned key. Then do some play testing and when stuck, save the data.

I'll let you know the results.

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

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

You could very well have a precision problem in the collision detection code but it's hard to tell based on the limited information that you've given us. I recommend reading this article either way: https://randomascii.wordpress.com/2012/02/13/dont-store-that-in-a-float/

Typical route taken for cheap not-getting-stuck-in-walls:

#1: Store current player position.
#2: Advance player.
#3: If there is a collision, put the player back at saved position.

I also tried to switch to move always and undo last movement when collision occurs, but although possible, isn't optimal because after collision checking there are several other parameters that 'decide' if the player should move and in what direction.

Then your order of operations is wrong.

Also, this is not a graphics question.

Any input is appreciated

Isn’t input exactly what causes you to get stuck in walls etc.?


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Thanks all.

@LSpiro: probably right on the input :)

Here's the current/ old code, I'm gonna dig into it and see if I can do the movement always and undo the movement if there's a collision.


bool CGame::UpdateGameTop()
{
	/*1* determine wanted direction (keyboard input or no changes/ keep moving)	**/
	int currentDir = mPlayer.mDirection;
	int wantedDir = GetUserInputTop();
	
	if(wantedDir == -1) 
		return false;

	/*2* check collision by 'predicting movement'	**/
	CGame_Player tempPlayer = mPlayer;
	tempPlayer.SetDirection(wantedDir);

	VECTOR3 tempMovement = tempPlayer.GetMovement() * mTimer.GetDelta();
	tempPlayer.Move(tempMovement); // * 0.75f);										// HACK: TO PREVENT GETTING 'STUCK' IN CORNERS => 27-4, was 1.2f

	bool collides = PlayerCollidesWall(tempPlayer);

	/*3* move the player, if no collision	**/
	
	// COLLISION + DIR NOT CHANGED		=> DON'T MOVE				UPDATE DIR => 0				OK
	// COLLISION + DIR CHANGED			=> MOVE						DON'T UPDATE DIR			OK
	// NO COLLISION + DIR NOT CHANGED	=> MOVE						DON'T UPDATE DIR			OK
	// NO COLLISION + DIR CHANGED		=> MOVE						UPDATE DIR = WANTED			OK

	if(collides && currentDir == wantedDir)			// collision, direction unchanged
	{
		mPlayer.SetDirection(0);
		return true;								// STOP
	}
	if(collides && currentDir != wantedDir)			// collision, changed direction
	{
		CGame_Player tempPlayer = mPlayer;
		tempPlayer.SetDirection(currentDir);

		VECTOR3 tempMovement = tempPlayer.GetMovement();
		tempPlayer.Move(tempMovement * mTimer.GetDelta());

		if(PlayerCollidesWall(tempPlayer))			// collision with old direction?
		{
			mPlayer.SetDirection(0);
			return true;							// STOP
		}
	}

	if(!collides &&	currentDir != wantedDir) mPlayer.SetDirection(wantedDir);		// change direction

	// MOVE THE PLAYER
	VECTOR3 movement = mPlayer.GetMovement();
	mPlayer.Move(movement * mTimer.GetDelta());
			
	// update player mesh inst position & player look rotation
	mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetWorldPos(DirectX::XMFLOAT3(mPlayer.GetPosition().x, mPlayer.GetPosition().y, mPlayer.GetPosition().z));
	mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetRot(DirectX::XMFLOAT3(0.0f, mPlayer.GetYRotLookDir(), 0.0f));

	/*4* check if player went through the mazehole and return to other side	**/
	PlayerThroughMazeHole();

	return true;
}

To illustrate, in First Person view / view from camera, I don't have the issue.

The big difference here is that movement is not 'automatic', only when the player pressed a key/ keeps a key pressed:


bool CGame::UpdateGameFP()
{
	mPlayer.Update();	

	VECTOR3 movement = VECTOR3(0.0f, 0.0f, 0.0f);

	if(mInput.KeyDown(VkKeyScan('w')&0xFF)) movement.z = 1.0f;										// DOWN
	if(mInput.KeyDown(VkKeyScan('s')&0xFF)) movement.z = -1.0f;										// DOWN
	
	if(mInput.KeyDown(VkKeyScan('a')&0xFF)) movement.x = -1.0f;										// DOWN
	if(mInput.KeyDown(VkKeyScan('d')&0xFF)) movement.x = 1.0f;										// DOWN

	// TESTING ONLY
	if(mInput.KeyDown(VK_PRIOR)) movement.y = 1.0f;													// DOWN
	if(mInput.KeyDown(VK_NEXT)) movement.y = -1.0f; 												// DOWN

	movement = mPlayer.NormalizeMovement(movement);

	mSceneManager.GetCurrentD3dcamRef().Move(movement * mTimer.GetDelta());
	mPlayer.SetPosition(mSceneManager.GetCurrentD3dcamRef().GetPositionV3());
	mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetWorldPos(mSceneManager.GetCurrentD3dcamRef().GetPosition());

	// collision with walls/ maze?
	if(PlayerCollidesWall(mPlayer))							// COLLISION: move back the player
	{
		mSceneManager.GetCurrentD3dcamRef().SetPosition(mSceneManager.GetCurrentD3dcamRef().GetLastPosition());
		mPlayer.SetPosition(mSceneManager.GetCurrentD3dcamRef().GetPositionV3());
		mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetWorldPos(mSceneManager.GetCurrentD3dcamRef().GetPosition());
	}

	PlayerThroughMazeHole();

	// Mouselook input
	if(mInput.MouseMoved()) mSceneManager.GetCurrentD3dcamRef().FreeLook(mInput.GetMouseMoveX(), mInput.GetMouseMoveY(), mPlayer.GetLookSpeed());

	// Update ghost indicator
	if(!UpdateGhostIndicator()) return false;

	return true;
}

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

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

Problem solved, no more hacks and done 'the way it should be'.

New approach:


bool CGame::UpdateGameTop()
{
	//	a.	check if direction is changed to determine 'wanted direction'
	//		b. try to move in wanted direction
	//			c. if possible, movement is done
	//			d. if not possible, undo movement
	//				e. try to move in old direction if direction != 0 (not moving)
	//					f. if possible, movement is done
	//					g. if not possible, new direction = 0 (not moving)

	int currentDir = mPlayer.mDirection;
	int wantedDir = GetUserInputTop();

	if(wantedDir == -1) return false;

	bool dirChanged = currentDir != wantedDir;
	bool done = false;
	if(dirChanged)		// move in new/ wanted direction
	{
		mPlayer.SetDirection(wantedDir);
		VECTOR3 movement = mPlayer.GetMovement() * mTimer.GetDelta();
		mPlayer.Move(movement);

		bool collides = PlayerCollidesWall(mPlayer);

		if(collides) 
		{
			mPlayer.SetPosition(mPlayer.GetLastPosition());		// move back player
			mPlayer.SetDirection(mPlayer.mLastDirection);
		}
		else done = true;
	}

	if(!done && mPlayer.mDirection != 0)		// move in old direction (automatically)
	{
		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);
		}
	}

	// update player mesh inst position & player look rotation
	mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetWorldPos(DirectX::XMFLOAT3(mPlayer.GetPosition().x, mPlayer.GetPosition().y, mPlayer.GetPosition().z));
	mSceneManager.GetCurrentD3dsceneRef().mMeshInst[mPlayerMeshInstId].SetRot(DirectX::XMFLOAT3(0.0f, mPlayer.GetYRotLookDir(), 0.0f));

	/*4* check if player went through the mazehole and return to other side	**/
	PlayerThroughMazeHole();

	return true;
}

Time for playtesting now. Thanks again for the help.

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