Trying to make character rotation match the rotation of the terrain or collider

I thought that if i set up rays on all four sides of the character and compared their distances to the ground to find out if standing on a slope and rotate the character incrementally frame by frame(to make the change seamless) until the rayhits had the same distance(therefore making the character seem to be standing on flat ground even on a wall or ceiling). Since i believe i set up my gravity to work locally, I believed it should work…here’s that part(and the gravity part) of code…

    public float TiltIncrement = 0.01f;

    void ProcessMotion()
    {
        //Tranform Vector to world space
        MoveVector = transform.TransformDirection(MoveVector);

        //Normalize vector if magnitude is > 1
        if (MoveVector.magnitude > 1)
            MoveVector = Vector3.Normalize(MoveVector);

        //apply slope
        WallRun();

        //apply sliding if able
//        ApplySlide();

        //apply speed
        MoveVector *= MoveSpeed;

        //Reapply VertVelocity to MoveVector.y
        MoveVector = new Vector3(MoveVector.x, VertVelocity, MoveVector.z);

        //apply gravity
        ApplyGravity();

        //Move Character in World Space
        TP_Controller.CharController.Move(MoveVector * Time.deltaTime);
    }

    void ApplyGravity()
    {
        if (MoveVector.y > -TermVelocity)
            MoveVector = new Vector3(MoveVector.x, MoveVector.y - Gravity * Time.deltaTime, MoveVector.z);

        if (TP_Controller.CharController.isGrounded  MoveVector.y < -1)
            MoveVector = new Vector3(MoveVector.x, -1, MoveVector.z);
    }    

    void WallRun()
    {
        RaycastHit Front;
        RaycastHit Back;
        RaycastHit Left;
        RaycastHit Right;

        Physics.Raycast(transform.position + new Vector3(0, 1, 1), Vector3.down, out Front);
        Physics.Raycast(transform.position + new Vector3(0, 1, -1), Vector3.down, out Back);
        Physics.Raycast(transform.position + new Vector3(-1, 1, 0), Vector3.down, out Left);
        Physics.Raycast(transform.position + new Vector3(1, 1, 0), Vector3.down, out Right);

        do
        {
            transform.Rotate(-Vector3.right * TiltIncrement);
        } while (Front.distance < Back.distance);

        do
        {
            transform.Rotate(Vector3.right * TiltIncrement);
        } while (Front.distance > Back.distance);

        do
        {
            transform.Rotate(-Vector3.forward * TiltIncrement);
        } while (Left.distance > Right.distance);

        do
        {
            transform.Rotate(Vector3.forward * TiltIncrement);
        } while (Left.distance < Right.distance);

    }
}

While I was putting it together, the logic seemed rock solid. Sadly, the instant I hit play…Unity froze completely.:face_with_spiral_eyes: :cry: Luckily, i had saved, but I really want to have this character ability. If anyone has a better idea of how to code this, I would really appreciate the assistance. :slight_smile:

Your do-while statements apply without end. I believe at least two of them could possibly always be going.

But I have a completely different approach to suggest. There’s a level on the Locomotion System package (on Unity’s main site), which demonstrates a gravity system like you want. It does need some major revisions to some of its logic, but it’s a wonderful place to start.

Two scripts needed from it are the Character Motor and the Player Controls or whatever it’s called. Everything else is unnecessary for the effect.

In case you care to know what revisions I meant, gravity always applies to the character’s “down” direction, and then the character is rotated to fit the planet’s surface. The problem is that if the character walks off a ledge, the system keeps applying gravity to his “down”, which then points to endless space.
Instead, gravity should be applied between the character and the planet themselves. Raycast toward the last gravity “down” direction. If there’s no surface, raycast again to the planet’s center, and apply gravity based on the normal that the raycast hits. This ensures that it will always work.

I think you want to check if the charakter move or rotate? So then why do you not compare his rotate or position with the rotation or position of the last frame?

I think something like that:

var lastrot = Vector3.zero;
var lastpos = Vector3.zero;

function update() {
if(transform.rotation != lastrot||transform.position != lastpos) {
Debug.Log("Charakter moved on or rotated");
}
lastrot = transform.rotation;
lastpos = transform.position;
}

realm_1

What exactly do you mean? I believed that by constantly adjusting the rotation would level out all of the raycasts to simulate a constantly adapting character…

| | | | here is a character() on a flat surface,
| | | | with all raycast returning same distance
----

| | | | Here the one raycast would return
| | | / a distance shorter than it’s
—*/ counterpart on the reverse side

_ _ _ | Hence the character should rotate
_ _ _*| until the raycasts tell it that
_ _ _ | is on flat ground

Also, If you haven’t noticed, I’m newish to this, I’ve just been thoroughly going though 3dBuzz vids and attempting to apply that info to a custom function, a self-test if you will. Sooooo, I don’t completely grasp what you meant by…

Could you elaborate plz, post a bit of code possibly(don’t just give me the answers,but point out where to make changes) Thanks so much for your help. :slight_smile:

http://unity3d.com/support/resources/unity-extensions/locomotion-ik
^Download this, and load up the level with planetoid gravity. There will be two scripts on the character; a character motor, and a Player Input script. They are the only two necessary for this.

Basically, a character with this motor is based on a rigidbody instead of a character controller. If you read through the script, you’ll find that it flawlessly converts your directional input into what direction the character is facing/tilted.

What this system does, though, is use a single raycast in the character’s “down” direction. The character is then turned to match the planet, and gravity is applied to the character’s “down” direction.

But this is bad. If there is nothing below the character (square planet or something), he won’t rotate, and gravity will send him flying in some odd direction.

Find the lines of code that make that happen, and see if you can understand how that happens.

Next, the fix:
Instead of raycasting “down”… Keep track of the planet the character is walking on. This time, “down” isn’t based on the character, but the surface of the planet.
First, raycast from the character to the planet.

The raycast will hit a surface (normal). “Down” is not based on the character’s rotation, but this surface. (Yes, only one raycast is needed.)

Also, the planet you’re keeping track of becomes whatever planet you’ve hit. Then, if your planet is made from multiple pieces, you’ll always be keeping track of the right planet.

Rotate the character to match this surface, and apply gravity in the surface’s “down” direction.

This is as simply as I can explain it. If you have any more trouble, you could even try waiting until I release my own character control system based on this concept. :stuck_out_tongue: