Character Controller and wall physics

Hello,

I’m working on a fairly complex character controller, and I’ve hit a bit of a snag. I’m using the built in CharacterController component and use CharacterController.Move() for movement. My problem: when the controller is walking into a wall, you can still gain (and retain) velocity. This causes two issues: if the character is walking diagonally against a wall (sliding along it [shown below in purple]) and the wall segment ends, the character’s velocity going towards the wall is retained and he will “shoot” out from it. The second is that if the character is running into a wall, he can actually get velocity [shown below in orange], meaning that if you run into a wall and decide to run backwards, there’s this sticky feeling of deccelerating before you move backwards, when in reality the ForceNormal from the wall would have removed any velocity.

My character has a fair amount on momentum, so it’s really noticeable so I can’t really just ignore it. I’ve tried a couple different ways to deal with it but they’re very rough. I’m aware using a rigidbody with Forces would probably solve this, but that brings with it a whole another slew of issues and I prefer the fine control of the CharacterController.

What I have so far is: I can get the normal of the wall he’s colliding with and Dot it with my moveDirection to get a value between 0 and 1 to shown how parallel/perpendicular the movement is. It’s not difficult to retrieve the player’s movement relative to the wall normal and then set the movement’s Z to 0 (the orange dotted line in the image). This fixes that problem. However, this effectively destroys that really nice sliding that the charactercontroller gives us: the character now sticks like glue to the wall if you’re going at it diagonally (like in the image on the right).

I’m guessing I need to somehow add my Z velocity to my X value when the controller collides with the wall, but whenever I try anything it gets all ugly. This seems like a common issue, and I was hoping someone out there has encountered it and has tried something to fix it.

Thanks for any help!

1256779--54893--$9Vf8RHZ.jpg

function OnControllerColliderHit(hit : ControllerColliderHit)
{
	lastHit = hit;
	
	if (hit.normal.y < 0.1f  hit.normal.y > -0.1f)	//check if it's "vertical" (improve this)
	{
		var yMove = moveDirection;
		yMove.y = 0.0f;
		yMove.Normalize();		

		var dot : float = Vector3.Dot(yMove, -hit.normal);	//Ranges from 0.0 to 1.0, perpendicular and parallel, respectively
		
		var tempform : GameObject = new GameObject();
		tempform.transform.rotation = Quaternion.LookRotation(-hit.normal);
		
		var moveY : Vector3 = moveDirection;
		moveY.y = 0.0f;
			
		//deal with the movement we already have	
			
		var tempmove : Vector3 = tempform.transform.InverseTransformDirection(moveY);	//movement local to wall direction
		
		//redirect movement onto the Z axis
		//this movement is pushed more to the horz based on the wall normal
		tempmove.x += tempmove.z * Mathf.Sign(tempmove.x) * (1.0f - dot);
		
		tempmove.z = 0.0f;	//destroy all movement normal to the surface
		
		tempmove = tempform.transform.TransformDirection(tempmove);	//make it global again
		
		var tempY : float = moveDirection.y;			
		moveDirection = tempmove;
		moveDirection.y = tempY;
		
		Destroy(tempform);
		
		print(moveDirection);	
	}
       
}

For those deathly curious, this is a pretty universal solution. Pretty sure if I sat down and did some math I could avoid using a dummy object, but it doesn’t really matter. Where moveDirection is your global movement.