从零开始入门创作游戏——2D沙盘类随机地图的生成

如果想做类似于泰拉瑞亚的沙盘游戏,首先需要生成随机的地图。


  • 定义世界大小

首先需要确定地图大小和砖块元素

1、地图所需基本参数

因为只有第一次开始时会生成地图,所以可以删除update函数

public class Sandbox_seed : MonoBehaviour
{
    public int worldwidth = 100;    // 地图宽度
    public int worldheight = 50;    // 地图高度

    void Start()
    {

    }
}

2、砖块元素

为了简单,直接创建方块元素来代替游戏砖块。在资源栏右键创建即可。默认白色方便进行观察。

生成方块后,为了保证其边缘对齐unity页面的网格,所以修改其原点到左下角,使得左下角第一个方块的左下角对准unity原点。

进入sprite编辑页,修改右下角的数值,x=0,y=0,修改完后保存

  • 使用函数生成地图噪声值

我所使用的是 Unity 中的 Mathf.PerlinNoise 函数来生成一个二维噪声值。

而该函数中需要增加部分参数。

其中为了保证地图随机,添加参数【seed】来创建随机数,即地图种子。使用相同的地图种子可以生成相同的世界地图。

public class Sandbox_seed : MonoBehaviour
{
    public int worldwidth = 200;          // 地图宽度
    public int worldheight = 50;          // 地图高度

    public int groundHeight = 25;         //地表高度
    public float surfaceFreq = 0.05f;     //地表的平滑度,越大越不平滑
    public float surfaceMultiper = 20f;   //地表的高低差,越大越陡峭

    public int seed = 0;                 //地图种子,可以使用随机数

    void Start()
    {

    }
}

使用PerlinNoise的代码如下:

public class Sandbox_seed : MonoBehaviour
{

    /*各种参数*/

    void Start()
    {
        GenerateTexture();
    }

    public void GenerateTexture()
    {
        for (int x = 0; x < worldwidth; x++)     //x为地图宽度,因为第一格为0,所以最后一格为199
        {
            float surfaceNoise = Mathf.PerlinNoise((x + seed) * surfaceFreq, seed * surfaceFreq) * surfaceMultiper + groundHeight;
        }
    }

关于Mathf.PerlinNoise函数,是以给定的2个参数为坐标,返回一个取值范围为0到1的浮点型,而且返回值比较连续且光滑。

而在这里的x的for循环中,函数内第一个参数【(x + seed) * surfaceFreq】递增而第二个函数【seed * surfaceFreq】不变,实际上生成的噪声图将呈现出水平连续的波纹状。

但由于结果是0到1的浮点型,因此需要增加其振幅,即参数【surfaceMultiper】,代表地表的高低差,数值越大越陡峭。

而为了让地图向上偏移,需要添加参数【groundHeight】,代表地表高度。

而Mathf.PerlinNoise函数内的额外参数【surfaceFreq】,是为了增加输出数值的噪声值,该值代表地表的平滑度,越大越不平滑

这个函数的实际表现可以结合下面生成砖块后的图像进行理解。

  • 使用噪音值生成地图砖块

以x为横坐标,以上面函数最终输出的值surfaceNoise为纵坐标生成地图。y的取值范围小于surfaceNoise即保证该线的下方会被方块填充。

注意添加参数tile,然后将砖块元素拖动到对应的位置让脚本引用。

public class Sandbox_seed : MonoBehaviour
{

    /*各种参数*/
    
    public Sprite tile; //砖块元素

    void Start()
    {
        seed = Random.Range(10000, 20000);

        GenerateTexture();
    }

    public void GenerateTexture()
    {
        for (int x = 0; x < worldwidth; x++)     //x为地图宽度,因为第一格为0,所以最后一格为199
        {

            float surfaceNoise = Mathf.PerlinNoise((x + seed) * surfaceFreq, seed * surfaceFreq) * surfaceMultiper + groundHeight;
            for (int y = 0; y <= surfaceNoise; y++)
            {
                GameObject newTile = new GameObject(name = "tile");
                newTile.transform.parent = this.transform;
                newTile.AddComponent<SpriteRenderer>();
                newTile.GetComponent<SpriteRenderer>().sprite = tile;
                newTile.transform.position = new Vector2(x, y);
            }
        }
    }

要使脚本运行起来,需要在unity场景中创建一个空对象,然后将脚本拖到该对象中。并且在脚本中的public sprite的位置(红框,平铺为翻译错误)添加创建的方形元素。

启动游戏后即生成随机地貌

seed = 0

 seed = 12304

seed = 16068

  • 创建洞窟的噪音和砖块

洞窟与地表一样使用 Mathf.PerlinNoise 函数来生成,但不一样的是,需要使用 x 轴和 y 轴来生成地图。

首先需要生成一张世界地图所需要的噪音图,然后使用该图来进行参照,来填充(掏空)地图砖块。代码如下

public class Sandbox_seed : MonoBehaviour
{

    /*各种参数*/

    public Texture2D noiseTexture;    //创建一个材质,然后通过代码生成噪音二位图

    public void GenerateNoiseTexture()
    {
        noiseTexture = new Texture2D(worldwidth, worldheight);

        for (int x = 0; x < noiseTexture.width; x++)
        {
            for (int y = 0; y < noiseTexture.height; y++)
            {
                float v = Mathf.PerlinNoise((x + seed)*0.05f , (y + seed ) * 0.05f);
                noiseTexture.SetPixel(x, y, new Color(v, v, v)); 
                //1、SetPixel函数根据像素位置,生成对应x,y坐标下的像素颜色为Color(v, v, v)的点
                //2、Color函数的参数如果是浮点型,取值范围为(0到1)时,0为黑,1为白,可以在画板查看对应色阶的RGB
            }
        }
        noiseTexture.Apply();  //将上面点生成对应的二位噪音图保存
    }
}

启动程序后可以点开材质选项,查看生成的图

然后根据生成的噪音图生成洞穴

public class Sandbox_seed : MonoBehaviour
{

    /*各种参数*/

    public float caveFreq = 0.25f;        //山洞参数,数值越大洞越多和大

    void Start()
    {
        seed = Random.Range(10000, 20000);

        GenerateNoiseTexture();
        GenerateTexture();
    }

    public void GenerateNoiseTexture(){ ... }

    public void GenerateTexture()
    {
        for (int x = 0; x < worldwidth; x++)      //x为地图宽度,因为第一格为0,所以最后一格为199
        {

            float surfaceNoise = Mathf.PerlinNoise((x + seed) * surfaceFreq, seed * surfaceFreq) * surfaceMultiper + groundHeight;

            for (int y = 0; y <= surfaceNoise; y++)
            {
                if (noiseTexture.GetPixel(x, y).r > caveFreq)    
                //GetPixel(x, y).r 是指该像素位置的RGB值中的R值,使用b和g都可以
                //如果该像素所在位置的颜色比洞窟参数的颜色深(白)的话,就生成该砖块,否则留空为洞穴
                {
                    GameObject newTile = new GameObject(name = "tile");
                    newTile.transform.parent = this.transform;
                    newTile.AddComponent<SpriteRenderer>();
                    newTile.GetComponent<SpriteRenderer>().sprite = tile;
                    newTile.transform.position = new Vector2(x, y);
                }

            }
        }
    }
}

最终生成的像素图如下

而对应的世界地图如下​​​,可以看出,像素噪音图中黑色的地方为空洞,而白色的地方为砖块

猜你喜欢

转载自blog.csdn.net/dwe147/article/details/130310084