math.noise
solve three important problems that makes it good for procedurally generated stuff:
- aperiodic -- the result is not predictable
- smooth -- the result is never sharp (and neither are its derivatives)
- multidimensional -- the result works well in more than one dimension (up to three)
First, ignoring the fact that math.noise
is multidimensional; imagine we want to make a hilly side-scrolling platformy game.
Then we can visualize this with code like the following:
2 | print ( string.rep( "#" , math.random( 10 , 20 ) ) ) |
That's definitely unpredictable, but it's not smooth like the real world.
Another option would be use to something like math.sin
:
2 | print ( string.rep( "#" , 10 + 5 * math.sin(i) ) ) |
While this is smooth, it's obviously repeating itself, and that will look worse and worse the more of it you seen.
We can sort of solve this problem by adding two different sin waves together:
However you can still see the repetition in the pattern.
THE SOLUTION: math.noise
Basically, all we have to do is replace math.sin
with math.noise
and you get what you want.
There are a few important considerations:
math.noise
is bounded between -0.5 and 0.5**
- if
x
and y
differ by more than 1
, then math.noise(x)
and math.noise(y)
are completely unrelated. That means when you sample from math.noise
you have to sample closer than 1
together.
2 | print ( string.rep( "#" , 10 + 10 * math.noise(i / 5 ) ) ) |
Multidimensional
The other nice thing is that you can use math.noise(x, y, z)
.
For example, if you're generating terrain, you can get an interesting, random, smooth height at position (x, math.noise(x / 10, z / 10), z)
.
For example,
03 | local height = (math.noise(x / 20 , z / 20 ) + 2 ) * 50 |
04 | local p = Instance.new( "Part" , workspace) |
05 | p.Locked, p.Anchored = true , true |
06 | p.Size = Vector 3. new( 4 , height, 4 ) |
07 | p.CFrame = CFrame.new( 4 * x, height / 2 , 4 * z) |
Gotchas
math.noise
isn't perfect. It has a few problems:
For any integer i
, math.noise(i)
is zero. That means there is a periodic occurrence of 0
in any result from math.noise
.
You can cover this up by using two math.noise
which have an irrational relative frequency; for example,
5 | math.noise( f / math.sqrt( 2 ) * i ) + math.noise( f * math.sqrt( 2 ) * i ) |
Though this will have the effect of amplifying another problem with math.noise
; math.noise
likes to give very small values.
math.noise
also appears to have some precision problems when you use relatively large numbers; try to stick fewer than a few thousand.