- Home /
How to get Quaternion.FromToRotation and hit.normal to allow the Y-axis to not be set to 0
I implemented a terrain follow script that looks like this:
var fwd:Vector3 = transform.TransformDirection(Vector3.forward); var up:Vector3 = transform.TransformDirection(Vector3.up);
transform.localPosition += fwd/speedModifier;
var hit:RaycastHit;
Debug.DrawRay(transform.localPosition, -up*20, Color.cyan);
if (Physics.Raycast(transform.localPosition, -up, hit, 10)){
if ( hit.distance > 1 ){
transform.localPosition.y -= hit.distance-1;
} else if ( hit.distance < 1 ){
transform.localPosition.y += 1-hit.distance;
}
var rot:Quaternion = Quaternion.FromToRotation(Vector3.up, hit.normal);
transform.rotation = rot;
}
The issue I am running into is when setting the rotation of the transform it is always snapping the GO on the Y-axis to 0 instead of using the Y-axis rotation already applied (for example 180). I have tried a bunch of different things which either gave undesirable results or setting the Y-axis to 0.
Essentially, how can I get the hit.normal and apply it to the GO transform via Quaternion.FromToRotation and it not snap the Y of the transform?
Thanks in advance!
Answer by Jaap Kreijkamp · Dec 13, 2009 at 01:22 AM
Let's try it again, now read what you want, function below should do the trick. What it does is calculating a projection of the GO's forward vector on plane defined by normal vector of hit, then generate a rotation that looks in direction of the projection vector and has the normal vector as up vector.
var speed = 0.5;
function Update () { var fwd:Vector3 = transform.forward;
transform.position += fwd * speed * Time.deltaTime;
var hit:RaycastHit;
// instead of -Vector3.up you could use -transform.up but as hit point will jump
// when slope changes it will give jitter. That's solvable as well by working from
// a pivot point in bottom centre of object instead of centre (and to make sure
// your raycast won't be too low move start pos back by a bit using again
// transform.up as direction.
if (Physics.Raycast(transform.position, -Vector3.up, hit, 10)){
if ( hit.distance > 1 ){
transform.localPosition.y -= hit.distance-1;
} else if ( hit.distance < 1 ){
transform.localPosition.y += 1-hit.distance;
}
var proj : Vector3 = fwd - (Vector3.Dot(fwd, hit.normal)) * hit.normal;
transform.rotation = Quaternion.LookRotation(proj, hit.normal);
}
}
I've been searching ALL NIGHT for the solution to this extremely common problem. Finally found it over here, thank you very much Jaap!
Saved my evening too! Every other solution I found wasn't working for me but this one finally did the trick for setting proper direction when moving around on my planetoid.
Answer by Weblyan · Jan 07, 2017 at 08:22 PM
Adding to Jaap's Answer. I had the same problem with the y-rotation snapping when i tried to align something with the terrain.
public static void AlignTransform(Transform transform)
{
Vector3 sample = SampleNormal(transform.position);
Vector3 proj = transform.forward - (Vector3.Dot(transform.forward, sample)) * sample;
transform.rotation = Quaternion.LookRotation(proj, sample);
}
public static Vector3 SampleNormal(Vector3 position)
{
Terrain terrain = Terrain.activeTerrain;
var terrainLocalPos = position - terrain.transform.position;
var normalizedPos = new Vector2(
Mathf.InverseLerp(0f, terrain.terrainData.size.x, terrainLocalPos.x),
Mathf.InverseLerp(0f, terrain.terrainData.size.z, terrainLocalPos.z)
);
var terrainNormal = terrain.terrainData.GetInterpolatedNormal(normalizedPos.x, normalizedPos.y);
return terrainNormal;
}
This does not use a raycast to get the normal. and instead gets the normal from the terrain data.
Answer by Unityfordummies · Feb 17, 2019 at 05:16 PM
Thank you so much @Weblyan. I spent hours looking for a way to do that.