Unity地形动态生成的一些经验记录

这篇具有很好参考价值的文章主要介绍了Unity地形动态生成的一些经验记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        正经人谁写日记啊
                                                --汪涵

        我以前也一直认为一个正经忙碌的人哪有时间和精力去写日记呢?

        但是我错了,正因为忙绿没有精力,才要将重要的事情记录下来.

        我记录的东西也都是一些比较简单的Unity使用,C#程序设计的浅显的东西.

        但是谁知道呢,当我不停地探索之后,发出来的内容,也许对于那些刚入门的新人来说,会是一个引导.

关于地形(terrain)对象

        有四个重要参数,地形宽度,地形长度和地形高度,以及高度图分辨率.

        前面三个都好理解,主要是最后一个高度图分辨率,它是一个二维的平面图,用灰色深度来代表地形高低起伏的一个图.(最黑为0)

        它不直观展示出来,它只在程序计算地形高度时起作用,这就比较抽象.

        并且高度图的分辨率和地形分辨率(长*宽)可以不是一样的.

        更低的分辨率在生成地形高度时会变得更陡峭.(这不是创建陡峭地形的方法,这种只会影响玩家的游戏体验.)

        一个1024*1024的地形图如果配上513*513的高度图那么整个地形的起伏会非常平滑

但是一个10240*10240的地形图,如果配上513*513的高度图,那么整个地形的起伏会变得非常突兀.

