x


How can I perform some action based on the terrain texture currently under my player?

I want trigger with texture on terrian, some area make player hurts, some area player can not pass throught, how this can be handled in Unity. Thanks.

more ▼

asked Apr 13 '10 at 08:54 AM

lzt120 gravatar image

lzt120
114 10 11 16

(comments are locked)
10|3000 characters needed characters left

4 answers: sort voted first

(see new edit below for link to example script)

You can read values from the "Mix Map" (a.k.a. Splat Map, Alpha Map, Control Texture - these are often used to refer to the same thing) using some undocumented terrain functions. The Mix Map controls the blending of each of your textures across the terrain, and is a 2D grid of values. The size of this grid is determine by the "Control Texture Resolution" setting in the terrain dialog box.

So, you'll need to convert the player's world coordinates to coordinates which represent the grid cell of the "control texture" which the player is currently within. A similar technique is described in this question, but in this case it converts it to the 2d heightmap grid:

How to translate WORLD coordinates to TERRAIN coordinates?

However instead of the heightmap size, you'll need to use the Mix Map size which can be read using the undocumented command, terrain.alphamapResolution.

So, to convert, you need to subtrack the terrain's position, divide by the terrain size, and multiply by that Mix Map Size:

var terrainData = terrain.terrainData;
var terrainPos = terrain.transform.position;
var mapX = ((player.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth;
var mapZ = ((player.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight;

Next you need to use those coordinates to sample the values which control the mix of textures at that position, using the undocumented function GetAlphamaps, like this:

var splatmapData = terrain.GetAlphamaps(x, y, width, height); 

Because the alphamap data is a 2d grid, the x & y values here are integers, as are the width & height values (which let you sample an area of the grid of any size). Because you're only interested in sampling the cell under the player, the width and height should be 1.

Now you're left with a variable (splatmapData) which contains a 3d array. To read the mix levels of each texture layer, you need something like this:

var texture1Level = splatmapData[0,0,0];  // texture layer 1
var texture2Level = splatmapData[0,0,1];  // texture layer 2
var texture3Level = splatmapData[0,0,2];  // texture layer 3
var texture4Level = splatmapData[0,0,3];  // texture layer 4

(the reason you have 0,0 at the start is because "splatmapData" could have contained a sampled area larger than 1x1 in size, so this specifies the location within the sampled area. Because our width & height was 1,1 - the sampled area is only one cell in size).

The number of lines above should match the number of texture layers you're using in your terrain. If an area is 100% covered in one texture, you should find that texture level has a value of 1.0, and the rest have a value of 0.0. If there is a blend of textures at that location, you'll find that more than one layer has a value larger than 0.0, and that they all add up to a total of 1.0.

For more detail about maniupulating the terrain textures via scripting, see these answers:
How can I automatically place grass and other details on my terrain to correspond with the splatmap?
How to automatically apply different textures on terrain based on height?

Note:
*Some of these functions are undocumented. These have now been officially added to the Unity API! Check the Terrain and TerrainData documentation.

In addition, I have written a helper script to easily detect the type of texture on a terrain at a given location, in answer to another question.

more ▼

answered Apr 13 '10 at 10:30 AM

duck gravatar image

duck ♦♦
41k 92 148 415

wow! thanks ...

Apr 13 '10 at 10:37 AM alexnode

this doesn't solve the problem of not being able to pass through certain areas though...!

Apr 13 '10 at 10:41 AM duck ♦♦

I think that using textures for collision doesn't really make sense . But for sound and changes in movement is ace ! You can do loads !!

Apr 13 '10 at 10:53 AM alexnode

Yes I think for collisions, unless it has to be procedurally determined, your best option would be to manually build invisible colliders in the correct place - either using collider primitives, or by importing a mesh built in a 3D app which contains the "walls" in the correct positions.

Apr 13 '10 at 03:46 PM duck ♦♦

By "texture layer" do you mean all the different terrain textures that can be painted, or something else? How can we tell how many texture layers we have?

Jun 28 '10 at 08:12 PM Zeroth
(comments are locked)
10|3000 characters needed characters left

After looking into it for a few hours.,I am pretty happy with this function I made.

public Texture getTerrainTextureAt( Vector3 position )
    {
       // Set up:
       Texture retval    =  new Texture();
       Vector3 TS; // terrain size
       Vector2 AS; // control texture size

       TS = Terrain.activeTerrain.terrainData.size;
       AS.x = Terrain.activeTerrain.terrainData.alphamapWidth;
       AS.y = Terrain.activeTerrain.terrainData.alphamapHeight;


       // Lookup texture we are standing on:
       int AX = (int)( ( position.x/TS.x )*AS.x + 0.5f );
       int AY = (int)( ( position.z/TS.z )*AS.y + 0.5f );
       float[,,] TerrCntrl = Terrain.activeTerrain.terrainData.GetAlphamaps(AX, AY,1 ,1);

       TerrainData TD = Terrain.activeTerrain.terrainData;

       for( int i = 0; i < TD.splatPrototypes.Length; i++ )
       {
         if( TerrCntrl[0,0,i] > .5f )
         {
          retval  =    TD.splatPrototypes[i].texture;
         }

       }


       return retval;
    }

Just use it like this.

Texture floorTexture = getTerrainTextureAt( transform.position );

Feel free to use this.

more ▼

answered Nov 07 '12 at 08:06 PM

TheKusabi gravatar image

TheKusabi
1

(comments are locked)
10|3000 characters needed characters left

Having a texture on the terrain in an area can be handled independently of taking an action when the player enters that area.

The texture can be applied as normal using the terrain editor.

To detect when the player enters these areas, you could create objects without renderers, but with colliders that you scale and place around the landscape. To make the areas impassable you simply add the colliders, which will turn the invisible. To make them passable but taking some when the player enters you can mark the colliders as Trigger and use the OnTriggerEnter, OnTriggerLeave and OnTriggerStay to take the appropriate action.

more ▼

answered Apr 13 '10 at 09:51 AM

KvanteTore gravatar image

KvanteTore
1.5k 8 15 33

Yes but is there any way to raycast on the terrain and get the current texture you are sitting on ? You could do some nice staff if you could find the name of each splat map. Like changing the footstep sound or the jump height etc. And it would be seriously quicker than adding colliders.

Apr 13 '10 at 10:17 AM alexnode

I mean yes you should add colliders and triggers for some functions but there are some nice things you could do with knowing the texture.

Apr 13 '10 at 10:33 AM alexnode
(comments are locked)
10|3000 characters needed characters left

There is a discussion here about that topic. It is interesting http://forum.unity3d.com/viewtopic.php?t=6429&highlight=ray+terrain+texture

more ▼

answered Apr 13 '10 at 10:34 AM

alexnode gravatar image

alexnode
984 27 32 49

(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x5061
x2193
x1467
x981
x26

asked: Apr 13 '10 at 08:54 AM

Seen: 3995 times

Last Updated: Nov 07 '12 at 08:06 PM