How to continuously align a plane to the slope/angle of the terrain?

Hello,

I have a gameobject (actually a canvas element with a RectTransform) that I want to align (it’s rotation) every update frame to the slope or angle of the terrain at the given position. It is a simple square “plane” with an image on it.

I’m having trouble pinpointing what values need to be plugged into what functions. I do NOT want to use raycasts. So I am wanting to use the “GetInterpolatedNormal” function within the terrain data itself. I’m assuming that is the best function to use (for performance)?

Here is my code I’m using so far (it’s not much yet):

var terrainAngle : Vector3 = Terrain.activeTerrain.terrainData.GetInterpolatedNormal(thisTransform.position.x,
                            thisTransform.position.y);
           
            imageSelectionCircleRectTransform.Rotation = Quaternion.Euler(terrainAngle);

I know that the parameters plugged into “GetInterpolateNormal” are supposed to be normalized between 0 and 1. Which…I’m not currently doing in that code…but set that aside. I also might need to adjust for the scale or size of my terrain, but I have no clue when or where I would need to do that, if I do at all? Do I need to calculate an angle after I get the normal at the location? Do I need to multiply something by a cosine or sine? I’m just not sure.

The code I posted above essentially does nothing right now. Thanks for any help.

Yes, you will need to normalize the values first. Normalizing a value is basically currentValue / maxValue. To do this, use the terrainData.size as well as the world position of the terrain. Calculate the local position of the player to the terrain, divide the localPosition X and Z by the terrainData.size X and Z, then call the GetInterpolatedNormal function. Something like :

Vector3 terrainPos = [PSEUDOCODE] terrain.transform.position;
Vector3 playerLocalPosToTerrain = thisTransform.position - terrainPos; // or even terrain.transform.InverseTransformPoint( thisTransform.position );
float normalX = playerLocalPosToTerrain.x / terrainData.size.x;
float normalZ = playerLocalPosToTerrain.z / terrainData.size.z;
Vector3 terrainNormal = terrain.terrainData.GetInterpolatedNormal( normalX, normalZ );

ref links :

disclaimer : totally untested theory :slight_smile:

1 Like

I’ll give it a shot Alucardj, thanks :slight_smile:

Actually, there’s a function on raycast to get the normal of the hit surface - then you just need to “RotateTowards” with the local up direction - here’s some messing around I did that employs raycasting to keep the ball’s robot “head”/“frame” aligned to the ground under it:

https://vimeo.com/110492478

http://docs.unity3d.com/ScriptReference/RaycastHit-normal.html

1 Like

That’s pretty cool bloomingdedalus. Very nice.

I was hoping to avoid raycasting though, because I’m going to be doing this for a lot of objects, and fairly often. I’m not ruling it out though. If I can’t get the terrain data method to work, I’ll probably do raycasting then. What you suggested is actually a pretty clever way of doing it in my opinion. Thanks for sharing it.