How can I rotate towards/look at a specific axis of a collided object?

I am creating some control functionality for a “tightrope” (grey box below). When the character is on the rope (colliding via raycast, the yellow line in the diagram) I would like the character’s transform.forward (blue) to smoothly rotate on the Y axis towards the rope’s transform.right (red), so that the character’s forward direction is aligned with the direction in which the rope is going. This allows a player to navigate across the rope using only the “forward” key without needing to use the “left” and “right” keys to adjust their position so they don’t fall off the rope. Since the slope of the rope is irrelevant, the rotation needs to be only on the Y axis.

I have tried using the following 2 ways:

transform.rotation = Quaternion.RotateTowards(transform.rotation, hit.transform.rotation, 1.0f);
transform.LookAt(hit.transform.right, Vector3.up);

The first way does not seem to do anything. The second, transform.LookAt does rotate the player, but affects all three axes not only the Y and its speed cannot be controlled, it simply snaps to the rotation. I’m not sure if LookAt is the best way to do this or if there is a better alternative. Any suggestions?

The typical way to solve this problem is to manipulate the point the character looks at so that the point has the same ‘Y’ value as the character:

var q = Quaternion.LookRotation(hit.transform.right);
// and this get executed each frame in Update()
transform.rotation = Quaternion.RotateTowards(transform.rotation, q, speed * Time.deltaTime);

First, you’d need to store the target direction since you want this done over more than one frame.

Then, you can use Vector3.RotateTowards() to slowly rotate the transform.forward towards your target. If you don’t care about height, you can set y to be the same as the character’s.

private Vector3 targetRotation;
private bool rotating = false;

public void Update() {
    if (rotating) {
        Vector3 newForward = Vector3.RotateTowards(transform.forward, targetRotation, 1.0f * Time.deltaTime, 0);
        transform.forward = newForward;
        if (newForward.normalized == targetRotation.normalized) {
            rotating = false;
        }
    }
}

public void OnCollisionEnter(Collision collision) {
    if (collision.collider.tag == "Rope") { // check if collided with rope
        rotating = true;
        targetRotation = collision.collider.transform.right;
        targetRotation.y = transform.position.y; // set target Y rotation to current Y
    }
}