- Home /
How can I stop a ball from rolling down while moving across a slope?
I have a simple rolling ball setup. However, I would like my ball to not roll down while moving along a slope's surface. For illustration: 
The green arrow shows the path the ball takes when it is not controlled by the user
The blue arrow shows the path that I want the ball to take when it is moved along the surface
The yellow arrow shows the approximate path that the ball actually takes when moved in the direction of the blue arrow
How can I get the ball to follow the blue path when it is strictly told to move in that direction? (I need this to work at many different angles so freezing the rigid body's rotation is out of the question)
I'm not sure if this will help much but here is the code to make the ball move:
void FixedUpdate()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
cam.transform.Rotate(Vector3.right * -mouseY * Time.deltaTime * 500);
player.transform.Rotate(Vector3.up * mouseX * Time.deltaTime * 500);
if (Input.GetKey(KeyCode.LeftShift))
{
speed = 10;
}
else
{
speed = 5;
}
rigidbody.angularVelocity = (cam.transform.right * (vertical * speed)) + (player.transform.forward * (-horizontal * speed));
}
Answer by tyruji · Jul 26, 2021 at 06:17 PM
The reason why your ball is sliding down the slope is because of gravity. What you could do is stimulate your own gravity - you'd get the closest surface and its normal and then apply the gravity relative to it. Here some example code:
private void ApplyGravity( float gravity_mag, float checkRadius, int layerMask )
{
Vector3 gravityDir;
Vector3 pos = player.transform.position;
Collider closest_col = null;
float closest_dist = float.maxValue;
foreach( var col in Physics.OverlapSphere( checkRadius, layerMask,
QueryTriggerInteraction.Ignore) )
{
float new_dist = ( col.transform.position-pos ).sqrMagnitude;
if( new_dist >= closest_dist ) continue;
closest_dist = new_dist;
closest_col = col;
} // sort through hit colliders and get the closest one
if( closest_col == null ) //didn't hit anything, apply default gravity
{
rigidBody.AddForce( Vector3.down*gravity_mag, ForceMode.Acceleration );
return;
}
// now raycast to get the surface's normal
var ray_dir = ( closest_col.transform.position-pos ).normalized;
Physics.Raycast( pos, ray_dir, out RaycastHit hit, checkRadius, layerMask,
QueryTriggerInteraction.Ignore );
rigidBody.AddForce( -hit.normal*gravity_mag, ForceMode.Acceleration );
// and add force in the opposite direction of the surface's normal
}
Sorry, if the code is unclear or has any mistakes, I'm not on a computer. Hope it gives you the idea, good luck!
Thank you so much for helping me with this problem! My sphere now works as expected and I couldn't have done it without you. :)
For those interested, here is the new code for the locomotion sphere:
//Get inputs
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
//Looking around
cam.transform.Rotate(Vector3.right * -mouseY * Time.deltaTime * 500);
player.transform.Rotate(Vector3.up * mouseX * Time.deltaTime * 500);
//Running
if (Input.GetKey(KeyCode.LeftShift))
{
speed = 10;
}
else
{
speed = 5;
}
//Rotate locosphere
if(Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 0.25f))
{
rigidBody.AddForce(-hit.normal * 9.81f, ForceMode.Acceleration);
}
else
{
rigidBody.AddForce(Vector3.down * 9.81f, ForceMode.Acceleration);
}
rigidBody.angularVelocity = (cam.transform.right * (vertical * speed)) + (player.transform.forward * (-horizontal * speed));
Your answer