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.双线性插值
/// <summary>
/// 对⼆维数组进⾏双线性插值
/// </summary>
/// <param name="array"></param>
/// <param name="length_0"></param>
/// <param name="length_1"></param>
/// <returns></returns>
public static async Task<float[,]> 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添加⼀个静态变量,⽤于存储图⽚的透明通道信息,不需要没⼀次修改地形时还要读⼀遍图⽚数据
/// <summary>
/// ⽤于记录要修改的Terrain⽬标数据,修改后统⼀刷新index复数
/// </summary>
private static Dictionary<int, float[,]> brushDic = new Dictionary<int, float[,]>();
然后创建⼀个初始化笔刷的静态函数并在静态构造函数中调⽤它 。
存储图⽚数据的⼆维数组⼤⼩先和图⽚的分辨率保持⼀致,在之后修改地形时再利⽤双线性插值做处理我的⾃定义笔刷图⽚是保存在Resources/Terrain/Brushs下的(brush的复数形式是brushes)
/
// <summary>
/// 初始化笔刷
/// </summary>
private static void InitBrushs()
{
Texture2D[] textures = Resources.LoadAll<Texture2D>("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);
}
}
/// <summary>
/// 静态构造函数
/// </summary>
static TerrainUtility()
{
deltaHeight = 1 / ainData.size.y;
terrainSize = ainData.size;
heightMapRes = ainData.heightmapResolution;
InitBrushs();
}
最后是供外部调⽤的利⽤⾃定义笔刷编辑地形的⽅法
/// <summary>
/// 通过⾃定义笔刷编辑地形
/// </summary>
/// <param name="terrain"></param>
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);
}
⾃定义笔刷到这⾥就可以使⽤了,下⼀篇将介绍树⽊和草的动态编辑

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。