9 Replies - 5716 Views - Last Post: 01 February 2009 - 01:59 PM Rate Topic: -----

#1 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

pacman collision/movement alogrithm.

Post icon  Posted 29 January 2009 - 07:29 PM

EDIT: Sorry, I thought this was the game dev forum. could a mod please move this over to that forum for me?

so after finishing my pong game I set off to start writing my pacman clone. It's been quite grueling, without any info on how to implement some of these features. However, I've managed to do it till now.

The player should only be able to change directions when they are close to the origin of the tile they currently inhabit, but because of how my update function works, the player can get still change the movement of the player.

It's kind of hard to explain, so I will let the code talk for me.

Hopefully you guys can help me. I haven't read how anyone else has done this, so I've been guessing as to how to make everything work correctly.

//===========================================================
// IN MAIN
//===========================================================
void HandleEvents( void )
{
	if( g_nCurFrame - g_nHandleEventsFrame <= 0 )
		return;

	//events
	if( SDL_PollEvent( &g_Event) )
	{
		//check if the user wants to quite
		if( g_Event.type == SDL_QUIT || g_Event.key.keysym.sym == SDLK_ESCAPE )
		{
			g_bQuit = true;
		}

		switch( g_Event.key.keysym.sym )
		{
			case SDLK_UP:
					g_pPlayer->SetDirection( UP );
				break;

			case SDLK_DOWN:
					g_pPlayer->SetDirection( DOWN );
				break;

			case SDLK_RIGHT:
					g_pPlayer->SetDirection( RIGHT );
				break;

			case SDLK_LEFT:
					g_pPlayer->SetDirection( LEFT );
				break;

			case SDLK_SPACE:
					std::cout << "hit";
				break;
		}
	}

	g_nHandleEventsFrame = g_nCurFrame+2;
}

//==================================================
// THE PLAYER UPDATE FUNCTION CALLED EVERY FRAME
//==================================================
//========================================================
// Purpose: constructors, they're so cool
//========================================================
void Player::Update( void )
{
	int NextTilex = 0;
	int	NextTiley = 0;

	//find the direction the pacman is traveling in
	switch( m_nDirection )
	{
		case UP:
			--NextTiley;
			break;

		case DOWN:
			++NextTiley;
			break;

		case LEFT:
			--NextTilex;
			break;
		
		case RIGHT:
			++NextTilex;
			break;
	}

	//check to see if the player is moving 90 degrees from their previous direction
	if( (m_nOldDirection == UP || m_nOldDirection == DOWN) && (m_nDirection == LEFT || m_nDirection == RIGHT) )
	{
		//check to see if the player is near the origin of the tile they are currently in
		Vector2 vecOrigin = GetTileOrigin(GetTilex(), GetTiley());

		//check to see if the player is close enough to the origin to constitute them moving
		if( abs(vecOrigin.x - m_vecOrigin.x) <= 4 && abs(vecOrigin.y - m_vecOrigin.y) <= 4  )
		{
			//set the player to the origin position
			SetPos( vecOrigin-Vector2(16,16) );
		}

		//if the player isn't near the origin, set the direction to the previous one
		else 
			m_nDirection = m_nOldDirection;
	}


	//check to see if the next tile is collidable
	if( GetTileFlag( GetTilex()+NextTilex, GetTiley()+NextTiley ) & NOTWALKABLE )
	{
			m_nDirection = NONE;
	}

	//handle input
	switch( m_nDirection )
	{
		case UP:
		{
			//update the animation
			m_pSprPlayer->Animate( 8, 4, true );

			SetPosy( GetPos().y - m_fSpeed );
			break;
		}

		case DOWN:
		{
			//update the animation
			m_pSprPlayer->Animate( 12, 4, true );

			SetPosy( GetPos().y + m_fSpeed );
			break;
		}

		case LEFT:
		{
			//update the animation
			m_pSprPlayer->Animate( 0, 4, true );

			SetPosx( GetPos().x - m_fSpeed );
			break;
		}

		case RIGHT:
		{
			//update the animation
			m_pSprPlayer->Animate( 4, 4, true );

			SetPosx( GetPos().x + m_fSpeed );
			break;
		}

		case NONE:
		{
			//update the animation
			m_pSprPlayer->SetCurrentFrame( 0 );
			break;
		}
	}

	//set the sprite position
	m_pSprPlayer->SetPos( m_vecPos );
}

