文章首发及后续更新:https://mwhls.top/4486.html,无图/无目录/格式错误/更多相关请至首发页查看。
新的更新内容请到mwhls.top查看。
欢迎提出任何疑问及批评,非常感谢!
汇总:Unity 记录
摘要:柏林噪声生成 2D 地图,以二维数组表示。
目前时间复杂度是O(H*W),和地图大小成正比,但都是可以同步的操作,有人知道如何加速吗?
参考:
Unity 中文手册 2021.1
Unity 2D-Extras
Mathf.PerlinNoise
目录
鼠标右键区域随机生成地图-2023/03/12
1. 粗糙的柏林噪声生成 2D 地图-2023/03/12
2. 随机的比例自定的 2D 柏林噪声-2023/3/15
3. 区域固定的的比例自定的 2D 柏林噪声-2023/3/15
4. 尺度可变的区域固定的的比例自定的 2D 柏林噪声-2023/3/15
长宽搞反了-2023/03/17
鼠标右键区域随机生成地图-2023/03/12
- 最近事儿多,今天算是有空来搞这个。
- 效果是右键点击,生成随机值为 0 1 的 10×10 的数组,并以鼠标位置为起点生成 tile。
- 只要把这个数组替换成柏林噪声生成的数组就能实现更美观的地图了。
- 不过在想要做一个 tile 字典时出了问题,没找到如何获得所有 tile,只能自己指定。
- 我还想弄成
{ID, Name, Tile}
这种呢…
- 我还想弄成
1. 粗糙的柏林噪声生成 2D 地图-2023/03/12
- 参考:
- unity柏林噪声生成2d随机地图
- Mathf.PerlinNoise only returns the float 0.4652731 at every given position
- 第一篇参考实际上没啥作用,不过我是照着它写的。
- 简单介绍一下:
- 柏林噪声的图大家应该都见过,没见过的去 Unity 手册看看:https://docs.unity.cn/cn/2021.3/ScriptReference/Mathf.PerlinNoise.html
- 每个位置的像素值与相邻位置的像素值相近,并且按照相同的梯度改变。
- 返回值区间为 [0, 1],所以我们如果拿 0.5 作为阈值,那原本较白的地方就会变得纯白,较黑的地方不变。
- 这种截断类似对山峰的截断,我们留下某个高度以上,如下面效果图,有 tile 的地方就是山峰,没有 tile 的地方就是被截断掉的。
- 函数注意点:
- 输入浮点数,输出值域 [0, 1]。
- 输入整数时,函数始终输出 0.4652731。
- 效果如下图
- 代码如下:
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
的坐标随机偏移,即可得到随机的柏林噪声。 - 将噪声用不同的比例过滤,可得到比例不同的噪声。
- 比例与噪声面积呈正相关,但斜率不是一条直线,为了减少不必要的计算量没有用排序来获得实际阈值位置。
- 选择大于或小于某值的噪声,可得到噪声的山峰部分与山谷部分
- 效果如下:
- 代码如下:
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 < height; i++){ for (int j = 0; j < width; j++){ if ((perlin_array[i, j] < thres_value & cropHigh) | (perlin_array[i, j] > thres_value & !cropHigh)){ offset_array[i, j] = 1; } } } return offset_array; }</code></pre>
3. 区域固定的的比例自定的 2D 柏林噪声-2023/3/15
- 和 2.随机的比例自定的 2D 柏林噪声 的效果差不多,但对于指定坐标,在不改变生成比例的前提下,每次的柏林噪声都是固定的。
- 也就是我用鼠标左键生成地图,那么不论我怎么拖动鼠标,都只会在没有地图的地方生成新的,且过渡自然。
- 代码如下,
base_x
和base_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 < height; i++){ for (int j = 0; j < width; j++){ if ((perlin_array[i, j] < thres_value & cropHigh) | (perlin_array[i, j] > thres_value & !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
:
- 代码如下:
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 < width; i++){ for (int j = 0; j < height; j++){ if ((perlin_array[i, j] < thres_value & cropHigh) | (perlin_array[i, j] > thres_value & !cropHigh)){ offset_array[i, j] = 1; } } } return offset_array; }</code></pre>
长宽搞反了-2023/03/17
- 不好意思,HWC 看多了,下意识把坐标也 HWC 了,实际上是 xy 坐标,也就是 WH
- 换句话说,前面的代码,除了 4. 尺度可变的区域固定的的比例自定的 2D 柏林噪声 是我刚刚修正过的,其它的 height 实际上是 width,width 则是 height。