Game Development Community

Problem with platformer wall collision when jumping

by Adam McDonald · in Torque Game Builder · 02/25/2012 (12:36 pm) · 5 replies

I have been building a platformer using the TDN Platformer Tutorial as a foundation. I have found that it is solid overall, but I have had a couple of problems with the the way it handles collision with walls when jumping.

1. If I am holding left or right against a wall, my character will frequently stop short when jumping. I thought maybe this was a physic-based problem, so I turned off all physics on both objects and it still happens.

2. If issue 1 does not happen, and the character clears the height of the wall, the character will not continue in the direction being held down. This can be avoided by simply jumping straight up and then pressing the direction in mid-air once the wall has been cleared, but forcing players to do this is not very intuitive. As a player, I would find it unacceptable.

If anyone has any ideas on how to fix these issues please let me know. I have made a lot of changes to the platformer tutorial scripts, but I have left the onUpdateVertical function essentially untouched.

function playerClass::updateVertical(%this)
{
   %yVelocity = %this.getLinearVelocityY();
   
   %this.setLinearVelocityY(5);
   %collision = %this.castCollision(0.005);
   
   %normalX = getWord(%collision, 4);
   %normalY = getWord(%collision, 5);
   
   // no collision
   if (%collision $= "")
   {
      %this.airborne = true;
      %this.setConstantForceY(100);
      %this.setLinearVelocityY(%yVelocity);
      return;
   }
   
   // collides with wall to the left
   if (%normalX == 1 && %normalY == 0)
   {
      %this.againstLeftWall = true;
      %this.setLinearVelocityX(0);
      %this.setLinearVelocityY(%yVelocity);
      return;
   }
   
   // collides with wall to the right
   if (%normalX == -1 && %normalY == 0)
   {
      %this.againstRightWall = true;
      %this.setLinearVelocityX(0);
      %this.setLinearVelocityY(%yVelocity);
      return;
   }
   
   // on ground with no wall collisions
   if (%normalX == 0 && %normalY == -1)
   {
      %this.airborne = false;
      %this.againstLeftWall = false;
      %this.againstRightWall = false;
      %this.setConstantForceY(0);
      %this.setLinearVelocityY(%yVelocity);
      return;
   }
   
   // in air and hits platform with head
   if (%normalY == 1)
   {
      %this.airborne = true;
      %this.setLinearVelocityX(0);
      %this.setConstantForceY(100);
      %this.setLinearVelocityY(%yVelocity);
      return;
   }
   
   // in case another type of collision normal was detected
   error("another collison type" SPC %normalX SPC %normalY);
   %this.airborne = false;
   %this.againstLeftWall = false;
   %this.againstRightWall = false;
   %this.setLinearVelocityY(%yVelocity);
}

#1
03/08/2012 (12:40 pm)
I know I've solved your second problem, but I'm not entirely sure what you mean by the first one. More because I don't remember running into that problem.

Anyway, the only change I did to the updateVertical function is in the "no collision" block.

You have the following:
// no collision  
   if (%collision $= "")  
   {  
      %this.airborne = true;  
      %this.setConstantForceY(100);  
      %this.setLinearVelocityY(%yVelocity);  
      return;  
   }

Mine is this:
// no collision  
   if (%collision $= "")  
   {  
      %this.againstLeftWall = false;
      %this.againstRightWall = false;
      %this.airborne = true;  
      %this.setConstantForceY(100);  
      %this.setLinearVelocityY(%yVelocity);  
      return;  
   }

I compared our functions and that was the only change in that function. I'm not sure if I have something different in my updateHorizontal function. I'll post it here for you though.

function Player::updateHorizontal(%this)
{
  if(%this.moveLeft == %this.moveRight)
  {
    %this.setLinearVelocityX(0);
    return;
  }
  if(%this.moveLeft)
  {
    if(!%this.againstLeftWall)
    {
      %this.againstRightWall = false;
      %this.setLinearVelocityX(-90);
    }
  }
  if(%this.moveRight)
  {
    if(!%this.againstRightWall)
    {
      %this.againstLeftWall = false;
      %this.setLinearVelocityX(90);
    }
  }
}

If you see anything different between yours and mine let me know, cause my curiosity is piqued.
#2
03/18/2012 (9:51 pm)
Thanks for the reply. I hadn't touched my code since you posted, but I tried what you did for no collision and it seems to have worked. Thinking back, I didn't always have the first problem (since I recall distinctly trying to solve the second problem independent of it).

My code for my character is fairly complex and convoluted based on my game design (character can change to 6 different colours and either sticks to, is repelled by, passes through or collides normally with objects of different colours depending on what colour the character is). I put the basic tutorial code in my original post to avoid having too many variables nobody would have any frame of reference for. Anyway, here's the updates horizontal and vertical with some other stuff to make it make sense.

function playerClass::updateHorizontal(%this)
{
	if($player.getLinearVelocityX() < 2)
	{
		if($player.getLinearVelocityX() > -2)
		{
			$player.setLinearVelocityX(0);
		}
	}
	if(!%this.airborne)
	{
		if(%this.moveLeft == %this.moveRight)
		{
			$player.setLinearVelocityX(0);
		}
	}
		
	if (%this.moveLeft)
	{
		if (!%this.moveRight)
		{
      		if (!%this.againstLeftWall)
      		{
				%this.againstRightWall = true;
				if(%this.control)
				{
					%this.setLinearVelocityX(%this.xMoveLeft);
				}
			}
      	}
	}
	if (%this.moveRight)
	{
		if (!%this.moveLeft)
		{
			if (!%this.againstRightWall)
			{
				%this.againstLeftWall = true;
				if(%this.control)
				{	
					%this.setLinearVelocityX(%this.xMoveRight);
				}
			}
		}
	}
	if (%this.moveUp)
	{
		if (!%this.airborne)
		{
			%this.setLinearVelocityY(-20);
		}
	}
	if (%this.moveDown)
	{
		if (!%this.airborne)
		{
			%this.setLinearVelocityY(20);
		}
	}
	if(!%this.airborne)
	{
		if($inStick)
		{
			if(!%this.airborne)
			{
				if(%this.moveUp == %this.moveDown)
				{
					$player.setLinearVelocityY(0);
				}
			}
		}
	}
}
function playerClass::updateVertical(%this)
{
	%xVelocity = %this.getLinearVelocityX();
	%yVelocity = %this.getLinearVelocityY();

	%this.setLinearVelocityY(5);
	%collision = %this.castCollision(0.004);  
	
	%normalX = getWord(%collision, 4);
	%normalY = getWord(%collision, 5);

//no collision
	if (%normalX == 0 && %normalY == 0)
   	{
		%this.airborne = %this.noColAir;
		%this.againstLeftWall = false;
		%this.againstRightWall = false;
      	%this.setConstantForceY(%this.noColConY);
		%this.setLinearVelocityY(%yVelocity);
		%this.setLinearVelocityX(%xVelocity);
      	return;
   	}
	
// collides with wall to the left
   	if (%normalX == 1 && %normalY == 0)
   	{

     	%this.againstLeftWall = true;
		%this.setLinearVelocityX(%this.xLeft);   					
		%this.setLinearVelocityY(%this.leftY);
      	return;
	}   
	
// collides with wall to the right
   	if (%normalX == -1 && %normalY == 0)
   	{
      	%this.againstRightWall = true;
      	%this.setLinearVelocityX(%this.xRight);
		%this.setLinearVelocityY(%this.rightY);
		return;
   	}
	
// on ground with no wall collisions
   	if (%normalX == 0 && %normalY == -1)
   	{
      	%this.airborne = %this.bottomAir;
      	%this.againstLeftWall = false;
      	%this.againstRightWall = false;
      	%this.setConstantForceY(%this.bottomConY);
      	%this.setLinearVelocityY(%this.bottomY);
      	return;
   	}
	
// in air and hits platform with head
   	if (%normalX == 0 && %normalY == 1)
   	{
			
		%this.airborne = %this.topAir;
      	%this.setLinearVelocityX(0);
      	%this.setConstantForceY(%this.topConY);
      	%this.setLinearVelocityY(%this.topY);
      	return;
   	}
	
}
function playerClass::onUpdate(%this)
{
	%this.updateHorizontal();
	%this.updateVertical();
	%this.onCollision();
	%this.setCurrentAnimation();
	%this.controlTimer();
	%this.resetThings();
}
function playerClass::controlTimer(%this)
{
	if(!$player.control)
	{
		%this.schedule(250, "returnControl");
	}
}
function playerClass::returnControl(%this)
{
	%this.control = true;
}
function playerClass::resetThings(%this)
{
	if(%this.control)
	{
		%this.xLeft = 0;
		%this.xRight = 0;
	}
}

The control function is used to take control from the player for a moment when they repel off an object, to prevent the user from just mashing up against it.
#3
03/21/2012 (1:07 am)
Ok, so I commented out my slightly more convoluted onUpdateHorizontal and replaced it with the one you posted and I still see the same issue. Since fixing the second issue it now occurs 100% of the time... the best way I can describe it is that the character seems to "hug" walls if you press against them. Basically stops if you're moving up or slows down if you're moving down. I want it to ignore the wall completely until it clears it.

I've been messing with this a lot, and turning off my moveRight/moveLeft functions in my onUpdateVertical when the character is touching the respective walls provides me with a slightly better result than where I started (no rubbing/hugging on walls, but once the character makes it above the wall it won't keep moving in that direction unless I release the directional key and press it again). I'm trying to wrap my head around other possible solutions, since this one feels hacky and doesn't quite work. For some reason using againstRightWall/againstLeftWall being false as a condition for moving in the respective direction doesn't help at all.
#4
04/01/2012 (12:11 am)
Ok, doesn't seem like anyone is following this thread but just in case someone runs into the same problem one day...

Basically, the variables being called in the collision functions were being set when the character actually touched the object, not when the cast collision detected the object. That was screwing things up. If I replace the variables with static numbers that fixes this issue, but causes others. What I'm going to do now is use triggers to set the variables appropriately before the character detects the object.

I really doubt this will come up for anyone else unless you have conditional collision variables like me, but you never know...
#5
07/21/2012 (10:58 am)
Adam, I'm having a similar problem, and I'm interested to see how you've perfected your platforming movement.

Basically, when you hold left or right against a wall and press jump, the player jumps higher than normal/expected. This happens specifically whn adding in the code:
// no collision
  if(%collision $= "")
  {
    %this.againstleftWall = false;
    %this.againstRightWall = false;
    %this.inAir = true;
    %this.setConstantForceY($gravity);
    %this.setLinearVelocityY(%tempY);
    return;
  }

If I leave out:
%this.againstleftWall = false;
%this.againstRightWall = false;
it will stop at the correct jump height but will not continue moving in the direction being held down.

Any ideas?

-Drew

[]I own TGB on another account, which can be verified.[]