Sign in to follow this  
GroZZleR

Hiccups as I walk between tiles...

Recommended Posts

GroZZleR    820
Hey all, I'm trying to have a character walk from one tile to another in your typical RPG style map layout. I'm looking to make the player walk from one tile to another before they are able to move again (ie, not move anywhere). This is for gameplay reasons and has to be exact. The problem is, the player hiccups as he walks ever so slightly beyond a tile and gets snapped back towards it. I'm using your typical position += velocity * deltaTime. This code is no doubt causing the issue:
			if (currentState == WALKING)
			{
				if (velocity.y < 0)
				{
					facing = FACING_UP;
					animationManager.setAnimation("WalkUp");
					
					if (targetTile.getPosition().y >= position.y)
					{
						position.y = targetTile.getPosition().y;
						
						velocity.y = 0;
					}
				}
				else if (velocity.x > 0)
				{
					facing = FACING_RIGHT;
					animationManager.setAnimation("WalkRight");
					
					if (targetTile.getPosition().x <= position.x)
					{
						position.x = targetTile.getPosition().x;
						
						velocity.x = 0;
					}
				}
				else if (velocity.y > 0)
				{
					facing = FACING_DOWN;
					animationManager.setAnimation("WalkDown");
					
					if (targetTile.getPosition().y <= position.y)
					{
						position.y = targetTile.getPosition().y;
						
						velocity.y = 0;
					}
				}
				else if (velocity.x < 0)
				{
					facing = FACING_LEFT;
					animationManager.setAnimation("WalkLeft");
					
					if (targetTile.getPosition().x >= position.x)
					{
						position.x = targetTile.getPosition().x;
						
						velocity.x = 0;
					}
				}
			}

Is the only solution to switch to a fixed time step and move away from the delta times? Thanks!

Share this post


Link to post
Share on other sites
DraganO    160
The code is quite simple and looks okay, so I don't think it is wrong.
The player shouldn't be able to walk beyond a tile.

The only thing i can think of: Maybe you're rendering at the wrong time. Are you sure you are:

1. Moving the player.
2. Checking it's position against the target tile.
3. Rendering your scene.

In that order?


Also prepare to get flamed for using the word "manager" in a variable name.^^

Share this post


Link to post
Share on other sites
fastcall22    10838
When the player presses a directional movement key, he is to move only one tile and cannot move again until he represses another key, is this correct?
Are you changing currentState to a non-walking state after reaching the destination tile?

EDIT:
Are you performing the the code snippet above before or after you are moving?

Share this post


Link to post
Share on other sites
Endurion    5407
The code you shows is the cause for moving back. The problem lies not here though.

The problem is (I assume) that you add the delta movement, but you don't clip it. So the player moves onto the target tile and a little bit beyond. The code snippet you showed then sets the player back causing the visual "hiccup".

If you need a tile-centric movement you have to check, if the delta movement would move the player beyond the tile. If it does, calculate the time delta that's actually needed to exactly reach the tile and stop the player there. Subtract the calculated delta from the full time delta and run the loop again until there is no more movement beyond a tile.

Share this post


Link to post
Share on other sites
fastcall22    10838
I agree with Endurion; the problem could lie here. Except, you could just set the position to the target position instead of the minimum delta that is required to move the position to the target position. [smile]

Try this:

if (velocity.y < 0)
{
facing = FACING_UP;
animationManager.setAnimation("WalkUp");

// check to see if the position after applying velocity * delta_time
// puts the character past the target position:
float predict_y = position.y + velocity.y * delta_time;
if ( targetTile.getPosition().y >= predict_y )
{
position.y = targetTile.getPosition().y;

velocity.y = 0;
}
}



Unfortunately, you'll have to change your four identical code blocks to match, you could refactor your code as:

struct WalkHelper
{
std::string mAnimName; // Animation name to feed to animationManager
FACING_ENUM mFacing; // Facing to set the character's "facing"
Vector2 mDirection; // The direction this refers to

WalkHelper( const std::string& anim, FACING_ENUM facing, const Vector2& dir ) :
mAnimName( anim ),
mFacing( facing ),
mDirection( dir )
{
}
};

/* ... */

if ( currentState == WALKING )
{
// Set up the possible movements (one-time init):
const WalkHelper walks[] = {
WalkHelper( "WalkUp", FACING_UP, Vector2( 0, -1 ) ),
WalkHelper( "WalkRight", FACING_RIGHT, Vector2( 1, 0 ) ),
WalkHelper( "WalkDown", FACING_DOWN, Vector2( 0, 1 ) ),
WalkHelper( "WalkLeft", FACING_KEFT, Vector2( -1, 0 ) )
};
const unsigned walks_sz = sizeof( walks ) / sizeof( WalkHelper );

// Iterate through all the possible movements:
for ( unsigned i = 0; i < walks_sz; ++i )
{
const WalkHelper& w = walks[i];

// Select the x or y component of w.mDirection that is not zero, then check
// to see if the selected component of direction and the character's velocity
// match signs:
if ( ( w.mDireciton.x != 0 && ( w.mDirection.x < 0 == velocity.x < 0 ) ||
( w.mDireciton.y != 0 && ( w.mDirection.y < 0 == velocity.y < 0 ) )
{
// Set facing and animation:
facing = w.mFacing;
animationManager.setAnimation( w.mAnimName );


// Get the distance between the character and the target tile:
float distance = ( targetTile.getPosition() - position ).length();

// Get the distance that the character will travel this frame:
float traveldst = ( velocity * dt ).length();

// If the distance is within the character's stride:
if ( distance < traveldst )
{
// Snap him to the tile, and set velocity to zero.
position = targetTile.getPosition();
velocity = Vector2( 0, 0 );

// No more directions to check
break;
}
}
}
}


*Untested code

[Edited by - _fastcall on June 19, 2009 2:04:47 AM]

Share this post


Link to post
Share on other sites
DraganO    160
Quote:
Original post by _fastcall
I agree with Endurion; the problem could lie here. Except, you could just set the position to the target position instead of the minimum delta that is required to move the position to the target position. [smile]


Try this:
*** Source Snippet Removed ***

Coming soon: A refactoring of your code snippet so you don't have to change your code in four places!


That's just needlessly complicated. If you simply check if the player moved to far after you actually moved him, you won't need to predict anything.

So just move him, THEN check if he moved too far. If so move him back. And THEN render.

Share this post


Link to post
Share on other sites

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