This post has been edited by mastersmith98: 29 January 2009 - 07:33 PM


Is This A Good Question/Topic? 0
  • +

Replies To: pacman collision/movement alogrithm.

#2 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 02:44 PM

Bump:

I've tried using another method, and that didn't work either. (it was quite complicated, and I deleted most of the code, so I don't have much to show for it). gah, why does this have to be so complex?
Was This Post Helpful? 0
  • +
  • -

#3 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 05:10 PM

hmmmm tough one

what about if you have a bool such as

bool movingToNewTile;

then you can only change direction if not moving to new tile

so


pseudo code

if(key press left && !movingToNewTile)
{
	moveLeft();
}




that would probably work, but im sorry that i cant tell you exactly how to implement this but the logic is there and thats what learnings all about :)

let me know how you implemented this (if you did) and ill tell you how to improve it :)

good luck :^:
Was This Post Helpful? 0
  • +
  • -

#4 WolfCoder  Icon User is offline

  • Isn't a volcano just an angry hill?
  • member icon


Reputation: 784
  • View blog
  • Posts: 7,613
  • Joined: 05-May 05

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 05:45 PM

You're doing this way too complicated than it needs to be~ Pac-Man keeps moving until he hits a wall and then stops. He moves TILE_SIZE number of pixels to reach each new tile right? You should keep track of how much distance is left (in any direction) before Pac-Man reaches a new tile.

Right when Pac-Man runs out of space, align him back to the grid in case he's a little off (if you love using doubles or floats). Then at that moment, immediately read the keyboard input and change Pac-Man's direction to match the keys. Never handle user input except for that moment in time.

This way, the user is holding down where they want to go, and when Pac-Man can go there he will go there (which occurs every TILE_SIZE pixels moved). Don't forget to set the distance left.
Was This Post Helpful? 0
  • +
  • -

#5 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 08:33 PM

the tiles aren't actually used for movement (except to see if the player is at the origin of one so that he can turn at a 90 degree angle of the direction they were going in Ex: you're going up and you decide to move left. if you're not close enough to the origin, you won't be able to move left). they are there mainly for collision. Each Tile is 32 pixels square (the size of a character sprite), so moving a character from tile to tile would look sloppy.

Quote

Right when Pac-Man runs out of space, align him back to the grid in case he's a little off (if you love using doubles or floats). Then at that moment, immediately read the keyboard input and change Pac-Man's direction to match the keys. Never handle user input except for that moment in time.

right now (as you can see) my input function is very detached from my update function. is there any possible way I could make the two more, cooperative?

Quote

This way, the user is holding down where they want to go, and when Pac-Man can go there he will go there (which occurs every TILE_SIZE pixels moved). Don't forget to set the distance left.

I would prefer to keep it like the original pacman, allowing the player to press the key in a direction and have the pacman travel in that direction indefinitely till he hits a wall.

and although I would only like to check for user input when the pacman is close to the origin of a tile, there is the problem of changing the player in the opposite direction of the direction they were traveling, which shouldn't have to be in the origin of a tile.

I'm sorry that this is horribly vague guys, but I really appreciate you trying to help :D

