欢迎光临散文网 会员登陆 & 注册

Unity 随机地形生成(Terrain 组件)

2023-02-26 20:45 作者:丿Ares丶-倾城  | 我要投稿

效果图:


噪声生成

创建 Noise.cs 脚本


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public static class Noise

{

    public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, int seed, float scale, int octaves, float persistance, float lacunarity, Vector2 offset)

    {

        float[,] noiseMap = new float[mapWidth, mapHeight];


        System.Random prng = new System.Random(seed);

        Vector2[] octavesOffset = new Vector2[octaves];

        for (int i = 0; i < octaves; i++)

        {

            float offsetX = prng.Next(-100000, 100000) + offset.x;

            float offsetY = prng.Next(-100000, 100000) + offset.y;

            octavesOffset[i] = new Vector2(offsetX, offsetY);

        }


        if (scale <= 0)

        {

            scale = 0.0001f;

        }


        float maxNoiseHeight = float.MinValue;

        float minNoiseHeight = float.MaxValue;


        for (int y = 0; y < mapHeight; y++)

        {

            for (int x = 0; x < mapWidth; x++)

            {


                float amplitude = 1;

                float frequency = 1;

                float noiseHeight = 0;


                for (int i = 0; i < octaves; i++)

                {

                    float sampleX = (x + offset.x) / scale * frequency + octavesOffset[i].x;

                    float sampleY = (y + offset.y) / scale * frequency + octavesOffset[i].y;


                    float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;

                    noiseHeight += perlinValue * amplitude;


                    amplitude *= persistance;

                    frequency *= lacunarity;

                }


                if (noiseHeight > maxNoiseHeight)

                {

                    maxNoiseHeight = noiseHeight;

                }

                else if (noiseHeight < minNoiseHeight)

                {

                    minNoiseHeight = noiseHeight;

                }

                noiseMap[x, y] = noiseHeight;

            }

        }


        for (int y = 0; y < mapHeight; y++)

        {

            for (int x = 0; x < mapWidth; x++)

            {

                noiseMap[x, y] = Mathf.InverseLerp(minNoiseHeight, maxNoiseHeight, noiseMap[x, y]);

            }

        }


        return noiseMap;

    }

}


地形显示

创建 TerrainDisplay.cs 脚本 用于创建 Terrain 对象

terrainName:生成的游戏对象名称

terrainMaterial:生成的 Terrain 使用的材质

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class TerrainDisplay : MonoBehaviour

{

    // 地形名称

    public string terrainName = "Terrain";


    // 地形材质

    public Material terrainMaterial;


    /// <summary>

    /// 创建 Terrain

    /// </summary>

    public void CreateTerrain(TerrainData terrainData)

    {

        // 查找游戏对象

        GameObject terrainObj = GameObject.Find(this.terrainName);

        if (terrainObj == null)

        {

            // 创建游戏对象

            terrainObj = new GameObject(this.terrainName);

            terrainObj.name = this.terrainName;

            // 添加地形组件

            terrainObj.AddComponent<Terrain>();

            // 添加地形碰撞体组件

            terrainObj.AddComponent<TerrainCollider>();

        }

        Terrain terrain = terrainObj.GetComponent<Terrain>();

        TerrainCollider terrainCollider = terrainObj.GetComponent<TerrainCollider>();

        terrain.terrainData = terrainData;

        terrainCollider.terrainData = terrainData;

terrain.materialTemplate = terrainMaterial;

    }

}

CreateTerrain 的参数传入的是 TerrainData 类型 而上面生成噪声图是  float[,] 二维数组

所以我们还需要对数据进行处理才能使用

地形生成

创建 TerrainGenerator.cs 脚本

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class TerrainGenerator : MonoBehaviour

{


    public static TerrainData CreateTerrainData(float[,] heightMap, float terrainHeight, int heightmapResolution)

    {

        TerrainData terrainData = new TerrainData();


        int width = heightMap.GetLength(0);

        int height = heightMap.GetLength(1);


        terrainData.heightmapResolution = heightmapResolution;

terrainData.alphamapResolution = heightmapResolution - 1;


        terrainData.size = new Vector3(width, terrainHeight, height);

        terrainData.SetHeights(0, 0, heightMap);


        return terrainData;

    }


}

接下来我们调用上面两个方法来生成地形

修改 TerrainGenerator.cs

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class TerrainGenerator : MonoBehaviour

