Cube being able to climb every surfaces using Raycast but cannot turn...

Hi All !

I’m developing a game on which everything is slow, you take your time to reach a point, enjoy the landscape… Semi-contemplation game on which you are a snail.

A good challenge because as a snail, you are allowed to move on almost every surfaces. I decided to start using just a cube and try to figure out the mechanics.

I’ve read a lot here and there, and came up with using raycasts. So far it works great as I can climb any sort of surface with the layer tag “ground”. Some empty objects are attached to the cube and oriented so their ray is oriented to the ground at different locations so I can calculate the average normal of the ground. Then I rotate the cube.
Here is the script and what I have.

https://drive.google.com/file/d/11Kh6iB6AKqtN3NSOKw_WwQeeVdAFFpRZ/view?usp=sharing

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GetNormalRaycast : MonoBehaviour
{
    public LayerMask mask;
    public float groundCheckLength = 1;
    public float leftCheckLength = 1;
    public float middleCheckLength = 1;
    public float rightCheckLength = 1;

    public Transform groundChecker;
    public Transform leftChecker;
    public Transform middleChecker;
    public Transform rightChecker;

    private Vector3 groundNormal;
    private Vector3 MiddleNormal;

    public float speed = 1;
    public float rotationSpeed = 50;

    // Update is called once per frame
    void Update(){
        if (Input.GetAxis("Vertical") != 0 ){

            // Ground
            //Ray ray = new Ray(transform.position, transform.up * -1.0f);
            Ray groundRay = new Ray(groundChecker.position, groundChecker.forward);
            RaycastHit groundHitInfo;

            if (Physics.Raycast(groundRay, out groundHitInfo, groundCheckLength, mask)){

                Debug.DrawLine(groundRay.origin, groundHitInfo.point, Color.red);

                Quaternion surfaceRot = Quaternion.FromToRotation (Vector3.up, groundHitInfo.normal);   
                Quaternion newRot = surfaceRot * Quaternion.AngleAxis(transform.rotation.eulerAngles.y, Vector3.up);
                groundNormal = groundHitInfo.normal;
                //transform.rotation = Quaternion.Slerp(transform.rotation, newRot, 0.1f);
                //Vector3 currentPos = transform.position;
                //Vector3 newPos = groundHitInfo.point + new Vector3(0f,0.5f,0f);
                //transform.position = Vector3.Slerp(currentPos,newPos, 0.1f);
               
            }else{

                Debug.DrawLine(groundRay.origin, groundRay.origin + groundRay.direction *groundCheckLength, Color.green);
                //groundCheckLength += groundCheckLength;
                groundNormal = new Vector3(0f,0f,0f);
               

            }
            //Middle checker
            Ray MiddleRay = new Ray(middleChecker.position, middleChecker.forward);
            RaycastHit middleHitInfo;

            if (Physics.Raycast(MiddleRay, out middleHitInfo, middleCheckLength, mask)){

                Debug.DrawLine(MiddleRay.origin, middleHitInfo.point, Color.red);

                Quaternion surfaceRot = Quaternion.FromToRotation (Vector3.up, middleHitInfo.normal);   
                Quaternion newRot = surfaceRot * Quaternion.AngleAxis(transform.rotation.eulerAngles.y, Vector3.up);
                MiddleNormal = middleHitInfo.normal;
            }else{

                Debug.DrawLine(MiddleRay.origin, MiddleRay.origin + MiddleRay.direction *middleCheckLength, Color.green);
                MiddleNormal = new Vector3(0f,0f,0f);
                //middleCheckLength += middleCheckLength;

            }

            //float horizontalData = Input.GetAxis("Horizontal");
            //Vector3 horizontalforce = new Vector3(0f, horizontalData, 0f);
            Vector3 averageNormal = ((groundNormal + MiddleNormal) / 2);
            Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, averageNormal);
            //Debug.Log("X : " + targetRotation.eulerAngles.x);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 0.08f);

            //transform.Rotate(targetRotation.eulerAngles.z,targetRotation.eulerAngles.x, 0f, Space.Self);
            transform.Translate(Vector3.forward * speed * Time.smoothDeltaTime, Space.Self);

        }

        if(Input.GetAxis("Horizontal") != 0){

           
            transform.Rotate(0,rotationSpeed * Time.deltaTime * Input.GetAxis("Horizontal"), 0);


        }

    }
}

The problem is that I can only go forward, no turns allowed. Also the cube is not “touching” the ground.
I kind of know where this happens, because I apply a rotation every time the player presses the vertical input.

Of course the cube has to be allowed to go left and right (not backward though).

I’m kinda stuck now, I thought I could somehow get the float value of the Horizontal input, map it to a min and max angle and construct a vector3 with it, then add the value to the Quaternion… but not to avail for now.
Maybe I’m not taking the problem by the right end.

If you end up having a brilliant idea for me, I would really appreciate it ^^

Have a good day !
Martin

You need to modify your directional inputs to be correctly oriented to the current surface you are walking on.

For an example of this process, check out any tutorial for walking around a globe / sphere, such as this one:

Thanks for the feedback Kurt !

Unfortunately the problem remains.

Orienting the object and make it “stick” to whatever is not the issue.
But Because I reorient my object to match the ground normal (average of many), it then also reorients the rotation on the Y axis, causing the object to face world Z axis, and not my object Z axis (forward).
In these lines I calculate the average normal and orient my object according to it.

Vector3 averageNormal = ((groundNormal + MiddleNormal) / 2);
Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, averageNormal);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 0.08f);

I guess the issue is here because there is no info about my left or right input, I simply use the normal.
I would like my cube to be able to go in the direction it is facing. Not always the world Z axis.

If any idea ?

Cheers

Now you should go look at the FirstPersonController script from the tutorial above. It’s only one line of code that transforms the rotation before driving the Rigidbody position, and it works correctly from any orientation you happen to be standing on.

Hi !
Thanks for the feedback.

It is still not working unfortunately ^^
I searched a lot and came across this issue on this tutorial, he is talking about cross product.

This is exactly the issue I’m having. I still think it is because I rotate my object along the normal I get under the object.
I can’t wrap my head around it…

Here is what I have ;
https://drive.google.com/file/d/1fv9h_JMvRcn8oxa6tQZJ_e-VWUP3iswk/view?usp=sharing

Thanks for any help !