Cookies   I display ads to cover the expenses. See the privacy policy for more information. You can keep or reject the ads.

Video thumbnail
[Transcribed by tymewiz] Hi everyone welcome to episode 02.
So today we are going to begin programming our noise map generation
we are going to be using perlin noise for this series since unity provides
a built-in implementation in their maths class
one thing to take note of with perlin noise however is that at integral coordinates
we are always going to get the same value
this isn't going to pose a problem, but it is good just to be aware of it
alright, so in this new unity project, let's head over to the scripts folder
and let's create a new c# script called Noise
so if we just open this up we're not going to need to apply this to any object in our scene
so we have no reason to inherit from monobehavior
also we are not going to want to make multiple instances of this script
so we can just go ahead and make it static
alright, so we want this to have a method for generating a noise map
and we want that to return essentially a grid of values between 0 and 1
so let's create a public static method returning a 2-D array of float values
and we can call this method GenerateNoiseMap
so ultimately its going to have a whole bunch of arguments including the lacunarity and persistence
values we talked about last episode
but for now let's just define the map dimensions as an int for the mapwidth
and another int for the mapheight
so we can now create our 2-D float array called our noisemap
set that equal to a new 2-D float array with a size of mapwidth by mapheight
and well want to loop through that noisemap
so we'll say for int y = 0, y less than mapheight, y ++
and similarly for the x value
for int x = 0, x less than mapwidth, x++
so now we want to figure out at which point we'll be sampling our height values from
so let's say float sampleX let's just set this equal to x for now
and then float sampleY we can set to y
of course we don't want to just use these integer values since as I mentioned earlier that will give us the same value every time
so let's introduce a new argument, a float for the scale of the noise
and we can just divide x by scale
and also y by scale
so we can get some nice non-integer values
now of course we have to be careful that if scale is 0 we are going to get a division by 0 error
so let's just handle that case by saying
if the scale is less than or equal to 0
then we can just clamp it to a minimum value of, say, 0.001
alright so now that we have our sample coordinates
we can say float perlinvalue is equal to mathf.perlin and we'll pass in sampleX and sampleY
we just need to now apply that to our noisemap so we can say noisemap, with coordinates x and y
is equal to that perlin value
finally of course we just need to return the noisemap that we just created
now obviously there is a lot more work we need to do on our GenerateNoiseMap method
but i'd first like to get it drawn onto the screen so that we can sort of visualize what we are working with
so let's head back to unity
and we are gonna create another c# script this one called mapgenerator
and I want to apply that to an object in the scene
so let's create a new empty and just call this mapgenerator as well
just reset that so it's in the center of the scene
and apply the mapgenerator script to it
so in here we want to have a bunch of values defining our map
such as later on lacunarity and persistence but for now just
public int mapwidth
public int mapheight
and also that float for the noise scale
alright then we want to have a public void, call this generatemap
and what it'll start out by doing is just fetching the 2-D noisemap from the noise class
so we can say 2-D float array
call this noisemap, is equal to noise.GenerateNoiseMap
and we can pass in all these parameters
mapwidth, mapheight, and noise scale
so later on of course we'll have a bunch of stuff over here to process this noisemap and turn it into our terrain map
but for now we just want to pass it along to our mapdisplay class
so that it can draw this noisemap to the screen
so let's go back to unity and create this mapdisplay class that i've been talking about
so let's call this mapdisplay
and we can apply that to the mapgenerator object in our scene as well
so this is just going to take the noisemap and turn it into a texture
and then its going to apply that texture to a plane in our scene
so let's create a new plane gameobject
and I'm just going to reset that so it's in the center
we can just remove the collider
and let's now go to our mapdisplay
so we're going to want a reference to the renderer of that plane so we can just set its texture
so let's say public renderer, call that our texture renderer
and then we are going to want to have a public method
called something like drawnoisemap
which takes in that 2-D float array for the noise map
and what we'll first want to do is figure out the width and height of the noisemap that we've been given
so we can do this by saying int width equals noisemap.getlength
0 for the first dimension
and then int height = noisemap.getlength 1 for the second dimension
now we can create our 2-D texture by saying texture2D, just call this texture is equal to
a new 2-d texture with a width of width and, you guessed it, a height of height
so we now want to go ahead and set the color of each of the pixels in this texture
so one way to do that is to say texture.setpixel and then we can pass in a x and y coordinate as well as a color
but it turns out it's faster to first generate an array of all of the colors for all of the pixels
and then to set them all at once like so
so we'll chose that method instead
so let's create a color array, call this our colormap, set that equal to a new color array
with a size of width multiplied by height
and then we'll want to loop through all of the values in our noisemap
so we can just say for in y = 0, y less than height, y++
once again same thing for x
for in x = 0, x less than width, x incrementing by 1 each time
we now want to set colormap
we need to of course figure out the index for the colormap
remember that the colormap is a 1-dimensional array
whereas our noisemap is a 2-dimensional array
so we can get the index by first multiplying y by the width of the map
so that will give us the index of the sort of row that we are currently on
and to get the column we of course just add the x value
so we want to set this equal to a color somewhere between black and white,
depending on the value of the noisemap at that point
so we can say color.lerp, from color a equals color.black
to color b equals color.white
and then we give it a percentage between 0 and 1, which is the same range as our noisemap, which is convenient
so we just say noisemap, with an index of x and y
ok so we want to apply those colors to our texture so we say texture.setpixels. pass in the colormap
and then just apply everything we've done by saying texture.apply
ok so now we'll want to apply the texture to the texture renderer
one thing to note is that we dont want to always have to enter game mode in order to preview our maps
it would be much nicer if we could generate them inside of the editor
so that means we can use texturerenderer.material since that is only instantiated at runtime
so instead we use sharedmaterial so we can set the sharedmaterials maintexture
equal to the texture we've just generated
it'd also be nice to set the size of the plane to the same size as the map
so we can just say texturerenderer.transform.localscale is equal to a new vector3
with an x size of width, a y size of 1, and a z size of height
alright, let's save that and let's go into the mapgenerator class so we can call the mapdisplay with our noisemap
so we need to get a reference to the mapdisplay of course so let's just say
mapdisplay, call that display, is equal to find objectoftype mapdisplay
and then we can say mapdisplay.drawnoisemap and just pass in the noisemap
alright, let's go back into unity, and let's apply the plane to here
and let's also create a material for this plane
im gonna create a new folder, to store materials in
and ill just create a new one over here, just call this, something like, map material
and im gonna set this equal to a unlit texture shader, since we dont have a light in our scene currently and im just gonna apply that over here
so at the moment there is no way for the mapgenerators' generate map method to be called
so what we're going to do is create a editor script that will have a little button over here to generate the map within the editor
so let's create an editor folder
and inside of that we'll create a new c# script
and we can call this something like mapgeneratoreditor
alright, let's open that up
and we'll start off by saying using unityeditor
and then we'll extend from editor instead of monobehavior
and we can delete all of this stuff
and instead just override the oninspectorgui method
ok, so let's start off by getting a reference to our mapgenerator
so we can do that by saying mapgenerator, just call that mapgen is equal to target, which is the object that this custom editor is inspecting
and we just want to cast that object to a mapgenerator
so we can just add in brackets before it
map generator
like so
alright, so let's draw the default inspector
so we can just say draw default inspector and then we want to add in a button
and we'll say if that button is pressed
so the button is guilayout.button with some text, say just generate
if that button is pressed we'll say mapgen.generatemap()
ok, so let's go into unity and we want to make sure that our map has a width and a height otherwise the texture is going to break
and potentially crash unity, that's happened to me once or twice
so let's give this a width of 10 and a height of 10 and a noisescale of, say .3
the button isnt actually showing up at the moment,
which is just a little something I forgot, we need say over here that this is a custom editor
and we have to say that the type of class that it is a custom editor of is the mapgenerator class
ok so now
we should see that generate button popping up
so if we press generate we now get this nice perlin map
let's increase the size to 100 by 100
let's perhaps add in an option for the map generator to autoupdate whenever we change one of these values
so let's create a public bool in the mapgenerator class
public bool autoupdate
and what the map generator editor will do
is it'll say if draw defaultinspector, which just means if any value was changed
then..
if mapgen.autoupdate, then we'll also generate the map
if we turn on autoupdate and increase the scale we should see that automatically updating
I'm going to end here for this episode, I hope you've enjoyed, and until next time... Cheers!