{

    // 高度图分辨率

    public enum HeightmapResolution

    {

        h33 = 33,

        h65 = 65,

        h129 = 129,

        h257 = 257,

        h513 = 513,

        h1025 = 1025,

        h2049 = 2049,

        h4097 = 4097,


    }

    public HeightmapResolution heightmapResolution = HeightmapResolution.h33;


    // 缩放

    public float scale;


    // 抵消

    public int octaves;

    // 持续

    [Range(0, 1)]

    public float persistance;

    // 空隙

    public float lacunarity;

    // 种子

    public int seed;

    // 偏移

    public Vector2 offset;

    // 自动更新

    public bool autoUpdate;

    // 地形高度

    public float terrainHeight = 1;


    public void GenerateMap()

    {

        TerrainDisplay display = FindObjectOfType<TerrainDisplay>();


        float[,] noiseMap = Noise.GenerateNoiseMap((int)heightmapResolution,

            (int)heightmapResolution, seed, scale, octaves, persistance, lacunarity, offset);


        display.CreateTerrain(CreateTerrainData(noiseMap, terrainHeight, (int)heightmapResolution));

    }



    public static TerrainData CreateTerrainData(float[,] heightMap, float terrainHeight, int heightmapResolution)

    {

        TerrainData terrainData = new TerrainData();


        int width = heightMap.GetLength(0);

        int height = heightMap.GetLength(1);


        terrainData.heightmapResolution = heightmapResolution;

terrainData.alphamapResolution = heightmapResolution - 1;


        terrainData.size = new Vector3(width, terrainHeight, height);

        terrainData.SetHeights(0, 0, heightMap);


        return terrainData;

    }



}

现在地形生成部分就完成了

接下来我们还需要实现一个 点击按钮 生成地形的功能

创建 TerrainEditor.cs 脚本


using System.Collections;

using System.Collections.Generic;

using UnityEditor;

using UnityEngine;


[CustomEditor(typeof (TerrainGenerator))]

public class TerrainEditor : Editor

{

    public override void OnInspectorGUI()

    {


        TerrainGenerator terrainGenerator = (TerrainGenerator)target;


        if (DrawDefaultInspector())

        {

            if (terrainGenerator.autoUpdate)

            {

                terrainGenerator.GenerateMap();

            }

        }


        if (GUILayout.Button("Generate"))

        {

            terrainGenerator.GenerateMap();

        }


    }

}



地形生成测试

添加 空游戏对象命名为 Map Generator


将刚才编写的这两个脚本添加到对象上


调整面板参数


点击 Generate 按钮即可生成地形


纹理绘制

想要在 Terrain 上绘制纹理需要在 Terrain 中添加 Layer

在绘制纹理前 我们先实现 添加 Layer 的功能

TerrainDisplay.cs 添加代码

[System.Serializable]

public struct TerrainLayerInfo

{

    public TerrainLayer terrainLayer;

    [Range(0, 1)]

    public float layerMaxHeight;

    [Range(0, 1)]

    public float layerMinHeight;

}

添加到最后即可


TerrainDisplay.cs 添加字段

public TerrainLayerInfo[] terrainLayerInfos;


TerrainDisplay.cs 中添加方法


public void TerrainLayerGenerator(Terrain terrain)

{

    TerrainLayer[] terrainLayers = new TerrainLayer[terrainLayerInfos.Length];

    for (int i = 0; i < terrainLayerInfos.Length; i++)

    {

        terrainLayers[i] = terrainLayerInfos[i].terrainLayer;

    }

    terrain.terrainData.terrainLayers = terrainLayers;

}



在脚本中添加 Layer 并点击 Generate 测试,可以看见地形已经绘制上纹理了


接下来实现基于高度绘制不同的纹理的功能

TerrainDisplay.cs 添加代码


public void TerrainLayerAlphaGenerator(Terrain terrain, float[,,] terrainAlphamp, float heightmapResolution)

{

    float[,] terrainHeightMap = terrain.terrainData.GetHeights(0, 0, (int)heightmapResolution, (int)heightmapResolution);


    for (int layerIndex = 0; layerIndex < terrainAlphamp.GetLength(2); layerIndex++)

    {

        for (int y = 0; y < terrainAlphamp.GetLength(1); y++)

        {

            for (int x = 0; x < terrainAlphamp.GetLength(0); x++)

            {

                if (terrainHeightMap[x, y] <= terrainLayerInfos[layerIndex].layerMaxHeight &&

                    terrainHeightMap[x, y] >= terrainLayerInfos[layerIndex].layerMinHeight)

                {

                    terrainAlphamp[x, y, layerIndex] = 1;

                }

                else

                {

                    terrainAlphamp[x, y, layerIndex] = 0;

                }

            }

        }

    }


    terrain.terrainData.SetAlphamaps(0, 0, terrainAlphamp);

}



CreateTerrain 方法中添加


float[,,] alphamaps = terrain

                .terrainData

                .GetAlphamaps(0, 0, terrain.terrainData.alphamapWidth, terrain.terrainData.alphamapHeight);

        TerrainLayerAlphaGenerator(terrain,alphamaps,terrainData.heightmapResolution);



纹理绘制测试

添加 Layer 并点击 Generate 纹理成功绘制


参考视频:https://www.bilibili.com/video/BV1sJ411e7nt/?share_source=copy_web&;vd_source=3ff483a8930d34f7fca4f473c89b4c15


Unity 随机地形生成(Terrain 组件)的评论 (共 条)

分享到微博请遵守国家法律