- Home /
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?
Answer by Tomer-Barkan · Oct 29, 2013 at 05:54 AM
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
}
}
Thanks a lot, I changed the OnCollision part to work with the raycast I'm using but there is still one error:
error CS1061: Type UnityEngine.Transform' does not contain a definition for
y' and no extension method y' of type
UnityEngine.Transform' could be found (are you missing a using directive or an assembly reference?)
Here is the code I am using:
private Vector3 targetRotation;
private bool rotating = false;
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;
}
}
if (hit.transform) // Hit collider
{
rotating = true;
targetRotation = hit.transform.right;
targetRotation.y = transform.y; // set target Y rotation to current Y
}
}
Apologies about the formatting, I had to copy and paste the code snippets together.
I changed transform.y to transform.position.y... line 18 in your code.
Ok, there's no errors now thanks a lot. The rotation works but for some reason the rotation is not affecting only the Y axis. The character has a large rotation offset around (what looks like) the X axis when the rotation is finished. Any ideas why this is happening?
The X rotation should be 0 after the rotation finishes. If it started with a non-0 rotation, then indeed the rotation will change to 0.
So you should be looking directly forward, not up or down at all... Is this not the case or is this not what you want?
Answer by robertbu · Oct 29, 2013 at 05:42 AM
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);
Thanks for that, I had to edit your code to fix a few things but there are still a few errors:
error CS1061: Type UnityEngine.Transform' does not contain a definition for
y' and no extension method y' of type
UnityEngine.Transform' could be found (are you missing a using directive or an assembly reference?)
error CS0117: UnityEngine.Quaternion' does not contain a definition for
LookAt'
error CS1502: The best overloaded method match for UnityEngine.Quaternion.RotateTowards(UnityEngine.Quaternion, UnityEngine.Quaternion, float)' has some invalid arguments error CS1503: Argument
#2' cannot convert object' expression to type
UnityEngine.Quaternion'
And here is what the code looks like now:
var v3 = hit.transform.right;
v3.y = transform.y;
var q = Quaternion.LookAt(v3);
transform.rotation = Quaternion.RotateTowards(transform.rotation, q, 1.2f * Time.deltaTime);
Sorry, I tired and mixed up things a bit. Code above fixed. Since you are using a Vector for a look direction, you don't need to bring the 'Y' down. You need to bring the 'y' down when you are looking at a point. @tbkn brings up a good point. Given what you are doing, you don't need to calculate q each frame unless your system is dynamic.
No problems at all. I tried using your new code, but it doesn't seem to do anything. I put everything inside Update into the "if" statement that checks for the raycast hit but the rotation stays the same whether the raycast hits or not. Any ideas?
'speed' is degrees per second, so it should be a larger number...try 90. Put a Debug.Log() statement inside your if to verify the hit. As a test you can just assign the rotation.
transform.rotation = q;