Okay, I came across this thread earlier when I faced this problem…
I solved it somehow just now. Here´s the solution I implemented:
Whenever the player is moving, I cast down two rays, one from his center and the second 0.1 units to the direction he is trying to move.
Both rays hit the ground, with that and a little geometry you can figure the slope angle. If the angle is too great (this happens when the ray cast down in front of him hits a much higher point then the one cast below him) you can just stop his movement.
A few things before I post the code for this:
1- There is a probably better solution using RaycastHit.normal.
2- I just programmed this off of the ThirdPersonController that came with Unity, in the last hour or so. This means every variable is hard coded and nothing is done to actually treat the problem (such as slide the character down).
3- I will post this now because I believe it is the most generic code I´ll have, since from now I´ll start making all kinds of checks to slide the character, make a wall jump, etc, etc…
Sooo, to stop the character from moving up very steep slopes you can get started by placing this code on your ThirdPersonControler:
//Dont move forward if there a steep slope in front of you
var hit : RaycastHit;
Physics.Raycast(transform.position, Vector3(0, -1, 0), hit);
var pointBelow = hit.point;
if(!Physics.Raycast(transform.position + moveDirection.normalized/10, Vector3(0, -1, 0), hit))
movement = Vector3(-movement.x, movement.y, -movement.z); //executing this means ray went though terrain/wall -> very steep slope -> dont move.
var pointForward = hit.point;
var heightDiff = pointForward.y - pointBelow.y;
if (heightDiff > 0)
{
if (Mathf.Atan2(heightDiff, 0.1) * Mathf.Rad2Deg > 30 IsGrounded())
movement = Vector3(-movement.x/10, movement.y, -movement.z/10);
}
This code should in the Update() function, just below the following:
// Calculate actual motion
var movement = moveDirection * moveSpeed + Vector3 (0, verticalSpeed, 0) + inAirVelocity;
movement *= Time.deltaTime;
And just above the following:
// Move the controller
var controller : CharacterController = GetComponent(CharacterController);
wallJumpContactNormal = Vector3.zero;
collisionFlags = controller.Move(movement);
Ok, now your character won´t be able to walk steep slopes (I define steep as above 30, it´s hard coded there, go ahead and change it for whatever you think is good).
The problem is that the movement will only be stopped if the character is on the ground (otherwise he would never be able to jump over or fly over those slopes either), that means he can just jump his way up a slope. To stop that you can just block the jump if the character is on a steep slope. To do that you have to place another little bit of code:
Find the function ApplyJumping() and place this code before anything else:
//Dont if on a steep slope.
var hit : RaycastHit;
Physics.Raycast(transform.position, Vector3(0, -1, 0), hit);
var pointBelow = hit.point;
if(!Physics.Raycast(transform.position + moveDirection.normalized/10, Vector3(0, -1, 0), hit))
return;
var pointForward = hit.point;
var heightDiff = pointForward.y - pointBelow.y;
if (heightDiff > 0)
{
if (Mathf.Atan2(heightDiff, 0.1) * Mathf.Rad2Deg > 30 IsGrounded())
return;
}
This will stop the char from jumping if he is standing on a slope.
Ok, this solution is far from the best you can get, it still needs a lot of work. But the work is a little more project-specific (depends on what you want to happen if you find a steep slope), so I will just post this much.
This solution works with the default ThirdPersonControler and a terrain from Unity, you don´t need any invisible colliders or anything else really.
The behavior I made is to flip the character speed and reduce it whenever he finds a steep slope, this is just so the code works “nicely” right off the box, but you need to think what it best for you and implement it.
Anyhow, this thread helped me figure out how to deal with the problem, I hope this post will help you too.
G. Otranto