EDIT: here is what my current code looks like (it's about 400 times more complicated than the past code lol).

//========================================================
// Purpose: Handle Events
//========================================================
void HandleEvents( void )
{
	//events
	while( SDL_PollEvent( &g_Event) )
	{
		switch( g_Event.type )
		{

			case SDL_QUIT:
				g_bQuit = true;
			break;

			case SDL_KEYDOWN:
			{
				switch( g_Event.key.keysym.sym )
				{
					case SDLK_UP:
							g_pPlayer->SetDirection( Vector2(0, -1.f) );
						break;

					case SDLK_DOWN:
							g_pPlayer->SetDirection( Vector2(0, 1.f) );
						break;

					case SDLK_RIGHT:
							g_pPlayer->SetDirection( Vector2(1.f, 0) );
						break;

					case SDLK_LEFT:
							g_pPlayer->SetDirection( Vector2(-1.f, 0) );
						break;

					case SDLK_SPACE:
							std::cout << "hit";
						break;
				}
			}
			break;
		}
	}

}
//========================================================
// Update Function
//========================================================
//========================================================
// Purpose: constructors, they're so cool
//========================================================
void Player::Update( void )
{
	//get the new vector direction
	Vector2 vecOldVel = Vector2::Normal(m_vecOldVel);
	m_vecVel.Normalize();

	if( abs(m_vecVel.x-vecOldVel.x) <= 8 && abs(m_vecVel.y - vecOldVel.y) <= 8 )
	{
		//if the new direction is oppsosite of the old one
		float Turn = trunc(vecOldVel*m_vecVel);

		if( Turn != 0.f )
			m_vecVel = m_vecVel;

		else if( Turn == 0.f )
		{
			Vector2 vecOrigin = GetTileOrigin(GetTilex(), GetTiley());

			//check to see if the player is close enough to the origin to constitute them moving
			if( abs(vecOrigin.x - m_vecOrigin.x) <= 4 && abs(vecOrigin.y - m_vecOrigin.y) <= 4  )
			{
				SetPos(vecOrigin-Vector2(16,16));
			}

			//if they aren't close enough to the origin, return them to their old velocity
			else
				m_vecVel = m_vecOldVel;
		}
	}

	//check to see if the next tile is clear
	if( GetTileFlag( GetTilex()+(int)m_vecVel.x, GetTiley()+(int)m_vecVel.y ) & NOTWALKABLE )
	{
		//check to see if the olddirection is clear
		if( GetTileFlag( GetTilex()+(int)vecOldVel.x, GetTiley()+(int)vecOldVel.y ) & NOTWALKABLE )
			m_vecVel = Vector2(0,0);
		
		else
			m_vecVel = vecOldVel;
	}

	m_vecVel = m_vecVel*m_fSpeed;

	//update animation
	Vector2 vecAnimDir = Vector2::Normal( m_vecVel );

	if( vecAnimDir == Vector2(0, -1) )
		m_pSprPlayer->Animate( 8, 4, true );
	
	else if( vecAnimDir == Vector2(0, 1) )
		//update the animation
		m_pSprPlayer->Animate( 12, 4, true );

	else if( vecAnimDir == Vector2(-1, 0) )
		//update the animation
		m_pSprPlayer->Animate( 0, 4, true );

	else if( vecAnimDir == Vector2(1, 0) )
		//update the animation
		m_pSprPlayer->Animate( 4, 4, true );
	
	else if( vecAnimDir == Vector2(0,0) )
		m_pSprPlayer->SetCurrentFrame( 0 );


	//set the sprite position
	SetPos( GetPos()+GetVel() );
	m_pSprPlayer->SetPos( m_vecPos );
}


once again I appreciate your guy's* help

NOTE*: I use guy to refer to both genders, no offense ladies :)

This post has been edited by mastersmith98: 31 January 2009 - 08:33 PM

Was This Post Helpful? 0
  • +
  • -

#6 WolfCoder  Icon User is offline

  • Isn't a volcano just an angry hill?
  • member icon


Reputation: 784
  • View blog
  • Posts: 7,613
  • Joined: 05-May 05

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 09:04 PM

View Postmastersmith98, on 31 Jan, 2009 - 08:33 PM, said:

...

Quote

This way, the user is holding down where they want to go, and when Pac-Man can go there he will go there (which occurs every TILE_SIZE pixels moved). Don't forget to set the distance left.

I would prefer to keep it like the original pacman, allowing the player to press the key in a direction and have the pacman travel in that direction indefinitely till he hits a wall.
...


