由于矿物生成的原理与地图生成的原理一样,都是根据随机噪音图像来生成对应的砖块,所以需要将生成噪音图的函数进行重构,之后每个矿物都生成唯一的贴图来参考生成位置。
-
重构噪音函数
其中增加了3个参数,用来保证每个砖块都能生成自己唯一的图像。由于我的seed取值范围为
seed = Random.Range(10000, 20000)
所以在 Mathf.PerlinNoise 的参数中除以了1000,为的是增加 frequent 的取值范围,在Unity页面中调试时可以更加方面修改参数,并让效果图更明显
此外还修改了生成的像素颜色,优化成仅有黑白两色的图像,能更清楚的看到效果
public void GenerateNoiseTexture(float frequent, float limit ,Texture2D noiseTexture)
//frequent 噪音频率,越小噪音越不明显,越大越明显
//limit 临界值,大于临界值则为白,小于临界值为黑
//noiseTexture 最终生成的噪音图像
{
for (int x = 0; x < noiseTexture.width; x++)
{
for (int y = 0; y < noiseTexture.height; y++)
{
float v = Mathf.PerlinNoise((x + seed)* frequent/1000, (y + seed ) * frequent/1000);
if (v > limit)
noiseTexture.SetPixel(x, y, Color.white);
else noiseTexture.SetPixel(x, y, Color.black);
//SetPixel函数根据像素位置,只有当噪音值大于阈值才会生成对应x,y坐标下的像素颜色为白色像素,否则生成黑色像素
}
}
noiseTexture.Apply(); //将上面点生成对应的二位噪音图保存
}
-
生成矿物和地图的噪音图
增加了以下参数,对应的作用也注释上了
public class Sandbox_seed : MonoBehaviour
{
[Header("世界参数")]
public Texture2D mapNoiseTexture; //地图噪音材质图
public float caveFreq = 50; //山洞参数,数值越大洞越多
public float caveSize = 0.25f;
[Header("矿物参数")]
public Texture2D copperNoiseTexture; //铜矿噪音材质图
public Texture2D goldNoiseTexture; //金矿噪音材质图
public int copperFreq = 100; //铜矿噪音参数
public int goldFreq = 200; //金矿噪音参数
public float copperSize = 0.3f; //铜矿生成概率
public float goldSize = 0.25f; //金矿生成概率
}
为了方便调试参数,增加了以下系统自带的函数OnValidate(),这个函数的作用是在Unity页面中对参数进行修改时,立刻更新对应的对象。在这个代码中,就是为了能更方便调试噪音参数。注意矿物材质的第二个参数和世界地图材质不一样。
public class Sandbox_seed : MonoBehaviour
{
public void OnValidate()
{
if (mapNoiseTexture == null)
//如果一开始的mapNoiseTexture为空,则重新生成一个。
//因为原本mapNoiseTexture是只有在开始运行才会生成的,如果不加这句话就会报错
{
mapNoiseTexture = new Texture2D(worldwidth, worldheight);
copperNoiseTexture = new Texture2D(worldwidth, worldheight);
goldNoiseTexture = new Texture2D(worldwidth, worldheight);
}
GenerateNoiseTexture(caveFreq, caveSize, mapNoiseTexture);
//注意这里的是 1 减去矿物尺寸,临界值为1时全部为白即全图生成对应矿物,而0为黑即不生成矿物
//这点是和地图的洞穴参数相反。
GenerateNoiseTexture(copperFreq, 1f - copperSize, copperNoiseTexture);
GenerateNoiseTexture(goldFreq, 1f - goldSize, goldNoiseTexture);
}
}
这个是世界地图的噪音材质图,白色为地砖位置
这个是矿物的噪音材质图,白色的为矿物位置
-
添加矿物地砖类
和之前的石头、草地、泥土一样,在Unity中手动创建地砖类,并添加上名字和贴图
然后在地砖集的类中添加这两个砖块
同时在生成的地砖集的类里拖拽地砖类
这样就能在生成地图的时候创建对应的砖块了。
-
生成地图
由于添加了矿物,需要调整生成砖块的顺序和逻辑。
注意的是,由于我们生成砖块的判断逻辑是
1. 这个位置是小于地表高度的 【y < surfaceNoise】
2. 这个位置在世界噪音材质中为白色【mapNoiseTexture.GetPixel(x, y).r == 1】
满足这两个条件才能生成砖块。【PlaceTile(tileClass, x, y);】
根据判断的顺序,我这边的矿物是会在地表生成,且优先生成铜矿。而且铜矿上不会长树。
public void GenerateTexture()
{
/* 前面代码不动 */
TileClass tileClass; //创建局部变量用来进行判断该地砖应该是什么类型
//判断该生成的是什么地貌
if(y < surfaceNoise && mapNoiseTexture.GetPixel(x, y).r == 1)
{
if (copperNoiseTexture.GetPixel(x,y).r == 1) //判断在铜矿材质中这个位置是否为白色,是白色就生成铜矿,不是则进行下面的判断
{
tileClass = tileAtlas.copper;
}
else if (goldNoiseTexture.GetPixel(x, y).r == 1) //判断在铜矿材质中这个位置是否为白色,是白色就生成金矿,不是则进行下面的判断
{
tileClass = tileAtlas.gold;
}
else if (y < surfaceNoise - dirtNoise) tileClass = tileAtlas.stone; //地表高度减去泥土厚度的为岩石
else if (y <= surfaceNoise - 1) tileClass = GetTileClass(tileAtlas.dirts); //距离地表1且不是岩石的为泥土
else tileClass = GetTileClass(tileAtlas.grasses); //剩下的,即地表高度等于1的为草地
PlaceTile(tileClass, x, y); //在(x,y)坐标生成砖块
if (tileClass.tileName.Contains("Grass") && worldTileVectorList.Contains(new Vector2(x, y))) //如果这个砖块是草地砖块且生成成功,则进行判断是否生成树
{
float treeRandom = Random.Range(0f, 1f);
if (treeChance >= treeRandom) //当参数大于随机值,则生成树
{
PlaceTree(x, y);
}
}
}
}
}
}
补充一点:
Color.white 的RGBA值如下,鼠标移动到该参数时也会自己展示
Color.black的RGBA值如下
-
最终效果
在Start() 函数中添加上生成材质的函数和生成地图砖块的函数
void Start()
{
seed = Random.Range(10000, 20000);
//先声明噪音图的材质类
mapNoiseTexture = new Texture2D(worldwidth, worldheight);
copperNoiseTexture = new Texture2D(worldwidth, worldheight);
goldNoiseTexture = new Texture2D(worldwidth, worldheight);
//再生成材质
GenerateNoiseTexture(caveFreq, caveSize, mapNoiseTexture);
GenerateNoiseTexture(copperFreq, 1f - copperSize, copperNoiseTexture);
GenerateNoiseTexture(goldFreq, 1f- goldSize, goldNoiseTexture);
GenerateTexture();
}
最终生成地图效果如下。生成的矿物数量(copperSize / goldSize)可以自己调整。