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.

(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?


*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.

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.

There is a discussion here about that topic. It is interesting

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*.texture;*
  •  	}*
  •  }*
  •  return retval;*
  • }*
    Just use it like this.
    Texture floorTexture = getTerrainTextureAt( transform.position );
    Feel free to use this.