Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
6
Question by Peter Bjrklund · Nov 17, 2010 at 10:00 AM · textureterraindetectreadsplat

Terrain with multiple splat textures - how can I detect which kind is under my character's location?

How can you detect if a character walk on for example grass, gravel or sand? Currently it seems that you can only assign one physics material or tag for one big terrain?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

6 Replies

· Add your reply
  • Sort: 
avatar image
25
Best Answer

Answer by duck · Nov 17, 2010 at 10:12 AM

This is slightly trickier than it might initially seem - partly because any given location on the terrain can be a mixture of two or more types of terrain, and partly because the data used to store this information is relatively complex.

Briefly, you need to read Alpha Map data from the terrain class using the GetAlphaMaps function. The alphamap data is a grid of values spread over the area of the terrain, and each entry in the grid contains the values used to "mix" each of the different terrain textures, so more specifically you need to calculate which grid cell your player is in, then read the mix values for that grid cell.

I've written about this in more detail here, however I'm tempted to wrap this up quickly into a helper function which can be used without getting knee deep in the terrain API :)

-- EDIT --

Here you go. Add the large script below in as a new C# script in your project. Name the file "TerrainSurface".

You'll then be able to use these helper functions to read either the mix values of textures, or just get the most dominant texture index at a given world position. Eg:

function Update() {
    var surfaceIndex = TerrainSurface.GetMainTexture(transform.position);
}

Your "surfaceIndex" will now contain an integer denoting the zero-based-index of the texture which is the most dominant texture at that location. If you have 4 textures added to your terrain, the value will be one of 0,1,2 or 3.

Alternatively you can read the actual mix of textures, like this:

function Update() {
    var surfaceMix = TerrainSurface.GetTextureMix(transform.position);
}

Your "surfaceMix" will contain an array of floating point values, denoting the relative mix of each texture at that location. For example, if you have 4 textures, and the 4th texture is "Mud", you could use this to determine how muddy the current location is, like this: (remember, the indices are zero-based, so the 4th texture has an index of 3)

function Update() {
    var surfaceMix = TerrainSurface.GetTextureMix(transform.position);
    var muddiness = surfaceMix[3];
}


And below is the main helper script. Add this in as a new C# script in your project. Name the file "TerrainSurface".

You can use the functions from C# scripts straight off. If you want to use these function from other Javascript scripts (as in the examples above), you need to put the script in a folder that gives it an earlier compilation pass. To do this, make a folder in your Project called "Plugins" and put the C# TerrainSurface script in there.

Enjoy!

// -- TerrainSurface.cs --

using UnityEngine; using System.Collections;

public class TerrainSurface {

