solid rectangle collision

The Partridge Family were neither partridges nor a family. Discuss.
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Post by albinopapa » September 19th, 2020, 10:16 am

Man I miss this project actually.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

ernie_avina
Posts: 8
Joined: September 5th, 2020, 1:01 am

Re: solid rectangle collision

Post by ernie_avina » September 20th, 2020, 12:26 am

alright i give up thanks alot. i appreciate it. i just dont understand it at all at this point i dont even know what Vec2 is or the ?
i guess ill just wait until im better at coding its just that it seems a lot easier than i thought. and also there is no velocity im moving the square with WASD.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Post by albinopapa » September 20th, 2020, 3:49 am

Vec2 is just a struct that holds an X and a Y.
A struct is just a way of grouping related data together.

The Vec2 in this case represents a 2D mathematical vector.

While not a true velocity, moving the square using WASD does give the square a direction and the amount you move the square each frame is your speed. So combining those two you get velocity, you just need to keep track of it's previous position before moving it then subtracting the current position and it's previous position gives you it's velocity over the previous frame. Then you can use the steps I provided.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Post by albinopapa » September 20th, 2020, 3:53 am

Oh, the ? is half of an operator called the ternary operator.

It's used like this:

result = condition ? condition_true : condition_false;

It's short hand for if/else and can be used like so:
int a = 2;
int b = 3;
int max_ab = a>b ? a : b;

Where using if/else would be

Code: Select all

if( a > b )
    max_ab = a;
else
    max_ab = b;
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Post by albinopapa » September 20th, 2020, 4:52 am

Code: Select all

// This is how to define a struct
struct CollisionResult{
	// To handle position and velocity correction, you'll need to 
	// return both.  A struct or class is the only way of doing 
	// such a thing using the return statement
	int xPosition = 0, yPosition = 0;
	int xVelocity = 0, yVelocity = 0;
};

// For simplicity, here's another struct to store the necessary elements for the
// HandleCollision() function
struct PhysicsProperties{
	int xPosition = 0, yPosition = 0;  // The position of the object
	int xVelocity = 0, yVelocity = 0;  // The velocity of the object
	int width = 0, height = 0;         // The dimensions of the object
};