That is how you do original Pac-Man as I didn't say he stopped, but if you want to move forward and backwards across the same line in between tiles you would have an input check right after the first input check that would check if the new direction the player wants to go is the opposite only. For example, if you want to move right and you're moving left, you can do it immediately. But, if you want to move up or down, that only happens when you're aligned to the grid.

/* Normal code */
if(distLeft <= 0){input_check();distLeft = 32;}
else{move_pacman();distLeft -= time_step;}
/* Other code */
if(dir == left && key_right)dir = right;
if(dir == right && key_left)dir = left;
if(dir == up && key_down)dir = down;
if(dir == down && key_up)dir = up;


This post has been edited by WolfCoder: 31 January 2009 - 09:06 PM

Was This Post Helpful? 1
  • +
  • -

#7 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

Re: pacman collision/movement alogrithm.

Posted 31 January 2009 - 10:48 PM

View PostWolfCoder, on 31 Jan, 2009 - 08:04 PM, said:

View Postmastersmith98, on 31 Jan, 2009 - 08:33 PM, said:

...

Quote

This way, the user is holding down where they want to go, and when Pac-Man can go there he will go there (which occurs every TILE_SIZE pixels moved). Don't forget to set the distance left.

I would prefer to keep it like the original pacman, allowing the player to press the key in a direction and have the pacman travel in that direction indefinitely till he hits a wall.
...


That is how you do original Pac-Man as I didn't say he stopped, but if you want to move forward and backwards across the same line in between tiles you would have an input check right after the first input check that would check if the new direction the player wants to go is the opposite only. For example, if you want to move right and you're moving left, you can do it immediately. But, if you want to move up or down, that only happens when you're aligned to the grid.

/* Normal code */
if(distLeft <= 0){input_check();distLeft = 32;}
else{move_pacman();distLeft -= time_step;}
/* Other code */
if(dir == left && key_right)dir = right;
if(dir == right && key_left)dir = left;
if(dir == up && key_down)dir = down;
if(dir == down && key_up)dir = up;



sorry, but I don't entirely follow you. I feel kind of ashamed that I'm struggling with such an easy concept lol.
Was This Post Helpful? 0
  • +
  • -

#8 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

Re: pacman collision/movement alogrithm.

Posted 01 February 2009 - 12:04 PM

After re-reading WolfCoder's explanation for a little bit, I finally came up with a solution to fix my problem! thanks a lot wolf ;D.

here's the code I wrote. It works quite well, however the pacman's movement studders a bit since I'm setting his position every time he gets near that DstToNextTile threshold.

void HandleEvents( void )
{
	//events
	while( SDL_PollEvent( &g_Event) )
	{
		switch( g_Event.type )
		{

			case SDL_QUIT:
				g_bQuit = true;
			break;

			case SDL_KEYDOWN:
			{
				g_pPlayer->SetKey( g_Event.key.keysym.sym );

				switch( g_Event.key.keysym.sym )
				{
						case SDLK_SPACE:
							std::cout << "hit";
						break;
				}
			}
			break;
		}
	}

}
//========================================================
// Purpose: move the entity
//========================================================
void Player::MovePlayer( void )
{
	switch( m_nDirection )
	{
		case UP:
			m_vecVel = Vector2(0,-1)*m_fSpeed;
		break;

		case DOWN:
			m_vecVel = Vector2(0,1)*m_fSpeed;
		break;

		case LEFT:
			m_vecVel = Vector2(-1,0)*m_fSpeed;
		break;

		case RIGHT:
			m_vecVel = Vector2(1,0)*m_fSpeed;
		break;

		case NONE:
			m_vecVel.Zero();
		break;
	}
}

//========================================================
// Purpose: check for changes in the keys
//========================================================
void Player::CheckInput( void )
{
	switch( m_kKey )
	{
		case SDLK_UP:
			m_nDirection = UP;
		break;

		case SDLK_DOWN:
			m_nDirection = DOWN;
		break;

		case SDLK_RIGHT:
			m_nDirection = RIGHT;
		break;

		case SDLK_LEFT:
			m_nDirection = LEFT;
		break;

		//default:
			//m_nDirection = NONE;
		//break;
	}
}

//========================================================
// Purpose: update function
//========================================================
void Player::Update( void )
{

	if( m_nDstToTile <= 0 ) 
	{
		//set the position of the player to the very center of the tile
		Vector2 vecOrigin = GetTileOrigin( GetTilex(), GetTiley() );
		SetPos( vecOrigin-Vector2(16,16) );

		CheckInput();

		if( m_nDirection != NONE )
			m_nDstToTile = TILE_SIZE;
	}
	else
	{
		MovePlayer();
		m_nDstToTile -= (int)m_vecVel.Magnitude();
	}

	//check to see if the next tile is clear
	//if( GetTileFlag( GetTilex()+(int)m_vecVel.x, GetTiley()+(int)m_vecVel.y ) & NOTWALKABLE )
	//{
	//	//check to see if the olddirection is clear
	//	if( GetTileFlag( GetTilex(), GetTiley() ) & NOTWALKABLE )
	//		m_vecVel = Vector2(0,0);
	//	
	//	else
	//		m_vecVel = vecOldVel;
	//}

	//update the animations
	switch( m_nDirection )
	{
		case UP:
			m_pSprPlayer->Animate( 8, 4, true );
		break;

		case DOWN:
			m_pSprPlayer->Animate( 12, 4, true );
		break;

		case LEFT:
			m_pSprPlayer->Animate( 0, 4, true );
		break;

		case RIGHT:
			m_pSprPlayer->Animate( 4, 4, true );
		break;

		case NONE:
			m_pSprPlayer->SetCurrentFrame( 0 );
		break;
	}


	//set the sprite position
	SetPos( GetPos()+GetVel() );
	m_pSprPlayer->SetPos( m_vecPos );
}


any suggestions on how to make the pacman studder less?

btw, thanks for all the help up till now :D

EDIT: oh I forgot, the pacman can still not be centered perfectly on the tile. if it wasn't for me setting the pos of the player to the origin he wouldn't even be close to the center of the tile, most of the times.

This post has been edited by mastersmith98: 01 February 2009 - 12:07 PM

Was This Post Helpful? 0
  • +
  • -

#9 WolfCoder  Icon User is offline

  • Isn't a volcano just an angry hill?
  • member icon


Reputation: 784
  • View blog
  • Posts: 7,613
  • Joined: 05-May 05

Re: pacman collision/movement alogrithm.

Posted 01 February 2009 - 01:32 PM

I would have done this to re-align to grid once the tileDist is 0:

x = (int)(x/32);
y = (int)(y/32);
x *= 32;
y *= 32;



I let the integer chop off any fractional values and then return it back to multiples of 32. I would have to see Pac-Man studder to see what you're talking about. Your code still does more than it needs to, I can write a Pac-Man implementation without needing any sort of TileOrigin() function.

This post has been edited by WolfCoder: 01 February 2009 - 01:33 PM

Was This Post Helpful? 0
  • +
  • -

#10 mastersmith98  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 39
  • Joined: 22-December 08

Re: pacman collision/movement alogrithm.

Posted 01 February 2009 - 01:59 PM

View PostWolfCoder, on 1 Feb, 2009 - 12:32 PM, said:

I would have done this to re-align to grid once the tileDist is 0:

x = (int)(x/32);
y = (int)(y/32);
x *= 32;
y *= 32;



I let the integer chop off any fractional values and then return it back to multiples of 32. I would have to see Pac-Man studder to see what you're talking about. Your code still does more than it needs to, I can write a Pac-Man implementation without needing any sort of TileOrigin() function.

the player isn't getting misaligned because of floating point precision (even though I'm using floating point numbers, I'm only using them as integers).

the studdering happens because of realigning the player to the center of the tile (and without it the player can go off the specified grid).

EDIT: I got it to work. the problem was that the distance to the next tile wasn't TILE_SIZE(32) but TILE_SIZE-m_fSpeed(8), I needed to take into account the next movement update.

thanks a lot for your guys' help, I couldn't have done it without your comments.

This post has been edited by mastersmith98: 01 February 2009 - 07:40 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1