 public static float[] GetTextureMix(Vector3 worldPos) {

     // returns an array containing the relative mix of textures
     // on the main terrain at this world position.

     // The number of values in the array will equal the number
     // of textures added to the terrain.

     Terrain terrain = Terrain.activeTerrain;
     TerrainData terrainData = terrain.terrainData;
     Vector3 terrainPos = terrain.transform.position;

     // calculate which splat map cell the worldPos falls within (ignoring y)
     int mapX = (int)(((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
     int mapZ = (int)(((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);

     // get the splat data for this cell as a 1x1xN 3d array (where N = number of textures)
     float[,,] splatmapData = terrainData.GetAlphamaps(mapX,mapZ,1,1);

     // extract the 3D array data to a 1D array:
     float[] cellMix = new float[splatmapData.GetUpperBound(2)+1];
     for (int n=0; n<cellMix.Length; ++n)
     {
         cellMix[n] = splatmapData[0,0,n];    
     }

     return cellMix;        

 }

 public static int GetMainTexture(Vector3 worldPos) {

     // returns the zero-based index of the most dominant texture
     // on the main terrain at this world position.

     float[] mix = GetTextureMix(worldPos);


     float maxMix = 0;
     int maxIndex = 0;

     // loop through each mix value and find the maximum
     for (int n=0; n<mix.Length; ++n)
     {
         if (mix[n] > maxMix)
         {
             maxIndex = n;
             maxMix = mix[n];
         }
     }

     return maxIndex;

 }

}

Comment
Add comment · Show 8 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image denewbie · Nov 17, 2010 at 10:17 AM 0
Share

This is how I would do it too. Tough one thing you ll have to note is GetAlpha$$anonymous$$aps and all its class of functions aren't so-called official scripts so if changes come about in further versions of unity they aren't guaranteed to work. Sometimes, I have left over materials in my terrain on editor mode that I created at run-time dynamically so be sure to check after use.

avatar image denewbie · Nov 17, 2010 at 10:21 AM 0
Share

1 more thing. To easily see what functions you have available in the API could I suggest that you view your scripts via Visual Studios. That way, these scripts can also be displayed when you press "." so you wont have a hard time figuring out whats available and stuff...

avatar image denewbie · Nov 17, 2010 at 10:33 AM 0
Share

Oh 1 more thing to add. IF you re going for large scale and real terrain data you might want to look at this concept we call shapefiles: http://en.wikipedia.org/wiki/Shapefile

avatar image duck ♦♦ · Nov 17, 2010 at 11:22 AM 0
Share

Updated with helper script & examples!

avatar image DocSWAB · Nov 19, 2010 at 10:07 PM 2
Share

@denewbie: Alpha map reading is now officially exposed in Unity 3.0

Show more comments
avatar image
1

Answer by Martian-Games · Apr 15, 2012 at 08:08 AM

Here is my JavaScript translation of the same cs code: Thanks Ben! (works splendidly) !

// -- TerrainSurface.js -- // // Ben Pitt // // JS translation by MartianGames :) //

public class TerrainSurface {

 public static function GetTextureMix(worldPos:Vector3) {
     // returns an array containing the relative mix of textures
     // on the main terrain at this world position.

     // The number of values in the array will equal the number
     // of textures added to the terrain.

     var terrain:Terrain = Terrain.activeTerrain;
     var terrainData:TerrainData = terrain.terrainData;
     var terrainPos:Vector3 = terrain.transform.position;

     // calculate which splat map cell the worldPos falls within (ignoring y)
     var mapX:int = (((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
     var mapZ:int = (((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);

     // get the splat data for this cell as a 1x1xN 3d array (where N = number of textures)
     var splatmapData = terrainData.GetAlphamaps(mapX,mapZ,1,1);

     // extract the 3D array data to a 1D array:
     var cellMix = new float[splatmapData.GetUpperBound(2)+1];
     for (var n=0; n<cellMix.Length; ++n)
     {
         cellMix[n] = splatmapData[0,0,n];    
     }

     return cellMix;        

 }

 public static function GetMainTexture(worldPos:Vector3) {

     // returns the zero-based index of the most dominant texture
     // on the main terrain at this world position.

     var mix = GetTextureMix(worldPos);


     var maxMix = 0f;
     var maxIndex = 0;

     // loop through each mix value and find the maximum
     for (var n=0; n<mix.Length; ++n)
     {
         if (mix[n] > maxMix)
         {
             maxIndex = n;
             maxMix = mix[n];
         }
     }

     return maxIndex;

 }

}

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image lmtm · Sep 25, 2013 at 09:21 AM 0
Share

Hi, it is very usefull script. I would like to use it for my scene where I have 4 terrains.

This script is working perfectly on $$anonymous$$AIN TERRAIN.(terrain = Terrain.activeTerrain;)

Can You help me to udpate this script to work with 4 terrain?

Thanks.

$$anonymous$$ilos.

avatar image
0

Answer by oliver-jones · Nov 17, 2010 at 10:04 AM

I would suggest that you place a collider or something like that over, for example grass. Then give that collider a tag of Grass, or a name of Grass.

Then in your script you have something like:

    function OnCollisionEnter(collisionInfo : Collision) {
        print("Now Touching: " + collisionInfo.transform.name);
        PlayGrass = true;
    }

And place that within your Player

EDIT---

With that in place you can do something like this:

var PlayGrass = false;

function Update() {

if(PlayGrass == true) { audio.Play(Grass Audio File); } }

Rough Coding there, but you get the picture

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by Yanger_xy · Mar 03, 2011 at 03:56 PM

   // calculate which splat map cell the worldPos falls within (ignoring y)
    int mapX = (int)(((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
    int mapZ = (int)(((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);

I can not fully understand it, could you explain it for me? Thanks a lot.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image b1gry4n · Oct 07, 2014 at 02:07 PM 0
Share

if you think about terrain splat maps as a 2d plane it becomes a little easier.

"worldPos.x - terrainPos.x" : getting the position in world space relative to the terrain (think local space)

"/ terrainData.size.x" : divide by the max width (think percentages. ex: 5 / 10 = 0.5 OR 50%, 5 being the local space position, 10 being the max width)

" terrainData.alphamapWidth" : get the same point but on the alpha map. taking the above example, if the alpha map width for some strange reason is 200, 0.5 200 = 100 OR 50% of 200 is 100.

basically youre translating world position to the terrains local position, getting that point, and updating the alpha map based off that point

avatar image
0

Answer by Erra666 · Dec 08, 2015 at 11:59 AM

You cant use if statements to return an int. Cannot convert int to bool..

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

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

Answers Answers and Comments

6 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Modifying Terrain Splat Texture at Runtime 2 Answers

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

how to assign texture on the terrain at runtime 0 Answers

Using substances on terrain 1 Answer

Exporting terrain splatMap 5 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges