Search Unity

Create perlin noise to spawn 2D objects

Discussion in 'Scripting' started by elmar1028, May 24, 2015.

  1. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    Hey guys,

    I've written a script which allows me to spawn gameobjects at random points of the scene, but it turned out to be too costly in terms of performance on mobile platforms.
    I've heard about perlin noise and heard amazing results.

    I've been look over the web how to a) create perlin noise and b) use it to spawn 2D objects

    Any ideas?

    Thanks in advance! :D
     
    anycolourulike likes this.
  2. RiokuTheSlayer

    RiokuTheSlayer

    Joined:
    Aug 22, 2013
    Posts:
    356
    transform.position+=New Vector3(0,Mathf.PerlinNoise(transform.position.x/5),0);

    I still haven't worked out exactly how coordinates in Perlin Noise work for unity, but using this in something like Start() should give the object a nice offset. Divide transform.position by more to get smoother terrain if you like, divide it by less to get more rigid terrain.

    You may need to add 0.1f to the transform.position.x if it stops working after a certain distance. Haven't tested that though
     
  3. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    Hello,

    Is the script you posted, generates a perlin noise or changes position of an object according to a perlin noise.
    I have attached it to few of my objects to change position accordingly, but it only moved a less than a unit to the left, without any randomness or variation.
     
    anycolourulike likes this.
  4. RiokuTheSlayer

    RiokuTheSlayer

    Joined:
    Aug 22, 2013
    Posts:
    356
    Yeah, try Mathf.PerlinNoise(transform.position.x+0.1f)*5 instead... It's quite odd, I never know exactly how it works
     
  5. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    (Unity's) Perlin noise returns a value from 0 to 1 (normally perlin returns -1 to 1). Multiplying the result by 5 means it returns 0 to 5. Multiply it by the range you want. If you're placing multiple objects in a row and want them in different, random seeming positions, then you want a really high frequency:

    Mathf.PerlinNoise(x * frequency, y * frequency)

    If frequency is low, such as 0.005f, then the resulting values will be close to each other and your objects will spawn close together. If you want them to spawn in very different positions, try a really high frequency such as 0.1 or higher.
     
    Last edited: May 24, 2015
  6. RiokuTheSlayer

    RiokuTheSlayer

    Joined:
    Aug 22, 2013
    Posts:
    356
    Actually, Unity's Perlin Noise is a range of 0 to 1
     
    JasonBricco likes this.
  7. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    Huh, you're right - seems they restricted it. Edited.
     
  8. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    It didn't work. It moves it a few units and that's all. Nothing random at all.
     
  9. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    Maybe show the code you're using?
     
  10. dterbeest

    dterbeest

    Joined:
    Mar 23, 2012
    Posts:
    389
    Mathf.PerlinNoise expects two floating point values as x and y, these should be between 0 and 1. It gives back a float between 0 and 1 as well. If you look at the scripting page for perlin noise you'll find a good example
     
  11. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Quite possibly the issue. I'm pretty sure the doc doesn't mention anything about what the input needs to be.
     
  12. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    Ahh! I thought x, y were coordinate points! You got me confused there.
    Are there chances that gameobjects would overlap each other?
     
  13. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    If you consider math rounding, yes, it's a possibility.
     
  14. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    That was, in fact, the entire point of "multiplying by a low frequency" like I was talking about. Although the values do not need to be between 0 and 1 - I use this method for spawning trees with a frequency of 0.1. The results are quite often greater than 1, and it's fine. It's just that the higher the number, the more noisy the results are (less smooth). And for trees in my case, I want noisiness. If it's smooth, then all the trees appear too close together.

    For the tree case, before I spawn any, I do a small search around the area I'm about to place it to be sure no other tree is too close by so that there isn't overlap.

    Edit: actually, I'm talking about perlin/simplex noise in general, not Unity's implementation. Perhaps their's has different properties about it.
     
  15. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    Got it working!

    I have spawned 420 objects in using Perlin Noise :)

    However my objects are now overlapping (some of them are circled blue):


    Here the code for it:

    Code (CSharp):
    1. transform.position = new Vector2(Mathf.Clamp(Mathf.PerlinNoise(0, 1) * Random.Range(-100.5f, 100.5f), -100, 100), Mathf.Clamp(Mathf.PerlinNoise(0, 1) * Random.Range(-100.5f, 100.5f), -100, 100));
    Mathf.Clamp() is used to limit circles being spawned only between -100 and 100.

    How do I fix overlapping issue?

    Thanks for the help so far. :D

    There is no performance drop as well! Even on mobile! (probably because each circle does nothing)
     
  16. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    You're sort of defeating the purpose of perlin noise there and essentially only using Random.Range. There's no reason to do a perlin calculation at all with your code.

    Perlin will always produce the same result given the same input. You always pass 0, 1 into it and so it will always produce the same result. The randomness is being caused by Random.Range.
     
    AlucardJay likes this.
  17. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    That's what perlin noise is for isn't it? I want to spawn all objects randomly without making them overlap with each other. If perlin noise gives me same value, then how can I use it for random generation?
     
  18. ThatGuyFromTheWeb

    ThatGuyFromTheWeb

    Joined:
    Jan 23, 2014
    Posts:
    29
    What do you want to achieve? Spawn a fixed number of say ... 500 objects ... with a completely random distribution in a certain squared area? You don't need Perlin Noise for that. Usually you use perlin noise to get a not completely random distribution, so your objects are forming some more dense groups or more empty spaces and something like that.

    Also, if you don't want your objects to overlap, you have to calculate distances to each already placed object, before you place a new one. Perlin Noise doesn't do that for you.
     
  19. elmar1028

    elmar1028

    Joined:
    Nov 21, 2013
    Posts:
    2,359
    Well, looks like I misunderstood some stuff! :p
    I thought perlin noise is used for that since games like Minecraft to generate terrain.
     
  20. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    That is how it can be used, but that isn't exactly your issue.
     
  21. ThatGuyFromTheWeb

    ThatGuyFromTheWeb

    Joined:
    Jan 23, 2014
    Posts:
    29
    This should be enough to work somehow and to explain the difference, between completely random placement and PerlinNoise used as some kind of density function. Note: Minecraft uses this in another completely different way. There's many uses for perlin noise.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5.  
    6. public class PlaceStuffs : MonoBehaviour {
    7.  
    8.     int count = 2000; // Number of cubes we want to place. Don't set this to high or the position picking loop may run forever.
    9.     float x = 200f;    // placement area width
    10.     float z = 200f;   // placement area height
    11.     float minDistance = 0.5f; // How far our cubes should stay away from each other. Also: Don't set this to high or the position picking loop may run forever.
    12.  
    13.     List<Vector3> positions = new List<Vector3>();
    14.    
    15.     void Start() {
    16.  
    17.         PlaceStuffRandomly(false); // Set this to true, to place using perlin noise
    18.        
    19.         // Yay! ... worked. Lets build the cubes.
    20.         for ( int j = 0; j < positions.Count; j++ ) {
    21.             var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
    22.             go.transform.position = positions[j];
    23.         }
    24.     }
    25.  
    26.  
    27.     public void PlaceStuffRandomly( bool UsePerlin ) {
    28.        
    29.         Vector3 newPos;
    30.         bool Nope = false;
    31.  
    32.         // We want to place #count godly cubes
    33.         for ( int i = 0; i < count; i++ ) {
    34.            
    35.             do {
    36.                 //Pick a random new pos ...
    37.                 newPos = new Vector3(Random.value * x, 0, Random.value * z);
    38.  
    39.                 if ( UsePerlin ) {    
    40.                     // We have to ask mister perlin if he thinks this point is ok (And check distances)
    41.                     Nope = !( PerlinThinksItShouldBeThere(newPos) && CouldPlaceItThere(newPos));
    42.                 } else
    43.                     // It's enough to check distances
    44.                     Nope = !CouldPlaceItThere(newPos);
    45.  
    46.             } while ( Nope ); // This loop will run endlessly, if you try to stuff too many things in a too small ares. (Even on a modern quadcore!)
    47.  
    48.             positions.Add(newPos);
    49.         }
    50.  
    51.     }  
    52.  
    53.     private bool CouldPlaceItThere( Vector3 newPos ) {
    54.         // Loop through all positions where we already want to place something
    55.         for ( int i = 0; i < positions.Count; i++ )
    56.             if ( Vector3.Distance(positions[i], newPos) < minDistance ) // ... and check if the new point maybe is to close
    57.                 return false;      
    58.         return true;
    59.     }
    60.  
    61.     private bool PerlinThinksItShouldBeThere( Vector3 newPos ) {
    62.        
    63.         // Basically how fast perlin changes his mind when you ask him for nearby Points
    64.         float frequency = 8;
    65.        
    66.         // Lets ask him what he thinks of the current position
    67.         float howSurePerlinIsThatItShouldBeThere = Mathf.PerlinNoise(newPos.x / x * frequency, newPos.z / z * frequency);
    68.  
    69.         if ( Random.value <= howSurePerlinIsThatItShouldBeThere )
    70.             return true;
    71.         else
    72.             return false;
    73.     }
    74.  
    75.  
    76. }