- Home /
Hover effect : raycasting problems with terrain height and speed
Hi everybody,
I'm a new Unity user. Here is my project (I plan to program a Wipeout clone) : http://dl.dropbox.com/u/12575338/WebPlayer.html
I have 2 problems with the hover effect :
The first problem appears when the ship is above the lower altitude of the ground : there is a small rotation around the y-axis (about 2 degrees) and the ship then returns to its starting rotation. Then resumes its small rotation, etc... When the ship in above a mountain, or is climbing, there's no problem.
Here is the relevant code :
RaycastHit hit;
for (int i = 0; i <= corners.Length - 1; i++)
{
if (Physics.Raycast(corners[i].position, -corners[i].up, out hit, hoverHeight+100))
{
if (hit.collider.gameObject.tag == "Terrain")
{
hitNormal[i] = body.transform.InverseTransformDirection(hit.normal);
if (lastNormals[i] != hitNormal[i])
{
increment = 0;
lastNormals[i] = hitNormal[i];
}
distance = hit.distance;
}
}
}
average = -(hitNormal[0] + hitNormal[1] + hitNormal[2] + hitNormal[3] + hitNormal[4]) / 5;
if (increment != 1)
{
increment += 0.03f;
}
rotation = Quaternion.Slerp(body.transform.localRotation, Quaternion.Euler(average * Mathf.Rad2Deg), increment);
rotation.y = transform.up.y*Mathf.Deg2Rad;
I set up 5 positions on my ship (stored in the array "corners[5]") from which I launch a ray down. If the ray collides with the terrain, I store the normal in my hitNormal[] array. Then I calculate the average of these normals, and I set up the rotation of my ship according to the result. After debbuging, I noticed that the normals vary a lot when the ship is above the lower altitude of the terrain. So the average varies too, and that's what make this glitch around the y-axis. And I don't understand why it varies only above the lower altitude and not higher or when climbing.
Another problem : when the ship climbs slowly, it hovers correctly along the side of the hill. But in high speed, it goes through the hill. Note that I have no Collider on my ship, and I move it in FixedUpdate() with AddForce.
Can you help me ? Thanks a lot
Hey a question to you - because math really ain't my thing but what is: rotation.y = transform.up.y*$$anonymous$$athf.Deg2Rad; doing? It appears to be converting a number between -1 and +1 to in degrees to a radian - but I'm lost on that... and it's modifying 4 dimensional Quaternion space - yikes :)
Hark ... why aren't you using the game engine? ie, just use the physics in the game engine. otherwise you're just kind of using an expensive photoshop, you know?! heh
@whydoidoit : this line is here because I noticed that my ship's y-axis was not correctly aligned after the rotation. So I modified the Quaternion's y-axis to make me the same as my Ship. And $$anonymous$$ath.Deg2Rad() because on a Quaternion, the x, y and z parts must be in radians.
@Fattie : I would like to do that, but as an Unity newbie, I have no idea of how to do that. $$anonymous$$aybe you can help me to start ?
@Harkonnen: I think this problem requires a Rigidbody
. Not my area of expertise since I don't actually need any Physics for my game x_x.
Hi Hark, look at any of the tutorials for Unity.
the main thing you do with unity is make well it is a game engine
(1) add a cube and a sphere to your scene
(2) click the button to add rigigbody (use the menus)
(3) click the button to add "collider" (use menus)
fling them around and youwill see they bounce and so on
Enjoy!
Answer by Wolfram · Jun 25, 2012 at 11:30 AM
(converted to an answer, as I looked more into your code, and more problems/suggestions arised)
Harkonnen, you cannot manipulate Quaternions this way. Well, you can, but it doesn't have your intended result, as "rotation.y" is something entirely different as the rotation around the y axis. A real quaternion is normalized over all four paramerers xyzw, the "Quaternion" class does not store its parameters in an Axis-Angle representation.
What you probably want to do is manipulate eulerAngles.y instead.
EDIT: just looked at your code more closely. Of yourse you don't have an "eulerAngles" fields, I thought we were talking about a transform. You'd somehow need to work on eulerAngles instead. And another thing, "average * Mathf.Rad2Deg" doesn't make sense either, Quaternion.Euler() expects three angles in degrees, but you are giving it the normal vector, converted to degrees, neither of which makes sense.
EDIT2: Hm, there are too many logical errors in your code, it might be better to start from scratch after reading some tutorials, or finding examples doing a simliar thing (for example, you could look into a ship/buoy floater script, which does a similar thing).
Things that will not work:
You're replacing hitNormal[i] only if there is an actual hit with a Terrain tagged object. If for some reason there is a different collider inbetween, you won't update that hitNormal, but will still use the old value for averaging.
You cannot average normal vectors by just adding them together and dividing by the number of vectors. Normalize the sum, instead of dividing.
As mentioned above, you can't feed Quaterion.Euler the values you do
And you can't manipulate Quaternions the way you do.
Instead I'd suggest the following:
Fix your code that it correctly averages all hit.normals, and only those that actually come from the current Terrain test.
Stay away from Quaternion manipulation and functions, unless you know exactly how they work, and how they are handled inside Unity.
Instead, I'd suggest to do a simple Vector3.Slerp betweeen the current transform.up and your averaged world spacenormal (not local space! so lose the InverseTransformDirection). This should smoothly orient your transform along that normal, and leave the other orientation vectors alone (well, at least the Y angle - the X angle will need to change per definition of the euler angle rotation order, as your ship changes pitch).
As the "t" value of your Slerp, use Time.deltaTime*smoothnessFactor, as you will see in 99% of all Unity Answers questions dealing with Lerp/Slerp. This will "abuse" the functionality of the Lerp (because it's no longer a real linear interpolation), but it will do the task as most people intend to.
Thank you very much for your precious advices. I will read some tutos, and start from scratch by following your advices. Thank you for spending some time. :)
wolfram, just a question : why can't I average the normals by dividing their sum by 5 ? why I have to normalize the sum ?
When adding several vectors, the resulting length is neither the sum of their individual lengths, nor that length divided by their number.
You are right that you can average vectors by using the sum divided by the number of vectors, but for directional/normal vectors, this might not necessarily be what is intended, as the resulting length is now different from your input vectors. In the case of normals, the length of the averaged vector will not be 1, unless all normals are identical.
If you feed a non-normalized normal to a shader, for example, the lighting will be wrong, unless the shader internally renormalizes the normal (which they usually don't do, for performance reasons).
In case of Vector3.Slerp, this function indeed interpolates direction separately from length, so it will work as expected even if the input vectors have different lengths. However, when using simple averaging (or Lerp, for example), the result will be skewed towards the longer vector.
So the length might be irrelevant, but you cannot generally assume that it is irrelevant.
So in your case it will probably work without normalization, since both Vector3.Slerp and probably also assignment to transform.up will be intelligent enough to deal with non-normalized vectors - in which case, you can even ignore the averaging altogether and just add all normals together.
However, to be on the safe side, and unless you know exactly how the underlying functions you are feeding your vectors to work internally, you should always normalize your directional/normal vectors.
Thank you very much. I think I should refresh my knowledge of vector algebra :)