因为每个高度图的像素点,将要承担10倍于1024*1024地形图的起伏.

   

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class CreateTerrain : MonoBehaviour
{
    public int seed;
    public float terrainWidth = 1024f;//地形的宽度
    public float terrainLength = 1024f;//地形的长度
    public int terrainHeight = 50;//地形的高度

    public Texture2D[] terrainTexture;
    public float perlinScale = 10.0f;
    public float[] perlinThreshold = {0.25f, 0.5f, 0.75f };

    private float randomOffsetX;
    private float randomOffsetY;
    public float centerHeight ;
    public float edgeHeight ;
    [Range(0, 2048)]
    public float slopeRadius;
    int terrainResolution;

    public static float Add(float a, float b)
    {
        float c = a + b;
        return c;
        //计算高度图分辨率
    }

    所以在创建高度图分辨率时应该和地形宽度和长度进行一个动态改变.

将长度和宽度相加,并且除以2,已达到清晰度和兼顾配置的需求.

 void Start()
    {
        terrainResolution = ((int)Add(terrainWidth,terrainLength)/2);
        Random.InitState(seed);
        randomOffsetX = Random.Range(0f, 10000f);
        randomOffsetY = Random.Range(0f, 10000f);

         Unity的任何游戏对象,都有2种坐标系,一个是世界坐标系,一个是本地坐标系.如果是3D游戏就是三维坐标系,如果是2D游戏就是二维坐标系.

        三维坐标系实在是让人头大,它是计算机图形学的一个基础概念.在镜头控制系统中是最绕人的,在地形创建中只需要关注他的世界坐标系即可.

 //创建一个简单的平面地形,并将其移动到(0,0,0)坐标
        Vector3 terrainPosition = new Vector3(terrainWidth,terrainHeight,terrainLength);
        TerrainData terrainData = new TerrainData();
        terrainData.heightmapResolution = terrainResolution;
        terrainData.size = terrainPosition;

        Vector3类型是一个三维向量结构体,将要创建的地形的长宽高赋值给它,并在后面将值赋值给创建出的地形对象.

        TerrainData是Terrain游戏对象里面的数据对象,通过terrainData对象你才能控制terrain的具体数据.(长宽高,位置等等.)

        而且还可以在游戏中实时控制地形.

        

 GameObject terrainObject = Terrain.CreateTerrainGameObject(terrainData);//创建地形的方法

        float terrainHalfWidth = terrainWidth * 0.5f;
        float terrainHalfLength = terrainLength * 0.5f;

        Vector3 terrainOffset = new Vector3(-terrainHalfWidth, terrainHeight, -terrainHalfLength);
        terrainObject.transform.position += terrainOffset;

        紧跟着就是创建地形方法.CreateTerrainGameObject();

        创建出的只是一个地形对象,如果不给他设置任何的数值,它将是一个默认的大小和位置.

        生成地形时是从地形的左下角作为(0,0)的,所以1024*1024的地形将会在右上角处达到(1024,1024)坐标.

        所以我还加了一个移动地形对象的操作,将地形中心点对准(0,0)

        方法就是将地形大小除以2,之后对先有地形的三维坐标进行更改.

地形对象的坐标系和其大小始终是一样的,当地形变大时,他的坐标系也会跟着变大,所以直接减去地形的长宽的一半,并将新的位置赋值给地形对象的.transform.position,就可以实现将地形中心点移动到世界坐标系的(0,0)了.

关于地形贴图(TerrainTexture2D)

        地形贴图支持多贴图功能,因此只能使用数组形式来进行赋值,哪怕只有一个贴图

        TerrainLayer[] terrainLayers = new TerrainLayer[terrainTexture.Length];//TerrainLayer支持多贴图功能,因此只能使用数组形式来进行赋值,哪怕只有一个贴图.
        for (int i = 0; i < terrainTexture.Length; i++)
        {
            terrainLayers[i] = new TerrainLayer();
            terrainLayers[i].diffuseTexture = terrainTexture[i];
            terrainLayers[i].tileSize = new Vector2(16, 16);
            terrainLayers[i].tileOffset = new Vector2(0, 0);
            terrainLayers[i].diffuseTexture.wrapMode = TextureWrapMode.Repeat;
            terrainLayers[i].normalMapTexture = null;
            terrainLayers[i].normalScale = 0.0f;
            terrainLayers[i].smoothness = 0.0f;
            terrainLayers[i].metallic = 0.0f;
        }

           上面代码接着地形创建.
        声明一个TerrainLayer[]类型的数组,这个数组就是贴图图层数组,他存储了所有的预先在unity界面里导入的贴图.
        我这里导入了4张贴图

                         unity 动态创建terrain,unity,游戏引擎

        所以会循环4次,依次新建地形贴图图层对象,将贴图赋予地形散射(漫反射) 纹理.

        设置贴图大小,设置地形层的纹理平铺偏移量,设置地形层散射纹理的包装模式为“重复”。这意味着当纹理在地形表面被平铺时,如果纹理范围超过地形表面,纹理将在边界处重复,从而覆盖整个地形表面,将地形层的法线贴图设置为 null,设置地形层的法线缩放值,设置地形层的光滑度,设置地形层的金属度.

terrainData.alphamapResolution = terrainResolution;
        terrainData.terrainLayers = terrainLayers;
        float[,,] splatmapData = new float[terrainData.alphamapResolution, terrainData.alphamapResolution,terrainTexture.Length];

        for (int i = 0; i < terrainData.alphamapResolution; i++)
        {
            for (int j = 0; j < terrainData.alphamapResolution; j++)
            {
                float perlinValue = Mathf.PerlinNoise((float)(i+randomOffsetX) / terrainData.alphamapResolution * perlinScale, (float)(j+randomOffsetY) / terrainData.alphamapResolution * perlinScale);
                if (perlinValue <= perlinThreshold[0])
                {
                    splatmapData[i, j, 0] = 1;
                    splatmapData[i, j, 1] = 0;
                    splatmapData[i, j, 2] = 0;
                    splatmapData[i, j, 3] = 0;
                }
                else if(perlinValue <= perlinThreshold[1])
                {
                    splatmapData[i, j, 0] = 0;
                    splatmapData[i, j, 1] = 1;
                    splatmapData[i, j, 2] = 0;
                    splatmapData[i, j, 3] = 0;
                }
                else if (perlinValue <= perlinThreshold[2])
                {
                    splatmapData[i, j, 0] = 0;
                    splatmapData[i, j, 1] = 0;
                    splatmapData[i, j, 2] = 1;
                    splatmapData[i, j, 3] = 0;
                }
                else
                {
                    splatmapData[i, j, 0] = 0;
                    splatmapData[i, j, 1] = 0;
                    splatmapData[i, j, 2] = 0;
                    splatmapData[i, j, 3] = 1;
                }
            }
        }
        terrainData.SetAlphamaps(0, 0, splatmapData);

         接下来进行贴图混合,I,J的最大值分别取地形高度图分辨率,perlinThreshold在前面变量声明时已经定义了一个固定的值,这里就是将4个贴图按25%的比例进行权重分配.

        循环内部利用柏林噪声和随机值偏移量来控制,柏林噪声生成的数值,使用随机值偏移量可以实现种子功能,在同一种子下,柏林噪声生成的噪声形态将会一样.

        unity 动态创建terrain,unity,游戏引擎

        对于贴图交界处,unity会自动混合平滑过渡,不需要另行编写代码.

        最后,设置阿尔法maps将这个三维数组赋值给刚刚创建的地形上.

关于地形高度图(terrainHeightMap)

float[,] circleHeightMap = CreateCircleHeightMap(terrainResolution, centerHeight / terrainHeight, edgeHeight / terrainHeight, slopeRadius);
 terrainData.SetHeights(0, 0, circleHeightMap);//应用高度图

float[,] CreateCircleHeightMap(int resolution, float centerHeight, float edgeHeight, float radius)
    {
        float[,] heightMap = new float[resolution, resolution];
        float halfWidth = terrainWidth * 0.5f;
        float halfLength = terrainLength * 0.5f;
        float maxDistSqr = radius;

        float transitionWidth = Mathf.Min(terrainWidth, terrainLength) * 0.2f;//计算高度过渡区的宽度.
        float transitionEndSqr = maxDistSqr+transitionWidth;//计算高度过渡区结束点.
        float transitionStartSqr = maxDistSqr;


        for (int i = 0; i < resolution; i++)
        {
            for (int j = 0; j < resolution; j++)
            {
                float distX = (i / (float)resolution) * terrainWidth - halfWidth;
                float distY = (j / (float)resolution) * terrainLength - halfLength;
                float distSqr = distX * distX+ distY * distY;
                distSqr = Mathf.Sqrt(distSqr);

                if (distSqr <= transitionStartSqr)
                {
                    heightMap[i, j] = centerHeight;
                }
                else if (distSqr <= transitionEndSqr)
                {
                    float t = (distSqr - transitionStartSqr) / (transitionEndSqr - transitionStartSqr);
                    heightMap[i, j] = Mathf.Lerp(centerHeight, edgeHeight, t);
                }
                else
                {

                    heightMap[i, j] = edgeHeight;
                }
                
            }
        }
        
        return heightMap;
    }

上述代码定义了一个从地图最中心点开始,根据输入的园的半径来获得一个高区平台,并且在平台边缘会平滑过渡到地形的低区.

        利用平方距离和勾股定理来确定当前的(i,j)点对于中心点的距离,t系数是一个归一化数值,负责平滑过渡插值,而平滑过渡的宽度区域,则是用transitionWidth的系数0.2f来决定的,越大的系数将会让过渡区域越宽,因为使用的是整个地形长宽的最大值,所以基本上在0.5f时就已经能覆盖整个地形.

根据实际情况来设置.

        下图为平台高度60,地区高度10,过渡区宽度系数0.2f,所生成的地形.符合预期.

unity 动态创建terrain,unity,游戏引擎文章来源地址https://www.toymoban.com/news/detail-709380.html

到了这里,关于Unity地形动态生成的一些经验记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 使用柏林噪声程序化生成地形

    参考教程链接 项目链接 👇对噪声和柏林噪声不了解的可以看下面这个讲解。 柏林函数简介   简单来说柏林噪声是一种连续的、渐变的噪声,不理解原理也无所谓,unity自带有Mathf.PerlinNoise(X-coordinate,Y-coordinate);我们可以根据这个来制作更有层次性的柏林噪声。你可以把这个

    2024年02月15日
    浏览(39)
  • 【简单详细】Unity生成地形和UI小地图实时展示人物位置的详细制作过程

    效果: 注意:一个 父物体 Map是一个纯白色image,放到左上角,要加 Mask组件 哦!!!       它的 两个子物体, 其中一个(map)是显示出地图纹理的image,另外一个(mapPlayer)是一个小点,我们也用image来表示,也就是效果图上的 绿点 。     我们把人物放进去,挂载脚本

    2024年02月05日
    浏览(46)
  • Unity PS4/PS5搭建开发环境/打补丁包和一些填坑经验

    首先,主机游戏PlayStation/Nintendo Switch都是比较闭塞的,开发者账号是必须的。 开发环境有两个部分,一是SDK Kit(各种开发调试环境),二是Unity的支持库(安装后才能在Unity中切换到PS平台); 需要严格按照SDK版本和对应支持的Unity版本安装开发环境。 1. 安装PS4/PS5 SDK: 登录开发

    2024年02月14日
    浏览(139)
  • Unity记录一些glsl和hlsl的着色器Shader逆向代码

    以下内容一般基于 GLSL 300 之后 以下某些代码行,是“伪代码“,绝大部分是renderDoc 逆向产生标准代码 本人OpenlGL零基础,也不打算重头学 目录 Clip() 剔除函数 discard; FS最终颜色输出 out 和最终颜色相加方程 从 discard; 命令可得知,一般通过透明度剔除, _26 == color.a _21.w 刚

    2024年02月07日
    浏览(46)
  • Unity动态创建材质球

    目录 一、获取贴图 1:从Assets文件中获取 2:从本地文件中获取 二、创建材质球 三、替换材质 总结 前言 我们在做一些AR类似与涂涂乐的功能时,经常会用到给模型替换材质球的功能。当我们的用户创建好贴图时就需要动态将新的贴图转换成材质贴在模型上。他的原理其实很

    2024年02月05日
    浏览(36)
  • 【Unity入门】22.动态创建实例

        大家好,我是Lampard~~     欢迎来到Unity入门系列博客,所学知识来自B站阿发老师~感谢    (1)Instantiate克隆创建对象     昨天我们学习了预制体这个概念,在编辑器中使用预制体创建游戏对象,并学习如何修改预制体等。 今天主要来学习,如何在脚本中利用预制体创

    2024年02月05日
    浏览(40)
  • Unity Audio -- (2)创建动态音效

            本节的目标是添加脚步声到角色身上,当角色走路时,触发动画事件并播放声音。         脚步声是我们在真实世界中常常被我们所忽视的声音,但脚步声能够传达出许多环境信息。你现在可以花一小段时间绕着你周围的环境走一走并仔细听听脚步声。如果可能

    2024年02月08日
    浏览(55)
  • 10. unity预制体、动态创建实体案例

    1. 预制体 预制体 Prefab 就是事先制作好的物体,可以提高开发效率。 1.1 预制体的制作 第一步 :在界面编辑器中先创建一个三维模型,比如创建一个正方体,命名为“骰子”,然后给它添加一个材质,再挂载一个旋转的脚本,如图所示: 第二步 :经过上述步骤制作好模型后

    2024年02月05日
    浏览(43)
  • 【unity实战】随机地下城生成2——绘制地图Tilemap的使用及一些技巧的使用(含源码)

    参考原视频链接: 【视频】:https://space.bilibili.com/370283072 注意 :本文为学习笔记记录,推荐支持原作者,去看原视频自己手敲代码理解更加深入 修改素材配置 切割图片 绘制瓦片地图 先新建我们的调色盘,保存好位置 拖入我们刚才切片好的素材 在房间预设体创建我们的瓦

    2024年02月13日
    浏览(41)
  • Unity 动态创建Mesh 基础方法与高级方法

    最近在做项目优化,注意到动态创建Mesh时,Unity提供了一套高级方法用于快速创建模型,特此记录学习一下。 关于Mesh的基本概念再次不在阐述,可以参考Unity Mesh 官方文档,介绍的很详细,其中 基础方法 包括:SetVertices、SetNormals、SetUVs、SetTriangles、SetIndices、SetColors、SetTa

    2024年03月27日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包