Unity动态编辑Terrain地形(三) 自定义笔刷 您所在的位置:网站首页 新建笔刷 Unity动态编辑Terrain地形(三) 自定义笔刷

Unity动态编辑Terrain地形(三) 自定义笔刷

2023-09-19 00:49| 来源: 网络整理| 查看: 265

****

完整代码我已经上传到了我的Github上,需要的话可以直接去下载https://github.com/xdedzl/RunTimeTerrainEditor,里面有一个TerrainModilfyDemo的场景,我做了一个简单的UI用来测试,工程版本目前使用的是2019.2,但2018.3之后的版本应该都没问题,但Unity貌似不支持从2019回滚到2018,需要新建工程后将资源复制过去。注意编译环境需要是.net4.x,用3.5会报错。

这一篇用到了上一篇的函数Unity动态编辑Terrain(二)地势

****  

上一遍文章介绍了Terrain运行时编辑高度图,这一篇还是编辑高度,只是方式发生了改变,之前的编辑是在取到数据之后自己写算法决定笔刷的形状,这一次使用自定义笔刷,利用一张图片的透明度信息来决定笔刷的形状。

一、Unity编辑器中的自定义笔刷

Unity在编辑器模式下提供了自定义笔刷的功能,做以下几步即可

1.在根目录下创建一个名为Gizmos的文件夹

2.创建一个带透明通道的图片(推荐png格式,大小为256 * 256)并将图片命名为brush_0,brush_1,brush_2等,数字不可以有间隔

3.把图片放入Gizmos文件夹下,重启Unity

三步之后,就可以在Terrain的编辑界面看到我们的自定义笔刷了,unity在这里识别的是图片的Alpha通道,RGB的值对其不产生影响,所以我们做的图片只需要包含透明通道就可以了。

为了和Unity保持一致,我们在TerrainUtility中做的自定义笔刷功能利用Alpha通道计算,当然利用RGB当中的任一值都是可以的。

 

二、TerrainUtility自定义笔刷 1.图片的导入

在为Unity编辑器下的自定义笔刷导入图片时,我们不需要在导入后对图片做任何修改,但在自己的开发中,由于需要读写图片的数据,所以需要将图片ImportSettings中的  Read/Write Enables  选项勾上,如下图所示

2.双线性插值 

我们在上一篇创建的Utility工具类中创建一个异步静态函数用于插值,关于双线性插值的具体类容可参考https://blog.csdn.net/xdedzl/article/details/85414427

/// /// 对二维数组进行双线性插值 /// /// /// /// /// public static async Task BilinearInterp(float[,] array, int length_0, int length_1) { float[,] _out = new float[length_0, length_1]; int original_0 = array.GetLength(0); int original_1 = array.GetLength(1); float ReScale_0 = original_0 / ((float)length_0); // 倍数的倒数 float ReScale_1 = original_1 / ((float)length_1); float index_0; float index_1; int inde_0; int inde_1; float s_leftUp; float s_rightUp; float s_rightDown; float s_leftDown; await Task.Run(async () => { for (int i = 0; i < length_0; i++) { await Task.Run(() => { for (int j = 0; j < length_1; j++) { index_0 = i * ReScale_0; index_1 = j * ReScale_1; inde_0 = Mathf.FloorToInt(index_0); inde_1 = Mathf.FloorToInt(index_1); s_leftUp = (index_0 - inde_0) * (index_1 - inde_1); s_rightUp = (inde_0 + 1 - index_0) * (index_1 - inde_1); s_rightDown = (inde_0 + 1 - index_0) * (inde_1 + 1 - index_1); s_leftDown = (index_0 - inde_0) * (inde_1 + 1 - index_1); _out[i, j] = array[inde_0, inde_1] * s_rightDown + array[inde_0 + 1, inde_1] * s_leftDown + array[inde_0 + 1, inde_1 + 1] * s_leftUp + array[inde_0, inde_1 + 1] * s_rightUp; } }); } }); return _out; } 3. TerrainUtility之自定义笔刷

上一篇文章中的地形数据读取和写入搞清楚之后锕,自定义笔刷其实就很简单了。现在接着上一篇编写TerrainUtility,在代码中讲解。

首先为TerrainUtility添加一个静态变量,用于存储图片的透明通道信息,不需要没一次修改地形时还要读一遍图片数据

/// /// 用于记录要修改的Terrain目标数据,修改后统一刷新 /// private static Dictionary brushDic = new Dictionary();

然后创建一个初始化笔刷的静态函数并在静态构造函数中调用它 。

存储图片数据的二维数组大小先和图片的分辨率保持一致,在之后修改地形时再利用双线性插值做处理

我的自定义笔刷图片是保存在Resources/Terrain/Brushs下的(brush的复数形式是brushes)

/// /// 初始化笔刷 /// private static void InitBrushs() { Texture2D[] textures = Resources.LoadAll("Terrain/Brushs"); for (int i = 0, length = textures.Length; i < length; i++) { // 获取图片颜色ARGB信息 Color[] colors = textures[i].GetPixels(); // terrainData.GetHeightMap得到的二维数组是[y,x] float[,] alphas = new float[textures[i].height, textures[i].width]; for (int j = 0, length0 = textures[i].height, index = 0; j < length0; j++) { for (int k = 0, length1 = textures[i].width; k < length1; k++) { alphas[j, k] = colors[index].a; index++; } } brushDic.Add(i, alphas); } } /// /// 静态构造函数 /// static TerrainUtility() { deltaHeight = 1 / Terrain.activeTerrain.terrainData.size.y; terrainSize = Terrain.activeTerrain.terrainData.size; heightMapRes = Terrain.activeTerrain.terrainData.heightmapResolution; InitBrushs(); }

最后是供外部调用的利用自定义笔刷编辑地形的方法

/// /// 通过自定义笔刷编辑地形 /// /// public static async void ChangeHeightWithBrush(Vector3 center, float radius, float opacity, int brushIndex = 0, bool isRise = true) { int mapRadius = 0; int mapRadiusZ = 0; Vector2Int mapIndex = default(Vector2Int); float[,] heightMap = null; float limit = 0; Terrain terrain = InitHMArg(center, radius, ref mapIndex, ref heightMap, ref mapRadius, ref mapRadiusZ, ref limit); if (terrain == null) return; // 是否反转透明度 if (!isRise) opacity = -opacity; //修改高度图 await Task.Run(async () => { float[,] deltaMap = await Utility.BilinearInterp(brushDic[brushIndex], 2 * mapRadius, 2 * mapRadius); //float[,] deltaMap = await Utility.ZoomBilinearInterpAsync(brushDic[brushIndex], 2 * mapRadius, 2 * mapRadius); for (int i = 0; i < 2 * mapRadius; i++) { for (int j = 0; j < 2 * mapRadius; j++) { heightMap[i, j] += deltaMap[i, j] * deltaHeight * opacity; } } }); // 重新设置高度图 SetHeightMap(terrain, heightMap, mapIndex.x, mapIndex.y); }

自定义笔刷到这里就可以使用了,下一篇将介绍树木和草的动态编辑 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有