Search Unity

Check out this question if you're bored

Discussion in 'Scripting' started by keenanwoodall, Jul 27, 2014.

  1. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598

    So I've been working on a project that creates a sea of cubes by layering different perlin noises. The cubes find their position on the perlin noise "texture" and their height is set to a value between -1 and +1. That value is determined by the color of the perlin noise. How would I apply this to a planes mesh; where the vertices of the mesh act like the cubes do? Here is my code ( which you're welcome to try out :) ) Hopefully it's not too long.
    Code (js):
    1.  
    2. var cubeAmountX : int = 50;
    3. var cubeAmountZ : int = 50;
    4. var seed : int = 0.0;
    5. var seed2 : int = 0.0;
    6. var cube : GameObject;
    7. var colorSetsScale : boolean;
    8. var heightSetsScale : boolean;
    9. var sizeDivider : float = 2.0;
    10. var animateColor : boolean;
    11. var roundHeightToInt : boolean;
    12. var noiseScale : float = 6.5;
    13. var noiseScale2 : float = 6.5;
    14. var cubeHeight : float = 3;
    15. var cubeHeight2 : float = 3;
    16. var animateSpeed : float = 0.5;
    17. var animateSpeed2 : float = 1.0;
    18. private var finalHeight : float;
    19.  
    20. function Start ()
    21. {
    22.     for (var spawnPositionX = 0; spawnPositionX < cubeAmountX; spawnPositionX++)
    23.     {
    24.         for (var spawnPositionZ = 0; spawnPositionZ < cubeAmountZ; spawnPositionZ++)
    25.         {  
    26.             var makeCube = Instantiate(cube, Vector3(spawnPositionX,0,spawnPositionZ), Quaternion.identity);
    27.             makeCube.transform.parent = this.transform;
    28.         }
    29.     }
    30.     for (var child : Transform in this.transform)
    31.     {
    32.         if (noiseScale <= 0.001)
    33.         {
    34.             noiseScale = 0.0011;
    35.         }
    36.         var height = Mathf.PerlinNoise(child.transform.position.x/noiseScale + (seed * 10), child.transform.position.z/noiseScale + (seed *  10));
    37.         var height2 = Mathf.PerlinNoise(child.transform.position.x/noiseScale2 + (seed2 * -10), child.transform.position.z/noiseScale2 + (seed2 *  -10));
    38.         height *= cubeHeight;
    39.         height2 *= cubeHeight2;
    40.         finalHeight = height + height2;
    41.         if (animateColor)
    42.         {
    43.             child.renderer.material.color = Color(finalHeight,finalHeight,finalHeight,finalHeight);
    44.         }
    45.         if (colorSetsScale)
    46.         {
    47.             if (sizeDivider <= 0.0)
    48.             {
    49.                 sizeDivider = 0.001;
    50.             }
    51.             child.transform.localScale = Vector3 (finalHeight,finalHeight,finalHeight);
    52.         }
    53.     }
    54. }
    55.  
    56. function Update ()
    57. {
    58.     for (var child : Transform in this.transform)
    59.     {
    60.         var height = Mathf.PerlinNoise(child.transform.position.x/noiseScale, child.transform.position.z/noiseScale);
    61.         var height2 = Mathf.PerlinNoise(child.transform.position.x/noiseScale2 + (seed2 * -10), child.transform.position.z/noiseScale2 + (seed2 *  -10));
    62.         height *= cubeHeight;
    63.         height2 *= cubeHeight2;
    64.         var finalHeight = height + height2;
    65.         if (animateColor)
    66.         {
    67.             child.renderer.material.color = Color(finalHeight,finalHeight,finalHeight,finalHeight);
    68.         }
    69.     }
    70.     for (var child : Transform in this.transform)
    71.     {
    72.         height = Mathf.PerlinNoise(Time.time * animateSpeed + (child.transform.position.x/noiseScale) + (seed * 10), Time.time * animateSpeed + (child.transform.position.z/noiseScale) + (seed * 10));
    73.         height2 = Mathf.PerlinNoise(Time.time * animateSpeed2 + child.transform.position.x/noiseScale2 + (seed2 * -10),Time.time * animateSpeed2 + child.transform.position.z/noiseScale2 + (seed2 *  -10));
    74.         height *= cubeHeight;
    75.         height2 *= cubeHeight2;
    76.         finalHeight = height + height2;
    77.         if (!roundHeightToInt)
    78.         {
    79.             child.transform.position.y = finalHeight + transform.position.y;
    80.         }
    81.         else if (roundHeightToInt)
    82.         {
    83.             child.transform.position.y = Mathf.RoundToInt(finalHeight * cubeHeight + transform.position.y);
    84.         }
    85.         if (animateColor)
    86.         {
    87.             child.renderer.material.color = Color(finalHeight,finalHeight,finalHeight,finalHeight);
    88.         }
    89.         if (colorSetsScale)
    90.         {
    91.             if (sizeDivider <= 0.0)
    92.             {
    93.                 sizeDivider = 1;
    94.             }
    95.             child.transform.localScale = Vector3 (finalHeight,finalHeight,finalHeight);
    96.             heightSetsScale = false;
    97.         }
    98.         if (heightSetsScale)
    99.         {
    100.             if (sizeDivider <= 0.0)
    101.             {
    102.                 sizeDivider = 1;
    103.             }
    104.             var cubeHeight : float = child.transform.localPosition.y;
    105.             child.transform.localScale = Vector3 (cubeHeight/sizeDivider, cubeHeight/sizeDivider, cubeHeight/sizeDivider);
    106.             colorSetsScale = false;
    107.         }
    108.         if (!heightSetsScale && !colorSetsScale)
    109.         {
    110.             child.transform.localScale = Vector3 (1,1,1);
    111.         }
    112.     }
    113. }
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
  3. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    Thats what I'd like to do, but I can't figure it out. Here's my progress on the vertex based displacement
    Code (js):
    1.  
    2. var seed : int = 0.0;
    3. var roundHeightToInt : boolean;
    4. var noiseScale : float = 6.5;
    5. var waveHeight : int = 3;
    6. var animateSpeed : float = 1.0;
    7.  
    8. private var mesh : Mesh;
    9. private var vertices : Vector3[];
    10. private var perlinHeight : float;
    11.  
    12. function Start ()
    13. {
    14.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    15. }
    16.  
    17. function Update ()
    18. {
    19.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    20.     vertices = mesh.vertices;
    21.     for (var vert = 0; vert < vertices.Length; vert++)
    22.     {
    23.         var worldVert : Vector3 = vertices[vert];
    24.         perlinHeight = Mathf.PerlinNoise (worldVert.x / noiseScale, worldVert.z / noiseScale);
    25.         worldVert.y = perlinHeight * waveHeight;
    26.     }
    27.     mesh.RecalculateBounds();
    28.     mesh.RecalculateNormals();
    29. }
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Are you just talking about a HeightMap?

    Unity doesn't appear to use a texture for its terrain heightmaps (I never played with their terrain system... just never made a game needing it). Which is weird, because most engines I've played with just used textures directly.

    Anyways, there are scripts out there that exist to map a texture to the existing terrain heightmap container.

    Here's one:
    http://wiki.unity3d.com/index.php?title=HeightmapFromTexture
     
  5. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    I'
    m trying to update in realtime my own mesh. Not a terrain object. Is that what you're suggesting? to use a terrain object?
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    You can modify the terrain data at runtime.
     
  7. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    I'd like to do it to a my own mesh though. Could I do your heightmap idea on something that isn't a unity terrain object? I'f not, I need to figure out how to move the vertices in the way I move cubes in the video at the top of the thread
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Is there a reason why?

    Is this for purely academic reasons because you want to learn?

    Or is there something wrong with the Terrain class in Unity that it doesn't support something you need?
     
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    If you want to use your own mesh, well it's not that hard.

    You have a mesh (imported one, or procedurally generated).
    The number of verts on the mesh should be to the detail level you desire (procedurally generated meshes make this easier to change at runtime).
    You loop over the verts of the mesh, and set their position based on your heightmap texture. Just like the cubes.

    Honestly that's what the Terrain class in Unity does as well. It has a mesh, and it adjusts the height of the vertices based on the 'TerrainData' object associated with it. It's just the TerrainData isn't a texture, and instead is its own data structure.
     
  10. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    That's basically what I've been trying. Here's my script. You can see where I'm stuck...As in it doesnt work
    Code (js):
    1.  
    2. var seed : int = 0.0;
    3. var roundHeightToInt : boolean;
    4. var noiseScale : float = 6.5;
    5. var waveHeight : int = 3;
    6. var animateSpeed : float = 1.0;
    7.  
    8. private var mesh : Mesh;
    9. private var vertices : Vector3[];
    10. private var perlinHeight : float;
    11.  
    12. function Start ()
    13. {
    14.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    15. }
    16.  
    17. function Update ()
    18. {
    19.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    20.     vertices = mesh.vertices;
    21.     for (var vert = 0; vert < vertices.Length; vert++)
    22.     {
    23.         var worldVert : Vector3 = vertices[vert];
    24.         perlinHeight = Mathf.PerlinNoise (worldVert.x / noiseScale, worldVert.z / noiseScale);
    25.         worldVert.y = perlinHeight * waveHeight;
    26.     }
    27.     mesh.RecalculateBounds();
    28.     mesh.RecalculateNormals();
    29. }
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Vector3's are structs. That means they are passed by value, not by reference.

    You edit the vector3, which is the value of the vert, but you don't set it back to the vert array.

    After line 25 in that code put:
    vertices[vert] = worldVert;
     
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    This is the same thing as position on the transform.

    You can't say:

    Code (csharp):
    1.  
    2. var v = transform.position;
    3. v.x += 1.0f;
    4.  
    The change doesn't take. You have to say:

    Code (csharp):
    1.  
    2. var v = transform.position;
    3. v.x += 1.0f;
    4. transform.position = v;
    5.  
    I'd also like to point out that the Terrain class also supports smoothing and other extra features.
     
  13. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    Ok, done. But it still doesn't move the vertices
    Code (js):
    1.  
    2. var seed : int = 0.0;
    3. var roundHeightToInt : boolean;
    4. var noiseScale : float = 6.5;
    5. var waveHeight : int = 3;
    6. var animateSpeed : float = 1.0;
    7.  
    8. private var mesh : Mesh;
    9. private var vertices : Vector3[];
    10. private var perlinHeight : float;
    11.  
    12. function Start ()
    13. {
    14.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    15. }
    16.  
    17. function Update ()
    18. {
    19.     mesh = this.gameObject.GetComponent(MeshFilter).mesh;
    20.     vertices = mesh.vertices;
    21.     for (var vert = 0; vert < vertices.Length; vert++)
    22.     {
    23.         var worldVert : Vector3 = vertices[vert];
    24.         perlinHeight = Mathf.PerlinNoise (worldVert.x / noiseScale, worldVert.z / noiseScale);
    25.         worldVert.y = perlinHeight * waveHeight;
    26.         vertices[vert] = worldVert;
    27.     }
    28.     mesh.RecalculateBounds();
    29.     mesh.RecalculateNormals();
    30. }
     
  14. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    So I can manipulate my object's mesh through the terrain class without making it a terrain object?
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    What?

    No. That is not what I said in any way shape or form.
     
  16. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    oh, sorry. I know I'm overcomplicating this. Could you explain what I need to fix in my code? Btw, if you haven't noticed, I'm a pretty amateur programmer.
     
  17. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Also as to your code still not working, Mesh.vertices is also a copy of the vertices.
    http://docs.unity3d.com/ScriptReference/Mesh-vertices.html

    That means you have to set the vertices back to the mesh as well.

    Before you say mesh.RecalculateBounds();

    You should put:
    mesh.vertices = vertices;
     
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    There's other things wrong with your code.

    1) You have your mesh declared as a class level variable. That's fine. And you get the component upon 'Start'. That's good. But then you continue to re-get the component on every update. Why? You already got it. Why waste the time getting it again?

    2) You have your vertices and perlinHeight variables as a class level variable as well. But they're only used temporarily in the scope of the 'Update' method. Why? Define those variables INSIDE 'Update' where they're needed. A class level variable should be for variables that persist for the life of the object. Not the life of a method call.

    3) your naming conventions can be confusing. It's common for a for loop to use the variable 'i' (short for index). You use 'vert', but that value isn't the vert, it's the index of the vert. This causes you to have to call the actual vert 'worldVert'... note also that 'worldVert' is defined to the method, and not to the class (see 2).
     
  19. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    ok cool, now the mesh moves, although its weird...also the collider doesn't update. What should I call to update it?
     
  20. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    I'm just doing this out of curiosity, and to learn more. I also think advancing the script to do more stuff could result in a pretty cool tool
     
  21. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    ok, that helpful, thanks. The reason I have all the variables defined outside of update is so that I can modify them at runtime in the inspector.
     
  22. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,532
    Here's a cleaned up script:

    Code (csharp):
    1.  
    2. var seed:int = 0;
    3. var roundHeightToInt:boolean;
    4. var noiseScale:float = 6.5;
    5. var waveHeight:int = 3;
    6. var animateSpeed:float = 1.0;
    7.  
    8. private var _meshFilter;
    9.  
    10. function Start()
    11. {
    12.     _meshFilter = this.GetComponent(MeshFilter);
    13. }
    14.  
    15. function Update()
    16. {
    17.     var mesh = _meshFilter.mesh;
    18.     var vertices = mesh.vertices;
    19.     for(var i = 0; i < vertices.Length; i++)
    20.     {
    21.         var vert = vertices[i];
    22.         var perlinHeight = Mathf.PerlinNoise (vert.x / noiseScale, vert.z / noiseScale);
    23.         vert.y = perlinHeight * waveHeight;
    24.         vertices[i] = vert;
    25.     }
    26.     mesh.vertices = vertices;
    27.     mesh.RecalculateBounds();
    28.     mesh.RecalculateNormals();
    29. }
    30.  
    Not sure why it looks weird. That's what the results are.

    As for updating the collider. If you have a mesh collider on it you need to make sure the Mesh that is set to the 'sharedMesh' property of the MeshCollider is the same mesh you're modifying.

    I should point out that one thing that makes a useful tool is when it integrates with existing systems and tools. Say you shared this tool with me, and I wanted to use it to add a wave effect to my terrain. But my terrain is made using Unity's built in terrain editor... well it doesn't work for me and I have to convert my terrain to your structure.

    But vertices is a GIANT array that is being updated 60 times per second. You wouldn't be able to hand modify that at runtime. Same goes for 'perlinHeight' which is altered thousands of times per frame, 60 times per second.

    If you wanted to be able to tweak these, you'd probably have to pause operation. And you'd have to do it MID frame call, not at the end of the frame call (which is what the pause button at the top does). Which really could only be done in a debugger (like MonoDevelop), in which case it doesn't matter where they're defined. The debugger can access method level variables.
     
  23. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    Thanks a bunch for the indepth response. You've given me a bunch to think about :)
     
  24. keenanwoodall

    keenanwoodall

    Joined:
    May 30, 2014
    Posts:
    598
    So what would be your advice on optimizing this system so that it doesn't have to check every vertices in every frame?
     
  25. smitchell

    smitchell

    Joined:
    Mar 12, 2012
    Posts:
    702
    If anyone wants this in C# here it is:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MeshFun : MonoBehaviour {
    5.  
    6.     public float noiseScale = 6.0f;
    7.     public int waveHeight = 3;
    8.     private MeshFilter _meshFilter;
    9.  
    10.     void Start () {
    11.         _meshFilter = GetComponent<MeshFilter> ();
    12.     }
    13.  
    14.     void Update () {
    15.         if(!_meshFilter)
    16.             return;
    17.  
    18.         Mesh mesh = _meshFilter.mesh;
    19.         Vector3[] vertices = mesh.vertices;
    20.  
    21.         for(int i = 0; i < vertices.Length; i++) {
    22.             Vector3 vert = vertices[i];
    23.             float perlinHeight = Mathf.PerlinNoise(vert.x / noiseScale, vert.y / noiseScale);
    24.             vert.y = perlinHeight * waveHeight;
    25.             vertices[i] = vert;
    26.         }
    27.  
    28.         mesh.vertices = vertices;
    29.         mesh.RecalculateBounds ();
    30.         mesh.RecalculateNormals ();
    31.     }
    32. }
    33.  
     
    FuzzyBalls likes this.