void Game::Update(){
	int xPrevPosition = xSquarePosition;
	int yPrevPosition = ySquarePosition;

	// handle keyboard input
	if( /* w pressed */ ){ /*move square up*/ }
	else if( /* s pressed */ ){/*move square down*/}
	if( /* d pressed */ ){/*move square right*/}
	else if( /* a pressed */ ) {/*move square left*/}

	if( IsColliding( xSquarePosition, ySquarePosition, squareWidth, squareHeight, 
		xWallPosition, yWallPosition, wallWidth, wallHeight )
	{
		PhysicsProperties squareProps;
		squareProps.xPosition = xSquarePosition;
		squareProps.yPosition = ySquarePosition;
		squareProps.xVelocity = xSquarePosition - xPrevPosition;
		squareProps.yVelocity = ySquarePosition - yPrevPosition;
		squareProps.width = squareWidth;
		squareProps.height = squareHeight;

		PhysicsProperties wallProps;
		wallProps.xPosition = xWallPosition;
		wallProps.yPosition = yWallPosition;
		wallProps.xVelocity = 0;
		wallProps.yVelocity = 0;
		wallProps.width = wallWidth;
		wallProps.height = wallHeight;

		CollisionResult result = HandleCollision( squareProps, wallProps );
    
		// Once we have the corrected results, we need to adjust the squares position
		xSquarePosition = result.xPosition;
		ySquarePosition = result.yPosition;
	}
}

CollisitionResult HandleCollision( PhysicsProperties const& ObjectA, PhysicsProperties const& ObjectB )noexcept
{
	// Let's assume here that ObjectA are the square's properties and
	// ObjectB are the wall's properties
	CollisionResult result;
	int xPenetration = 0;
        int yPenetration = 0;
        
	if( ObjectA.yVelocity == 0 )
	{
		// Moving horizontal
		if( ObjectA.xVelocity < 0 )
		{
			// square left, wall right
			result.xPosition = ObjectA.xPosition + ( ( ObjectB.xPosition + ObjectB.width ) - ObjectA.xPosition );
			result.xVelocity = 0;
		}
		else if( ObjectA.xVelocity > 0 )
		{
			// square right, wall left
			result.xPosition = ObjectA.xPosition + ( ObjectB.xPosition - ( ObjectA.xPosition + ObjectA.width ) );
			result.xVelocity = 0;
		}
	}
	else
	{
		if( ObjectA.xVelocity < 0 )
		{
			if( ObjectA.yVelocity > 0 )
			{
				// Heading left and down
				xPenetration = ( ObjectB.xPosition + ObjectB.width ) - ObjectA.xPosition;
				yPenetration = ( ObjectA.yPosition + ObjectA.height ) - ObjectB.yPosition;
				if( penetration.x * velocity.y > penetration.y * -velocity.x )
				{
					result.yPosition = ObjectA.yPosition - yPenetration;
					result.yVelocity = 0;
				}
				else
				{
					result.xPosition = ObjectA.xPosition + xPenetration;
					result.xVelocity = 0;
				}
			}
			else
			{
				// Heading left and up
				xPenetration = ( ObjectB.xPosition + ObjectB.width ) - ObjectA.xPosition;
				yPenetration = ( OjbectB.yPosition + ObjectB.height ) - ObjectA.yPosition;

				if( penetration.x * -velocity.y > penetration.y * -velocity.x )
				{
					result.yPosition = ObjectA.yPosition + yPenetration;
					result.yVelocity = 0;
				}
				else
				{
					result.xPosition = ObjectA.xPosition + xPenetration;
					result.xVelocity = 0;
				}
			}
		}
		else if( ObjectA.xVelocity > 0 )
		{
			if( ObjectA.yVelocity > 0 )
			{
				// Heading right and down
				xPenetration = ObjectA.xPosition + ObjectA.width - ObjectB.xPosition;
				yPenetration = ObjectA.yPosition + ObjectA.height - ObjectB.yPosition;
				if( xPenetration * ObjectA.yVelocity > yPenetration * ObjectA.xVelocity )
				{
					// square bottom, wall top
					result.yPosition = ObjectA.yPosition - yPenetration;
					result.yVelocity = 0;
				}
				else
				{
					// square right, wall left
					result.xPosition = ObjectA.xPosition - xPenetration;
					result.xVelocity = 0;
				}
			}
			else if( ObjectA.yVelocity < 0 )
			{
				// Heading right and up
				xPenetration = ( ObjectA.xPosition + ObjectA.width ) - ObjectB.xPosition;
				yPenetration = ( ObjectB.xPosition + ObjectB.height ) - ObjectA.yPosition;
				if( xPenetration * -ObjectA.yVelocity > yPenetration * ObjectA.xVelocity )
				{
					// square top, wall bottom
					result.yPosition = ObjectA.yPosition + yPenetration;
					result.yVelocity = 0;
				}
				else
				{
					// square right, wall left
					result.xPosition = ObjectA.xPosition - xPenetration;
					result.xVvelocity = 0;
				}
			}
		}
		else
		{
			// Moving vertical
			if( ObjectA.yVelocity < 0.f )
			{
				// hero top, wall bottom
				result.yPosition = ObjectA.yPosition + ( ( ObjectB.yPosition + ObjectB.height ) - ObjectA.yPosition );
				result.yVelocity = 0;
			}
			else
			{
				// hero bottom, wall top
				result.yPosition = ObjectA.yPosition + ( ObjectB.yPosition - ( ObjectA.yPosition + ObjectA.height ) );
				velocity.y = 0;
			}
		}
	}
}
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

ernie_avina
Posts: 8
Joined: September 5th, 2020, 1:01 am

Re: solid rectangle collision

Post by ernie_avina » September 22nd, 2020, 7:47 am

thanks for that last post i dont even know what a struct either lol. i dont wanna discredit your help but i think i figured it out almost so what i did was make a bool for detection (isColliding) this is just a bool variable that holds the result for the collision detection ( IsColliding() ). then what i did is once the isColliding returns true (meaning it hit the side of the wall) i set the player position to the wall thats colliding. but now the issue is when i press another key since isColliding is still true it jumps to that corresponding side if the wall. i found out that when i move the player to the wall sometimes itll be placed 1 pixel from the wall preventing the collison, but sometime itll place the player on the wall causing a collision which means when i try to press the oposite key it snaps to the other side completely going throught the wall.

Code: Select all

 void Game::UpdateModel()
{
	if (wnd.kbd.KeyIsPressed('W'))
	{
		if (isColliding)
		{
			playerY = (wallY + wallHeight) + 1;
		}
		else if (!isColliding)
		{
			playerY -= 3;
		}
	}
	if (wnd.kbd.KeyIsPressed('A'))
	{
		if (isColliding)
		{
			playerX = (wallX + wallWidth) + 1;
		}
		else if (!isColliding)
		{
			playerX -= 3;
		}
	}
	if (wnd.kbd.KeyIsPressed('S'))
	{
		if (isColliding)
		{
			playerY = (wallY - playerHeight) - 1;
		}
		else if (!isColliding)
		{
			playerY += 3;
		}
	}
	if (wnd.kbd.KeyIsPressed('D'))
	{
		if (isColliding)
		{
			playerX = (wallX - playerWidth) - 1;
		}
		else if(!isColliding)
		{
			playerX += 3;
		}
	}
	playerX = ClampScreenX(playerX, playerWidth);
	playerY = ClampScreenY(playerY, playerHeight);
	isColliding = IsColliding(playerX, playerY, playerWidth, playerHeight, wallX, wallY, wallWidth, wallHeight);
}

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: solid rectangle collision

Post by albinopapa » September 22nd, 2020, 5:13 pm

This may or may not help your situation, but it's worth a shot:

Code: Select all

 void Game::UpdateModel()
{
	int vx = 0;
	int vy = 0;
	if (wnd.kbd.KeyIsPressed('W'))
	{
		vy = -3;
	}
	else if (wnd.kbd.KeyIsPressed('S'))
	{
		vy = 3;
	}
	if (wnd.kbd.KeyIsPressed('A'))
	{
		vx = -3;
	}
	else if (wnd.kbd.KeyIsPressed('D'))
	{
		vx = 3;
	}

	playerX += vx;
	playerY += vy;
	playerX = ClampScreenX(playerX, playerWidth);
	playerY = ClampScreenY(playerY, playerHeight);
	isColliding = IsColliding(playerX, playerY, playerWidth, playerHeight, wallX, wallY, wallWidth, wallHeight);

	if (isColliding)
	{
		if( vy < 0 )
			playerY = (wallY + wallHeight) + 1;
		else if( vy > 0 )
			playerY = (wallY - playerHeight) - 1;
		if( vx < 0 )
			playerX = (wallX + wallWidth) + 1;
		else if( vx > 0 )
			playerX = (wallX - playerWidth) - 1;
	}
}
The main differences are updating the position first using the direction, testing for collision AFTER moving and the if else for the two axes.
Without the if else for each axis, pressing 'up' and 'down' ( w,s ) means both are true and both blocks are executed. So moving into the wall from the top, then pressing 'W' means that the moving up block is run and moves the player to the bottom.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply