En el último tutorial que crea un objeto de nivel mediante la creación de sus azulejos individuales. Para cada azulejo, se generaron valores de altura pseudoaleatorias utilizando una función de ruido, por lo que podríamos asignar tipos de terreno y alturas para cada región del azulejo.

Esta serie de tutoriales se inspira y se expande sobre técnicas presentadas por Sebastian Lague, Holistic3D y esta charla GDC en la generación de procedimiento en No Man Sky.

Como ya he mencionado en el tutorial anterior, el ruido puede ser utilizado como diferentes cosas en su juego. En el último tutorial lo usamos como valores de altura. Ahora, vamos a utilizarlo para los valores de calor y humedad asignar a diferentes regiones del nivel. En el extremo, el uso de este calor y humedad valores que vamos a biomas asignar a esas diferentes regiones.
No se pierda! extremos de la oferta en

  • Accede a los más de 200 cursos
  • Nuevos cursos añadió mensual
  • Cancelar en cualquier momento
  • Los certificados de terminación

    ACCESO ahora

    Con el fin de seguir este tutorial, que se espera que esté familiarizado con los siguientes conceptos:


    • programación C #
    • conceptos unidad básica, como la importación de bienes, la creación de casas prefabricadas y la adición de componentes

      Tabla de contenidos

      archivos de código fuente

      Puede descargar los archivos de código fuente tutorial aquí.

      Generación de mapa de calor

      Vamos a empezar por la generación de un mapa de calor a nuestro nivel, así como la asignación de una textura a la misma en base a este mapa de calor. Una forma de hacerlo es mediante la repetición del proceso que hicimos para el mapa de altura: para cada azulejo, generando un mapa de calor, donde cada vértice de las baldosas corresponde a una coordenada en el mapa de calor. Esto llevaría a un mapa de calor distribuido aleatoriamente sobre o mapa.

      Sin embargo, en nuestro mundo, la temperatura no se distribuye de manera uniforme. En realidad, las regiones con menor latitud son por lo general más caliente, mientras que las regiones con latitudes altas son por lo general más frío. Por lo tanto, puede ser interesante para generar el mapa de calor de tal manera, que las regiones cercanas al centro del nivel son más calientes.

      Con el fin de hacer esto, vamos a tener que crear un nuevo método en la secuencia de comandos NoiseMapGeneration llamada GenerateUniformNoiseMap. (La función original generación de ruido será renombrado a GeneratePerlinNoiseMap también). Este método recibirá como parámetros de las dimensiones del mapa, sino también coordinar el Z del centro del nivel, así como la distancia máxima a la coordenada este centro, de modo que podemos generar ruido proporcional a la distancia de cada mapa de coordenadas para este centrar. Además, vamos a necesitar el desplazamiento en el eje Z de la baldosa, de manera que podemos calcular correctamente sus valores de temperatura.

      Observe que la maxDistanceZ y offsetZ se dan en número de vértices. Por lo tanto, un maxDistanceZ de 10 significa que cualquier punto será como máximo de 10 vértices de distancia del centro. Por otro lado, un offsetZ de 11 medios del azulejo estamos trabajando es de 11 vértices lejos del origen.

      Una vez que tenemos estos parámetros, podemos calcular la sampleZ basado en el índice y el desplazamiento, y luego calcular el ruido proporcional a la distancia al centro del nivel. El ruido es básicamente la distancia absoluta de la muestra al centro del Nivel dividido por la distancia máxima. De esta manera, el ruido será menor para las regiones cerca del centro, ya que pretendemos, y siempre estará entre 0 y 1. Al final, aplicamos este ruido a todos los puntos con la misma coordenada Z.
      flotación pública [,] GenerateUniformNoiseMap (int mapDepth, int mapWidth, flotar centerVertexZ, flotar maxDistanceZ, flotar offsetZ) {// crear un mapa de ruido vacío con el mapDepth y mapWidth coordina flotador [,] noiseMap = new float [mapDepth, mapWidth]; for (int zIndex = 0; zIndex para para probar este método, es necesario construir una Texture2D que muestra que en nuestro juego. En primer lugar, tenemos que añadir en el guión TileGeneration un nuevo atributo para almacenar los TerrainTypes que vamos a utilizar para mostrar el mapa de calor. Además, vamos a utilizar el mismo método BuildTexture para mostrar tanto el mapa de altura y el mapa de calor, así que vamos a cambiarlo para recibir la matriz terrainTypes como parámetro, en lugar de usar el de los atributos.
      TileGeneration clase pública: MonoBehaviour {[SerializeField] TerrainType privada [] heightTerrainTypes; [SerializeField] TerrainType privada [] heatTerrainTypes; privado Texture2D BuildTexture (float [,] de alturas, TerrainType [] terrainTypes) {int tileDepth = heightMap.GetLength (0); int tileWidth = heightMap.GetLength (1); Color [] = new mapa de colores de color [tileDepth * tileWidth]; for (int zIndex = 0; zIndex A continuación, en el método GenerateTile creamos un mapa de calor mediante el GenerateUniformNoiseMap y construimos una textura de ella. Sin embargo, no podemos mostrar tanto el mapa de altura y el mapa de calor, al mismo tiempo, ya que tenemos que cambiar la textura material de tileRenderer para eso.

      Por lo tanto, vamos a añadir a la escritura de un nuevo atributo llamado visualizationMode. Este atributo será una enumeración con dos valores: la altura y calor. Luego, en el método GenerateTile cambiamos la textura tileRenderer de acuerdo con el valor del atributo visualizationMode.
      TileGeneration público clase: MonoBehaviour {[SerializeField] privado VisualizationMode visualizationMode; public void GenerateTile (float centerVertexZ, flotador maxDistanceZ) {// Calcular profundidad de la loseta y el ancho basado en la malla vértices Vector3 [] meshVertices = this.meshFilter.mesh.vertices; int tileDepth = (int) Mathf.Sqrt (meshVertices.Length); int tileWidth = tileDepth; // calcular las compensaciones en base a la posición del flotador baldosas offsetX = -this.gameObject.transform.position.x; flotar offsetZ = -this.gameObject.transform.position.z; // generar un mapa de elevación usando Perlin Noise flotador [,] de alturas = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, olas); // Calcular vértice de desplazamiento basada en la posición del azulejo y de la distancia entre los vértices Vector3 tileDimensions = this.meshFilter.mesh.bounds.size; flotar distanceBetweenVertices = tileDimensions.z / (float) tileDepth; flotar vertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generar un mapa de calor usando flotador ruido uniforme [,] heatmap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); // construir un Texture2D desde el mapa de altura Texture2D heightTexture = BuildTexture (de alturas, this.heightTerrainTypes); // construir un Texture2D desde el mapa de calor Texture2D heatTexture = BuildTexture (mapa de calor, this.heatTerrainTypes); interruptor (this.visualizationMode) {case VisualizationMode.Height: material de textura // Asignar a ser el heightTexture this.tileRenderer.material.mainTexture = heightTexture; descanso; caso VisualizationMode.Heat: material de textura // Asignar a ser el heatTexture this.tileRenderer.material.mainTexture = heatTexture; descanso; } // actualizar el azulejo malla vértices de acuerdo con las UpdateMeshVertices mapa altura (de alturas); }} Enum VisualizationMode {Altura, Calor} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849publicclassTileGeneration: MonoBehaviour {[SerializeField] privateVisualizationMode visualizationMode; publicvoidGenerateTile (floatcenterVertexZ, floatmaxDistanceZ) {// Calcular profundidad de la loseta y anchura en base a la verticesVector3 malla [] meshVertices = this.meshFilter.mesh.vertices; inttileDepth = (int) Mathf.Sqrt (meshVertices.Length); inttileWidth = tileDepth; // calcular las compensaciones sobre la base de la baldosa positionfloatoffsetX = -this.gameObject.transform.position.x; floatoffsetZ = -this.gameObject.transform.position .z; // generar un mapa de elevación usando Perlin Noisefloat [,] de alturas = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, olas); // Calcular vértice desfase en base a la posición de baldosas y la distancia entre verticesVector3 tileDimensions = this.meshFilter.mesh.bounds.size; floatdistanceBetweenVertices = tileDimensions.z / (float) tileDepth; fl oatvertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generar un mapa de calor usando noisefloat uniforme [,] heatmap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); // construir un Texture2D de la altura mapTexture2D heightTexture = BuildTexture (de alturas, this.heightTerrainTypes); // construir un Texture2D del calor mapTexture2D heatTexture = BuildTexture (mapa de calor, this.heatTerrainTypes); conmutador (this.visualizationMode) {caseVisualizationMode.Height: // textura material de asignación ser el heightTexturethis.tileRenderer.material.mainTexture = heightTexture; break; caseVisualizationMode.Heat: material de textura // Asignar a ser el heatTexturethis.tileRenderer.material.mainTexture = heatTexture; break;} // actualizar el azulejo malla vértices de acuerdo con el mapUpdateMeshVertices altura (de alturas);}} {enumVisualizationMode Altura, calor}

      a continuación, podemos establecer algunos valores para esos nuevos parámetros y visualizar el resultado del mapa de calor por ir a t que el modo de juego. En el siguiente ejemplo, he creado cuatro valores de calor, desde caliente al más frío. El resultado es el de la figura derecha.


      La aleatorización el mapa de calor

      Hemos logrado generar nuestro mapa de calor a base de Latitute. Sin embargo, en un mundo real el mapa de calor no debe ser tan uniforme. Lo que tenemos que hacer entonces, es aleatorizar este calor mapa un poco.

      Podemos hacer que mezclando el mapa de calor uniforme con un mapa de calor de ruido Perlin. Otra cosa que podemos hacer está cambiando los valores de mapa de calor de acuerdo con la altura en la misma coordenada. Esto se debe a las zonas más altas generalmente tienen temperaturas más bajas.

      Con el fin de hacerlo, tenemos que cambiar el método de GenerateTile de la siguiente manera. Después de crear el mapa de calor uniforme, vamos a llamar a la función GeneratePerlinNoiseMap para generar otro mapa de calor utilizando Ruido Perlin. Luego, iterar a través de todos los mapas de ruido coordenadas multiplicando el uniformHeatMap y la randomHeatMap juntos. En esta misma iteración podemos aumentar el calor de acuerdo con el valor de altura en cada coordenada, añadiendo al calor mapear la multiplicación del calor por el valor de la altura en que coordenadas. Al hacer esto, se disminuye la temperatura en los valores más altos.
      public void GenerateTile (float centerVertexZ, flotar maxDistanceZ) {// Calcular profundidad de la loseta y el ancho basado en la malla vértices Vector3 [] meshVertices = this.meshFilter.mesh.vertices; int tileDepth = (int) Mathf.Sqrt (meshVertices.Length); int tileWidth = tileDepth; // calcular las compensaciones en base a la posición del flotador baldosas offsetX = -this.gameObject.transform.position.x; flotar offsetZ = -this.gameObject.transform.position.z; // generar un mapa de elevación usando Perlin Noise flotador [,] de alturas = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heightWaves); // Calcular vértice de desplazamiento basada en la posición del azulejo y de la distancia entre los vértices Vector3 tileDimensions = this.meshFilter.mesh.bounds.size; flotar distanceBetweenVertices = tileDimensions.z / (float) tileDepth; flotar vertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generar un mapa de calor usando flotador ruido uniforme [,] uniformHeatMap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); // generar un mapa de calor usando Perlin flotar Noise [,] randomHeatMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heatWaves); flotador [,] heatmap = nuevo flotador [tileDepth, tileWidth]; for (int zIndex = 0; zIndex con el fin de hacerlo aún más personalizable, se puede añadir un heatCurve como un atributo de nuestra escritura. Entonces, al aumentar el calor de acuerdo con la altura, podemos evaluar la altura de esta curva. De esta manera, se puede controlar el factor de multiplicación de acuerdo con la altura.
      TileGeneration público clase: MonoBehaviour {[SerializeField] privado AnimationCurve heatCurve; public void GenerateTile (float centerVertexZ, flotador maxDistanceZ) {// Calcular profundidad de la loseta y el ancho basado en la malla vértices Vector3 [] meshVertices = this.meshFilter.mesh.vertices; int tileDepth = (int) Mathf.Sqrt (meshVertices.Length); int tileWidth = tileDepth; // calcular las compensaciones en base a la posición del flotador baldosas offsetX = -this.gameObject.transform.position.x; flotar offsetZ = -this.gameObject.transform.position.z; // generar un mapa de elevación usando Perlin Noise flotador [,] de alturas = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heightWaves); // Calcular vértice de desplazamiento basada en la posición del azulejo y de la distancia entre los vértices Vector3 tileDimensions = this.meshFilter.mesh.bounds.size; flotar distanceBetweenVertices = tileDimensions.z / (float) tileDepth; flotar vertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generar un mapa de calor usando flotador ruido uniforme [,] uniformHeatMap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); flotar [,] randomHeatMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heatWaves); flotador [,] heatmap = nuevo flotador [tileDepth, tileWidth]; for (int zIndex = 0; zIndex al final, su mapa de calor debe ser similar a la de abajo. Tenga en cuenta que el calor se concentra todavía en el centro del nivel, pero las miradas de distribución más al azar.


      Generación de mapa de humedad

      El tercer tipo de ruido que vamos a añadir a nuestro juego es la humedad. Diferente del mapa de calor, sólo vamos a utilizar Ruido Perlin para generar la humedad, y no un ruido uniforme. Sin embargo, similar al mapa de calor, la humedad también debería verse afectada por la altura. En la práctica, cuanto mayor sea la región, la secadora debe ser, ya que es más lejos de la mar.

      Para hacerlo vamos a cambiar el guión TileGeneration manera similar a como lo hicimos para el mapa de calor. Primero necesitamos crear el moistureMap, y vamos a hacerlo como lo hicimos con el mapa de altura, utilizando Ruido Perlin. Tenga en cuenta que esto requiere un nuevo atributo para representar los moistureWaves. Entonces, iteramos través de todas las coordenadas de la moistureMap y actualizar su valor de acuerdo con la altura de la región. Sin embargo, diferente del mapa de calor, mayor será la región, menor debe ser la humedad. Esto se debe a regiones más altas están más lejos del mar, por lo que deben ser secadora.

      Después de la construcción de la moistureMap, podemos construir una Texture2D basado en la moistureMap y la moistureTerrainTypes (que hay que añadir como un atributo). Por último, se añade un nuevo modo de visualización llamado de humedad, y una declaración de caso cuando el modo de visualización es esta nueva. En este caso, queremos mostrar la moistureTexture.
      TileGeneration clase pública: MonoBehaviour {[] SerializeField TerrainType privada [] moistureTerrainTypes; [SerializeField] privada AnimationCurve moistureCurve; [SerializeField] privada Wave [] moistureWaves; public void GenerateTile (float centerVertexZ, flotador maxDistanceZ) {// Calcular la profundidad de azulejos y basado ancho en la malla vértices Vector3 [] meshVertices = this.meshFilter.mesh.vertices; int tileDepth = (int) Mathf.Sqrt (meshVertices.Length); int tileWidth = tileDepth; // calcular las compensaciones en base a la posición del flotador baldosas offsetX = -this.gameObject.transform.position.x; flotar offsetZ = -this.gameObject.transform.position.z; // generar un mapa de elevación usando Perlin Noise flotador [,] de alturas = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heightWaves); // Calcular vértice de desplazamiento basada en la posición del azulejo y de la distancia entre los vértices Vector3 tileDimensions = this.meshFilter.mesh.bounds.size; flotar distanceBetweenVertices = tileDimensions.z / (float) tileDepth; flotar vertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generar un mapa de calor usando flotador ruido uniforme [,] uniformHeatMap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); // generar un mapa de calor usando Perlin flotar Noise [,] randomHeatMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heatWaves); flotador [,] heatmap = nuevo flotador [tileDepth, tileWidth]; for (int zIndex = 0; zIndex Ahora, puede establecer algunos valores para los moistureTerrainTypes, así como los moistureWaves. A continuación, puede intentar jugar el juego para visualizar el mapa de humedad.


      Generación de biomas

      La última cosa que vamos a hacer en este tutorial es el uso de todas las variables de ruido que generamos en lo que va a asignar para biomas diferentes regiones del nivel. Nuestra generación bioma se basará en el modelo de la Whittaker, que clasifica biomas de acuerdo con su temperatura y la humedad tal como se representa a continuación.

      En nuestro caso, vamos a adaptarlo a nuestro juego, y construir una tabla con el tipo de bioma para cada combinación de valores de humedad y calor. Ya que tenemos cuatro tipos de terreno diferentes de calor y humedad cuatro tipos de terreno diferentes, la mesa tendrá 16 entradas. En este tutorial voy a utilizar la tabla que se muestra a continuación.
      más caliente caliente frío más frío dryest desertgrasslandtundratundra seco savannasavannaboreal foresttundra mojar tropical rainforestboreal forestboreal foresttundra más húmedo tropical rainforesttundratundra rainforesttropical

      Ahora tenemos que añadir los biomas de nuestro juego. La idea aquí es similar a la forma en que lo hicimos el TerrainTyp: la creación de una clase Serializable por ello por lo que se puede configurar en el editor. Sin embargo, ahora necesitamos una matriz 2D de biomas, ya que tenemos una tabla con diferentes biomas y matrices 2D no son serializable en la Unidad. Por lo tanto, lo que vamos a hacer es crear dos clases:


      1. BiomeRow: representa una fila de la tabla anterior
      2. Biome: representa una célula de la tabla anterior

        A continuación, se puede añadir una gran variedad de biomas a nuestro script TileGeneration, como a continuación. Another thing we need to do is updating the TerrainType class to have an index attribute, so that we can use this index later to access the biomes table.

        public class TileGeneration : MonoBehaviour {[SerializeField] private BiomeRow[] biomes;}[System.Serializable]public class Biome { public string name; public Color color;}[System.Serializable]public class BiomeRow { public Biome[] biomes;}[System.Serializable][System.Serializable]public class TerrainType { public string name; public float threshold; public Color color; public int index;}1234567891011121314151617181920212223publicclassTileGeneration:MonoBehaviour{[SerializeField]privateBiomeRow[]biomes;}[System.Serializable]publicclassBiome{publicstringname;publicColor color;}[System.Serializable]publicclassBiomeRow{publicBiome[]biomes;}[System.Serializable][System.Serializable]publicclassTerrainType{publicstringname;publicfloatthreshold;publicColor color;publicintindex;}

        The next step is to assign the biomes to the level regions according to the terrain types of that region. So, we are going to create a method called BuildBiomeTexture, which will receive as parameters the TerrainTypes for all our noise variables (height, heat and moisture), and will build a Texture2D based on their values.

        In order to create the biome texture we need to iterate through all the tile coordinates. For each coordinate, we first check if this is a water region. If so, we don’s select a biome based on the table, but we actually set its Color to be a predefined waterColor (set as an attribute).

        If the coordinate is not a water region, we select its biome according to the heat and moisture values. We can do that by using the new index attribute from the TerrainType class. First, we use the moisture index to access the correct row in the biomes table. Then, we use the heat index to access the correct cell in the biomes table. In the end, we assign the color according to the chosen Biome.

        private Texture2D BuildBiomeTexture(TerrainType[,] heightTerrainTypes, TerrainType[,] heatTerrainTypes, TerrainType[,] moistureTerrainTypes) { int tileDepth = heatTerrainTypes.GetLength (0); int tileWidth = heatTerrainTypes.GetLength (1); Color [] = new mapa de colores de color [tileDepth * tileWidth]; for (int zIndex = 0; zIndex < tileDepth; zIndex++) { for (int xIndex = 0; xIndex < tileWidth; xIndex++) { int colorIndex = zIndex * tileWidth + xIndex; TerrainType heightTerrainType = heightTerrainTypes [zIndex, xIndex]; // check if the current coordinate is a water region if (heightTerrainType.name != "water") { // if a coordinate is not water, its biome will be defined by the heat and moisture values TerrainType heatTerrainType = heatTerrainTypes [zIndex, xIndex]; TerrainType moistureTerrainType = moistureTerrainTypes [zIndex, xIndex]; // terrain type index is used to access the biomes table Biome biome = this.biomes [moistureTerrainType.index].biomes [heatTerrainType.index]; // assign the color according to the selected biome colorMap [colorIndex] = biome.color; } else { // water regions don't have biomes, they always have the same color colorMap [colorIndex] = this.waterColor; } } } // create a new texture and set its pixel colors Texture2D tileTexture = new Texture2D (tileWidth, tileDepth); tileTexture.filterMode = FilterMode.Point; tileTexture.wrapMode = TextureWrapMode.Clamp; tileTexture.SetPixels (mapa de colores); tileTexture.Apply (); volver tileTexture; }123456789101112131415161718192021222324252627282930313233343536privateTexture2D BuildBiomeTexture(TerrainType[,]heightTerrainTypes,TerrainType[,]heatTerrainTypes,TerrainType[,]moistureTerrainTypes){inttileDepth=heatTerrainTypes.GetLength(0);inttileWidth=heatTerrainTypes.GetLength(1);Color[]colorMap=newColor[tileDepth*tileWidth];for(intzIndex=0;zIndexFinally, we need to call this BuildBiomeTexture method inside the GenerateTile method. First, in the BuildBiomeTexture method we need to know which TerrainTypes were chosen for each noise variable type. So, we are going to add a new parameter in the BuildTexture method to save the chosenTerrainTypes.

        private Texture2D BuildTexture(float[,] heightMap, TerrainType[] terrainTypes, TerrainType[,] chosenTerrainTypes) { int tileDepth = heightMap.GetLength (0); int tileWidth = heightMap.GetLength (1); Color [] = new mapa de colores de color [tileDepth * tileWidth]; for (int zIndex = 0; zIndex In the Genera teTile method we create a 2D array for each noise variable type and save all the chosenTerrainTypes. Then, we can call the BuildBiomeTexture method using those 2D arrays. In the end, we are going to add another case statement when the visualization mode is equal to Biome.

        public void GenerateTile(float centerVertexZ, float maxDistanceZ) { // calculate tile depth and width based on the mesh vertices Vector3[] meshVertices = this.meshFilter.mesh.vertices; int tileDepth = (int) Mathf.Sqrt (meshVertices.Length); int tileWidth = tileDepth; // calcular las compensaciones en base a la posición del flotador baldosas offsetX = -this.gameObject.transform.position.x; flotar offsetZ = -this.gameObject.transform.position.z; // generate a heightMap using Perlin Noise float[,] heightMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heightWaves); // calculate vertex offset based on the Tile position and the distance between vertices Vector3 tileDimensions = this.meshFilter.mesh.bounds.size; float distanceBetweenVertices = tileDimensions.z / (float)tileDepth; float vertexOffsetZ = this.gameObject.transform.position.z / distanceBetweenVertices; // generate a heatMap using uniform noise float[,] uniformHeatMap = this.noiseMapGeneration.GenerateUniformNoiseMap (tileDepth, tileWidth, centerVertexZ, maxDistanceZ, vertexOffsetZ); // generate a heatMap using Perlin Noise float[,] randomHeatMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.heatWaves); float[,] heatMap = new float[tileDepth, tileWidth]; for (int zIndex = 0; zIndex < tileDepth; zIndex++) { for (int xIndex = 0; xIndex < tileWidth; xIndex++) { // mix both heat maps together by multiplying their values heatMap [zIndex, xIndex] = uniformHeatMap [zIndex, xIndex] * randomHeatMap [zIndex, xIndex]; // makes higher regions colder, by adding the height value to the heat map heatMap [zIndex, xIndex] += this.heatCurve.Evaluate(heightMap [zIndex, xIndex]) * heightMap [zIndex, xIndex]; } } // generate a moistureMap using Perlin Noise float[,] moistureMap = this.noiseMapGeneration.GeneratePerlinNoiseMap (tileDepth, tileWidth, this.levelScale, offsetX, offsetZ, this.moistureWaves); for (int zIndex = 0; zIndex < tileDepth; zIndex++) { for (int xIndex = 0; xIndex < tileWidth; xIndex++) { // makes higher regions dryer, by reducing the height value from the heat map moistureMap [zIndex, xIndex] -= this.moistureCurve.Evaluate(heightMap [zIndex, xIndex]) * heightMap [zIndex, xIndex]; } } // build a Texture2D from the height map TerrainType[,] chosenHeightTerrainTypes = new TerrainType[tileDepth, tileWidth]; Texture2D heightTexture = BuildTexture (heightMap, this.heightTerrainTypes, chosenHeightTerrainTypes); // build a Texture2D from the heat map TerrainType[,] chosenHeatTerrainTypes = new TerrainType[tileDepth, tileWidth]; Texture2D heatTexture = BuildTexture (heatMap, this.heatTerrainTypes, chosenHeatTerrainTypes); // build a Texture2D from the moisture map TerrainType[,] chosenMoistureTerrainTypes = new TerrainType[tileDepth, tileWidth]; Texture2D moistureTexture = BuildTexture (moistureMap, this.moistureTerrainTypes, chosenMoistureTerrainTypes); // build a biomes Texture2D from the three other noise variables Texture2D biomeTexture = BuildBiomeTexture(chosenHeightTerrainTypes, chosenHeatTerrainTypes, chosenMoistureTerrainTypes); switch (this.visualizationMode) { case VisualizationMode.Height: // assign material texture to be the heightTexture this.tileRenderer.material.mainTexture = heightTexture; descanso; case VisualizationMode.Heat: // assign material texture to be the heatTexture this.tileRenderer.material.mainTexture = heatTexture; descanso; case VisualizationMode.Moisture: // assign material texture to be the moistureTexture this.tileRenderer.material.mainTexture = moistureTexture; descanso; case VisualizationMode.Biome: // assign material texture to be the moistureTexture this.tileRenderer.material.mainTexture = biomeTexture; descanso; } // update the tile mesh vertices according to the height map UpdateMeshVertices (heightMap); }enum VisualizationMode {Height, Heat, Moisture, Biome}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778publicvoidGenerateTile(floatcenterVertexZ,floatmaxDistanceZ){// calculate tile depth and width based on the mesh verticesVector3[]meshVertices=this.meshFilter.mesh.vertices;inttileDepth=(int)Mathf.Sqrt(meshVertices.Length);inttileWidth=tileDepth;// calculate the offsets based on the tile positionfloatoffsetX=-this.gameObject.transform.position.x;floatoffsetZ=-this.gameObject.transform.position.z;// generate a heightMap using Perlin Noisefloat[,]heightMap=this.noiseMapGeneration.GeneratePerlinNoiseMap(tileDepth,tileWidth,this.levelScale,offsetX,offsetZ,this.heightWaves);// calculate vertex offset based on the Tile position and the distance between verticesVector3 tileDimensions=this.meshFilter.mesh.bounds.size;floatdistanceBetweenVertices=tileDimensions.z/(float)tileDepth;floatvertexOffse tZ=this.gameObject.transform.position.z/distanceBetweenVertices;// generate a heatMap using uniform noisefloat[,]uniformHeatMap=this.noiseMapGeneration.GenerateUniformNoiseMap(tileDepth,tileWidth,centerVertexZ,maxDistanceZ,vertexOffsetZ);// generate a heatMap using Perlin Noisefloat[,]randomHeatMap=this.noiseMapGeneration.GeneratePerlinNoiseMap(tileDepth,tileWidth,this.levelScale,offsetX,offsetZ,this.heatWaves);float[,]heatMap=newfloat[tileDepth,tileWidth];for(intzIndex=0;zIndexNow, you can set some values for the biomes table (which means setting the name and color of each biome). The figure below shows my result using the table I presented above and selecting some colors for each biome. Remember that the final result depends on the values you’ve used for all the noise variable types. So, feel free to try several different combinations until you find the one you prefer.


        Y con esto concluye este tutorial. In the next one we are going to finish you generated level adding more content such as trees, as well as adding a character that can walk around the level.

        Access part 3 here

        Mensajes relacionados

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *