Unity记录3.3-地图-柏林噪声生成 2D 地图

文章介绍了使用Unity的Mathf.PerlinNoise函数生成2D柏林噪声地图的方法,包括基础的噪声生成、随机比例调整、区域固定和尺度可变的噪声生成。作者探讨了时间复杂度和如何加速生成过程,并分享了代码示例。目前的时间复杂度为O(H*W),作者寻求优化方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章首发及后续更新:https://mwhls.top/4486.html,无图/无目录/格式错误/更多相关请至首发页查看。
新的更新内容请到mwhls.top查看。
欢迎提出任何疑问及批评,非常感谢!

汇总:Unity 记录

摘要:柏林噪声生成 2D 地图,以二维数组表示。

目前时间复杂度是O(H*W),和地图大小成正比,但都是可以同步的操作,有人知道如何加速吗?

参考:
Unity 中文手册 2021.1
Unity 2D-Extras
Mathf.PerlinNoise

鼠标右键区域随机生成地图-2023/03/12
  • 最近事儿多,今天算是有空来搞这个。
  • 效果是右键点击,生成随机值为 0 1 的 10×10 的数组,并以鼠标位置为起点生成 tile。
    • 只要把这个数组替换成柏林噪声生成的数组就能实现更美观的地图了。
  • 不过在想要做一个 tile 字典时出了问题,没找到如何获得所有 tile,只能自己指定。
    • 我还想弄成 {ID, Name, Tile} 这种呢…
1. 粗糙的柏林噪声生成 2D 地图-2023/03/12

Unity_08_PerlinNoise2d.png

  • 代码如下:
int[,] generate_perlin2d(int height, int width){
    int[,] offset_array = new int[height, width];
    float thres = 0.3f;
    for (float i = 0; i < height; i++){
        for (float j = 0; j < width; j++){
            if (Mathf.PerlinNoise(i/height, j/width) < thres){
                offset_array[(int)i, (int)j] = 1;
            }
        }
    }
    return offset_array;
}
2. 随机的比例自定的 2D 柏林噪声-2023/3/15
  • 将传入 PerlinNoise 的坐标随机偏移,即可得到随机的柏林噪声。
  • 将噪声用不同的比例过滤,可得到比例不同的噪声。
    • 比例与噪声面积呈正相关,但斜率不是一条直线,为了减少不必要的计算量没有用排序来获得实际阈值位置。
  • 选择大于或小于某值的噪声,可得到噪声的山峰部分与山谷部分
  • 效果如下:

Unity_09_BasinAndMountain.png

  • 代码如下:
    int[,] generate_perlin2d(int height, int width, float thres_prob=0.1f, bool cropHigh=false){
        /// <summary>
        /// Generate 2d perlin noise, by crop a field of perlin noise.
        /// cropHigh True: get mountain. cropHigh: get basin.
        /// </summary>
        int[,] offset_array = new int[height, width];
        float[,] perlin_array = new float[height, width];
        float perlin_max = 0f;
        float perlin_min = 1f;
        float field_x = Random.Range(0, 1000);
        float field_y = Random.Range(0, 1000);
        float perlin_x, perlin_y;
        // perlin noise array
        for (float i = 0; i < height; i++){
            for (float j = 0; j < width; j++){
                perlin_x = field_x + i / height * 10f;
                perlin_y = field_y + j / width * 10f;
                float perlin_noise = Mathf.PerlinNoise(perlin_x, perlin_y);
                perlin_array[(int)i, (int)j] = perlin_noise;
                perlin_max = Mathf.Max(perlin_max, perlin_noise);
                perlin_min = Mathf.Min(perlin_min, perlin_noise);
            }
        }
    float thres_value = (perlin_max - perlin_min) * thres_prob + perlin_min;
    for (int i = 0; i &lt; height; i++){
        for (int j = 0; j &lt; width; j++){
            if ((perlin_array[i, j] &lt; thres_value &amp; cropHigh) | (perlin_array[i, j] &gt; thres_value &amp; !cropHigh)){
                offset_array[i, j] = 1;
            }
        }
    }
    return offset_array;
}</code></pre>
3. 区域固定的的比例自定的 2D 柏林噪声-2023/3/15
  • 和 2.随机的比例自定的 2D 柏林噪声 的效果差不多,但对于指定坐标,在不改变生成比例的前提下,每次的柏林噪声都是固定的。
    • 也就是我用鼠标左键生成地图,那么不论我怎么拖动鼠标,都只会在没有地图的地方生成新的,且过渡自然。
  • 代码如下,base_xbase_y 表示区域的基坐标,即鼠标点击的位置。
    int[,] generate_perlin2d(int height, int width, int base_x, int base_y, float thres_prob=0.5f, bool cropHigh=false){
        /// <summary>
        /// Generate 2d perlin noise, by crop a field of perlin noise.
        /// cropHigh True: get mountain. cropHigh: get basin.
        /// </summary>
        int[,] offset_array = new int[height, width];
        float[,] perlin_array = new float[height, width];
        float perlin_max = 0f;
        float perlin_min = 1f;
        float perlin_noise;
        for (int i = 0; i < height; i++){
            for (int j = 0; j < width; j++){
                perlin_noise = Mathf.PerlinNoise((i+base_x)/10f, (j+base_y)/10f);
                perlin_array[i, j] = perlin_noise;
                perlin_max = Mathf.Max(perlin_max, perlin_noise);
                perlin_min = Mathf.Min(perlin_min, perlin_noise);
            }
        }
    float thres_value = (perlin_max - perlin_min) * thres_prob + perlin_min;
    for (int i = 0; i &lt; height; i++){
        for (int j = 0; j &lt; width; j++){
            if ((perlin_array[i, j] &lt; thres_value &amp; cropHigh) | (perlin_array[i, j] &gt; thres_value &amp; !cropHigh)){
                offset_array[i, j] = 1;
            }
        }
    }
    return offset_array;
}</code></pre>
4. 尺度可变的区域固定的的比例自定的 2D 柏林噪声-2023/3/15
  • 相较于 3. 区域固定的的比例自定的 2D 柏林噪声,现在的区域尺度可变,由 scale 控制,即原来的区域可能有 20 个山峰,scale 乘 10 后,山峰数量可能减少至 2 个。
    • 缺点是如果 scale=1 时,那同一区域山峰和山谷叠加后可能无法覆盖整个区域。
    • 为了解决整数问题,统一加了 0.5 偏置。
  • 效果如下,左下角 scale=1,右下角 scale=5,左上角 scale=10,右上角 scale=50

Unity_010_ScaleChange.png

  • 代码如下:
    int[,] generate_perlin2d(int width, int height, int base_x, int base_y, float scale=10f, float thres_prob=0.5f, bool cropHigh=false){
        /// <summary>
        /// Generate 2d perlin noise, by crop a field of perlin noise.
        /// cropHigh True: get mountain. cropHigh: get valley.
        /// </summary>
        int[,] offset_array = new int[width, height];
        float[,] perlin_array = new float[width, height];
        float perlin_max = 0f;
        float perlin_min = 1f;
        float perlin_noise;
        for (int i = 0; i < width; i++){
            for (int j = 0; j < height; j++){
                perlin_noise = Mathf.PerlinNoise(((i+base_x)/scale + 0.5f), ((j+base_y)/scale + 0.5f));
                perlin_array[i, j] = perlin_noise;
                perlin_max = Mathf.Max(perlin_max, perlin_noise);
                perlin_min = Mathf.Min(perlin_min, perlin_noise);
            }
        }
    float thres_value = (perlin_max - perlin_min) * thres_prob + perlin_min;
    for (int i = 0; i &lt; width; i++){
        for (int j = 0; j &lt; height; j++){
            if ((perlin_array[i, j] &lt; thres_value &amp; cropHigh) | (perlin_array[i, j] &gt; thres_value &amp; !cropHigh)){
                offset_array[i, j] = 1;
            }
        }
    }
    return offset_array;
}</code></pre>
长宽搞反了-2023/03/17
  • 不好意思,HWC 看多了,下意识把坐标也 HWC 了,实际上是 xy 坐标,也就是 WH
    • 换句话说,前面的代码,除了 4. 尺度可变的区域固定的的比例自定的 2D 柏林噪声 是我刚刚修正过的,其它的 height 实际上是 width,width 则是 height。
<think>好的,用户想了解Unity中基于柏林噪声的随机地图生成的优化方向。首先,我需要回忆一下柏林噪声的基本原理和在Unity中的应用场景。柏林噪声常用于生成自然地形,但因为计算量较大,可能需要优化。 用户可能是一名游戏开发者,正在开发随机生成地图的游戏,遇到了性能问题,或者在寻找提高生成效率的方法。他可能希望优化生成速度,减少卡顿,或者让地图更大更复杂而不影响性能。 首先,我得从算法层面考虑优化。比如,降低柏林噪声的计算复杂度,使用SIMD指令或者多线程。柏林噪声的插值计算可能比较耗时,简化计算步骤或者查找表可能会有帮助。另外,分形噪声的叠加层数太多会影响性能,减少octaves或者使用更高效的算法如域扭曲优化。 然后是内存和缓存的优化。预处理噪声数据,使用缓存重用已经计算的值,或者将噪声纹理预生成并存储,运行时通过采样获取,这样可以减少实时计算的压力。 接着是渲染方面的优化。比如LOD,根据距离调整地形细节,减少远处顶点的计算量。GPU加速也是一个方向,把柏林噪声的计算放到Compute Shader或者Shader中,利用GPU并行计算能力。 地图分块和动态加载也很重要,避免一次性生成整个地图,而是按需生成和卸载区块,这对开放世界游戏尤其有用。同时,考虑使用对象池管理地形块,减少内存分配开销。 还有参数调整,比如降低噪声采样频率,适当减少地图尺寸或分辨率,平衡质量和性能。或者使用更简单的噪声算法如Value Noise,再结合柏林噪声进行混合。 用户可能还关心如何在不明显降低视觉效果的前提下进行优化,所以需要权衡参数调整的影响。比如减少octaves可能会让地形更平滑,但合理调整参数可以保持视觉效果。此外,是否可以利用Job System和Burst编译器来加速C#代码,这也是Unity特有的优化手段。 需要检查有没有重复计算的情况,比如多个地形特征使用相同的噪声参数,可以考虑共享计算的结果。另外,异步生成和分帧处理可以避免卡顿,将生成任务分散到多帧中完成。 最后,还要考虑目标平台的性能差异,移动端可能需要更激进的优化,而PC端可以适当放宽。综合这些方向,给出结构清晰的建议,帮助用户逐步优化他们的随机地图生成系统。</think>在Unity中使用柏林噪声生成随机地图时,可以通过以下优化方向提升性能和表现效果: --- ### 一、算法层优化 #### 1. 柏林噪声计算简化 - **插值优化**:将传统的三次插值(Cubic)替换为线性插值(Linear),牺牲少量平滑度换取计算效率 - **预计算梯度表**:将柏林噪声中的梯度向量预先存入查找表(LUT),减少实时计算量 - **SIMD指令加速**:利用Unity的Burst编译器,通过向量化计算并行处理多个噪声点 #### 2. 分形噪声优化 - **Octaves压缩**:减少分形叠加层数(如从8层降为5层),通过调整幅度权重补偿细节损失 - **域扭曲替代**:使用更高效的湍流算法替代传统分形叠加,示例代码: ```csharp float WarpedNoise(float x, float y) { float q = Perlin(x, y); float r = Perlin(x + 5.2f*q, y + 1.3f*q); return r; } ``` --- ### 二、内存与缓存优化 #### 1. 数据重用策略 - **区块缓存**:对已生成噪声数据建立LRU缓存,适合重复访问的地形区块 - **纹理烘焙**:预生成噪声图保存为Texture2D,通过`Texture.GetPixel()`采样替代实时计算 #### 2. 内存访问优化 - **结构体布局**:使用`[StructLayout(LayoutKind.Sequential)]`保证数据连续存储 - **分块加载**:将地图划分为N×N区块,按需动态加载(推荐尺寸:64×64~256×256) --- ### 三、渲染加速方案 #### 1. LOD多级细化 ```csharp // 示例LOD切换逻辑 void UpdateLOD() { float dist = Vector3.Distance(player.position, chunk.position); currentLOD = dist > 50f ? 2 : (dist > 20f ? 1 : 0); } ``` - 配合Mesh Simplification工具生成多精度模型 #### 2. GPU加速方案 - **Compute Shader实现**:将噪声计算移植到GPU,吞吐量可提升10-100倍 - **Shader直接生成**:在片段着色器中动态计算噪声,适合表面细节生成 --- ### 四、架构优化 #### 1. 任务并行化 ```csharp // 使用C# Job System public struct NoiseJob : IJobParallelFor { public NativeArray<float> results; public void Execute(int index) { // 柏林噪声计算 } } ``` - 结合JobSystem+Burst实现多线程加速 #### 2. 动态调度策略 - **帧预算控制**:限制每帧最大生成区块数(如2-4个/帧) - **优先级队列**:按玩家运动方向预测加载优先级 --- ### 五、混合技术方案 1. **分层混合**:底层使用柏林噪声生成宏观地形,表层使用更简单的噪声生成细节 2. **过程式装饰**:在噪声生成的丘陵区域动态添加岩石/植被预制体 3. **边缘模糊**:在区块接缝处添加过渡区域,配合噪声混合实现无缝拼接 --- ### 优化效果对比 | 优化方案 | 生成速度提升 | 内存占用降低 | 适用场景 | |---------|-------------|-------------|---------| | SIMD加速 | 3-5x | - | CPU密集型 | | GPU计算 | 10-50x | 增加显存占用 | 大规模地形 | | LOD系统 | 2-3x | 30-50% | 开放世界 | 建议通过Profiler重点检测`PerlinNoise()`函数的调用耗时和GC.Alloc情况,逐步应用上述优化策略。实际项目中通常需要多种技术组合使用才能达到最佳效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值