Sign in to follow this  

Smooth Tile-Based Movement

This topic is 4200 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

How would I accompish continuous tile-based movement using SDL and C++? Right now I have it so my character can move 1 tile a time for every key press, but he does it in small chunks so it looks nice and smooth. Currently holding down a key will only move the character 1 tile. What I want to have happen is that the user can hold down a key and the character will continously move but will stop when the key is let go and will end up in the middle of a tile. Sort of like what happens in dragon warrior or final fantasy. Here is my input code:

//handle game input
void handleGameInput()
{
	// Fill our event structure with event information. //
	if ( SDL_PollEvent(&g_Event) )
	{
		// Handle keyboard input here //
		Uint8 *keys;
		keys = SDL_GetKeyState(NULL);
		if(keys[SDLK_UP]) {
			Hero.moving_up = true;
		}
		if(keys[SDLK_DOWN]) {
			Hero.moving_down = true;
		}
		if (keys[SDLK_ESCAPE])
		{
			currentstate = MENU;
			Done = 1;
			//return;  // this state is done, exit the function 
		}	
		if (keys[SDLK_RIGHT])
		{
			Hero.moving_right = true;
		}
		if (keys[SDLK_LEFT])
		{
			Hero.moving_left = true;
		}	
	}
	
	Hero.move(Map);
}


Here is my player.move() code:

//Move hero 1 tile at a time in direction specified
void Player::move(Tile tiles[MAP_HEIGHT][MAP_WIDTH])
{

	if(moving_up)
	{
		//check to see if hero is hitting wall or going off map
		if(checkWallCollisions(UP,tiles) == false)
		{
			posy = posy - PLAYER_SPEED;

			//the +8 below represents the map offset (which is 120)
			//and the nearest starting pos for y tile (which is 128)
			if((posy + 8) % TILE_SIZE == 0)
			{
				moving_up = false;

				//fight battle
				fight();
			}
		}
		else
		{
			//stop player
			moving_up = false;
		}
	}
	if(moving_down)
	{
		//check to see if hero is hitting wall or going off map
		if(checkWallCollisions(DOWN,tiles) == false)
		{
			posy = posy + PLAYER_SPEED;
			
			//the +8 below represents the map offset (which is 120)
			//and the nearest starting pos for y tile (which is 128)
			if((posy + 8) % TILE_SIZE == 0)
			{
				moving_down = false;

				//fight battle
				fight();
			}
		}
		else
		{
			//stop player
			moving_down = false;
		}
	}
	if(moving_left)
	{
		//check to see if hero is hitting wall or going off map
		if(checkWallCollisions(LEFT,tiles) == false)
		{
			posx = posx - PLAYER_SPEED;
			if(posx % TILE_SIZE == 0)
			{
				moving_left = false;

				//fight battle
				fight();
			}
		}
		else
		{
			//stop player
			moving_left = false;
		}
	}
	if(moving_right)
	{
		//check to see if hero is hitting wall or going off map
		if(checkWallCollisions(RIGHT,tiles) == false)
		{
			posx = posx + PLAYER_SPEED;
			if(posx % TILE_SIZE == 0)
			{
				moving_right = false;

				//fight battle
				fight();
			}	
		}
		else
		{
			//stop player
			moving_right = false;
		}
	}

	//update player variables
	posx_middle = posx+(TILE_SIZE/2);
	posy_middle = posy+(TILE_SIZE/2);
}


Thanks, Han

Share this post


Link to post
Share on other sites
Actually, Final Fantasy and Dragon Warrior only have tile-by-tile movement. If you want movement like in Legend of Zelda, you need to set your moving_up, moving_down etc. to false at the end of Player::move and probably change your checkWallCollision method, which you haven't posted.
By the way, does this

if(posx % TILE_SIZE == 0)
{
moving_left = false;

//fight battle
fight();
}


mean you fight a battle every step? Doesn't sound like fun.

Share this post


Link to post
Share on other sites
Do you have a way of know when the player has hit the center of the tile? I'd just call handleGameInput() again after the player has moved in to position.

Share this post


Link to post
Share on other sites
Hehe right now it does because I am working on the battle system. Later on it will be random battles or some other method. As for FF having only tile-by-tile movement, that is true, but it is also true that you can hold down your joystick in one direction and the character will keep moving. This is what I am hoping to accomplish. Right now my character will only move 1 tile per keypress, even if I hold it down.

Thanks,

Han

Share this post


Link to post
Share on other sites
Hmmm.. I think it's the SDL_PollEvent. Try throwing out

if(SDL_PollEvent(...))) { ... }

and put

SDL_PumpEvents();

at the top (SDL_PumpEvents() is basically a function to update the internal key state, etc. if you don't use other event handling functions).

Share this post


Link to post
Share on other sites
I now have the code so that my character will move continously if I hold down an arrow key. But now I want the character to stop in the middle of the tile if I let go of the arrow key. How can I accomplish this?

Here is my GameInput code:



while (SDL_PollEvent(&g_Event)) //Poll our SDL key event for any keystrokes.
{
switch(g_Event.type)
{
case SDL_KEYDOWN:
switch(g_Event.key.keysym.sym)
{
case SDLK_LEFT:
Hero.moving_left = true;
break;
case SDLK_RIGHT:
Hero.moving_right = true;
//moverate = 4;
break;
case SDLK_UP:
Hero.moving_up = true;
break;
case SDLK_DOWN:
Hero.moving_down = true;
break;
case SDLK_ESCAPE:
currentstate = MENU;
Done = 1;
//return; // this state is done, exit the function
break;
default:
break;
}
break;
case SDL_KEYUP:
switch(g_Event.key.keysym.sym)
{
case SDLK_LEFT:
Hero.moving_left = false;
break;
case SDLK_RIGHT:
Hero.moving_right = false;
//moverate = 0;
break;
case SDLK_UP:
Hero.moving_up = false;
break;
case SDLK_DOWN:
Hero.moving_down = false;
break;
default:
break;
}
break;
}
}

//Hero.posx = Hero.posx + moverate;
Hero.move(Map);




Here is my Hero.move function:



//Move hero 1 tile at a time in direction specified
void Player::move(Tile tiles[MAP_HEIGHT][MAP_WIDTH])
{

if(moving_up)
{
//check to see if hero is hitting wall or going off map
if(checkWallCollisions(UP,tiles) == false)
{
posy = posy - PLAYER_SPEED;

//the +8 below represents the map offset (which is 120)
//and the nearest starting pos for y tile (which is 128)
if((posy + 8) % TILE_SIZE == 0)
{
//moving_up = false;

//fight battle
//fight();
}
}
else
{
//stop player
moving_up = false;
}
}
if(moving_down)
{
//check to see if hero is hitting wall or going off map
if(checkWallCollisions(DOWN,tiles) == false)
{
posy = posy + PLAYER_SPEED;

//the +8 below represents the map offset (which is 120)
//and the nearest starting pos for y tile (which is 128)
if((posy + 8) % TILE_SIZE == 0)
{
//moving_down = false;

//fight battle
//fight();
}
}
else
{
//stop player
moving_down = false;
}
}
if(moving_left)
{
//check to see if hero is hitting wall or going off map
if(checkWallCollisions(LEFT,tiles) == false)
{
posx = posx - PLAYER_SPEED;
if(posx % TILE_SIZE == 0)
{
moving_left = false;

//fight battle
//fight();
}
}
else
{
//stop player
moving_left = false;
}
}
if(moving_right)
{
//check to see if hero is hitting wall or going off map
if(checkWallCollisions(RIGHT,tiles) == false)
{
posx = posx + PLAYER_SPEED;
if(posx % TILE_SIZE == 0)
{
moving_right = false;

//fight battle
//fight();
}
}
else
{
//stop player
moving_right = false;
}
}

//update player variables
posx_middle = posx+(TILE_SIZE/2);
posy_middle = posy+(TILE_SIZE/2);
}

Thanks,

Han


Share this post


Link to post
Share on other sites
Quote:
Original post by Hannibal111111
I now have the code so that my character will move continously if I hold down an arrow key. But now I want the character to stop in the middle of the tile if I let go of the arrow key. How can I accomplish this?


1)Spawn your character in the middle of a tile.

2)Allow him to move only one tile at a time, either by having him move at a certain speed for a set amount of time, or have him move and continually test if his position is the center of a tile.(An additional thing to do is when the character stops have him "snap" to the nearest tile center. This will prevent error from acuminating if using the time based method)

3)Only check for input when the character is not moving.(If the player is holding the direction key down he'll start moving again immediately as soon as he stops so the player wont notice.)



It would work something like this.

Character is at sometime in its center.

The player pushes right.

The character starts moving right and wont stop until he reaches the next tile.

The character reaches the next tile center.

The input is checked and it looks like the player is still holding right.

Start moving again.

The player releases the right button.

The character reaches the next tile center.

The input is checked and no buttons are being pressed so there we wait.


Share this post


Link to post
Share on other sites
When no keys are being pressed, find out which tile you are on. Set the target position to the middle of that tile, and each subsequent frame (as long as they don't press a key again) move towards that position until you reach it!

Share this post


Link to post
Share on other sites

This topic is 4200 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