游戏中随机地图的实现

游戏中随机地图的实现

很多游戏都用到了随机地图,比如矮人要塞,CDDA,MineCraft,RimWorld。
随机地图带给游戏更多的趣味性,每一次新建游戏都有不同的体验。
一般游戏中生成随机地形都是使用柏林噪声,接下来我们就来看看怎么实现。

白噪声

首先我们先来使用一个白噪声函数试试生成一张完全随机的图片。
public float[][] GenerateWhiteNoise(int width,int height,int seed)
        {
            Random r = new Random(seed);
            float[][]noise=new float[width][];
            for (int i = 0; i < width; i++)
            {
                noise[i] = new float[height];
            }
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    noise[i][j] = (float)r.NextDouble() % 1;
                }
            }
            return noise;
        }
private void mapPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap bm = new Bitmap(500, 500);
            float[][] noises = GenerateWhiteNoise(500, 500,6527);
            for (int i = 0; i < 500; i++)
            {
                for (int j = 0; j < 500; j++)
                {
                    int co = (int)(noises[i][j] * 256);
                    bm.SetPixel(i, j, Color.FromArgb(co, co, co));
                }
            }
            Pen p = new Pen(Color.Black, 1);
            g.DrawImage(bm,new Point(0,0));
        }

这里写图片描述

完全随机的话,貌似并不能到到我们要的效果,接下来,我们就试试加一些平滑过渡。

柏林噪声

柏林噪声应用在很多游戏的地形生成上,也应用在图片的处理上。
float Interpolate(float x0, float x1, float alpha)
        {
            return x0 * (1 - alpha) + alpha * x1;
        }

public float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
        {
            int width = baseNoise.Length;
            int height = baseNoise[0].Length;

            float[][] smoothNoise = new float[width][];
            for (int i = 0; i < width; i++)
            {
                smoothNoise[i] = new float[height];
            }
            int samplePeriod = 1 << octave;
            float sampleFrequency = 1.0f / samplePeriod;
            for (int i = 0; i < width; i++)
            {
                //calculate the horizontal sampling indices
                int sample_i0 = (i / samplePeriod) * samplePeriod;
                int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
                float horizontal_blend = (i - sample_i0) * sampleFrequency;

                for (int j = 0; j < height; j++)
                {
                    //calculate the vertical sampling indices
                    int sample_j0 = (j / samplePeriod) * samplePeriod;
                    int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
                    float vertical_blend = (j - sample_j0) * sampleFrequency;

                    //blend the top two corners
                    float top = Interpolate(baseNoise[sample_i0][sample_j0],
                       baseNoise[sample_i1][sample_j0], horizontal_blend);

                    //blend the bottom two corners
                    float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
                       baseNoise[sample_i1][sample_j1], horizontal_blend);

                    //final blend
                    smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
                }
            }

            return smoothNoise;
        }

public float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
        {
            int width = baseNoise.Length;
            int height = baseNoise[0].Length;

            float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
            float persistance = 0.5f;
            //generate smooth noise
            for (int i = 0; i <octaveCount; i++)
            {
                smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
            }

            float[][] perlinNoise = new float[width][];
            for (int i = 0; i < width; i++)
            {
                perlinNoise[i] = new float[height];
            }
            float amplitude = 1.0f;
            float totalAmplitude = 0.0f;

            //blend noise together
            for (int octave = octaveCount - 1; octave >= 0; octave--)
            {
               amplitude *= persistance;
               totalAmplitude += amplitude;

               for (int i = 0; i <width; i++)
               {
                  for (int j = 0; j < height; j++)
                  {
                     perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
                  }
               }
            }
            //normalisation
           for (int i = 0; i <width; i++)
           {
              for (int j = 0; j <height; j++)
              {
                 perlinNoise[i][j] /= totalAmplitude;
              }
           }

           return perlinNoise;

        }
int o=10;
private void mapPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            Bitmap bm = new Bitmap(500, 500);
            float[][] noises = GenerateWhiteNoise(500, 500,6527);
            float[][] smooth = GenerateSmoothNoise(noises, 5);
            float[][] perlins = GeneratePerlinNoise(noises, o);

            for (int i = 0; i < 500; i++)
            {
                for (int j = 0; j < 500; j++)
                {
                    int co = (int)(perlins[i][j] * 256);
                    bm.SetPixel(i, j, Color.FromArgb(co, co, co));
                }
            }
            Pen p = new Pen(Color.Black, 1);
            g.DrawImage(bm,new Point(0,0));
        }

这里写图片描述
这里写图片描述
这里写图片描述

可以看到 octaveCount为10的时候就有很好的烟雾效果,
不过我应用在生成随机地图的时候,取值为6可以获得最好的效果。

柏林噪声的应用

柏林噪声可以应用在图片中,获得类似烟雾,爆炸,
气体的效果,可以将两张图片用一定的比例合并在一张。
Color GetColor(Color gradientStart, Color gradientEnd, float t)
        {
            float u = 1 - t;

            Color color = Color.FromArgb(
               255,
               (int)(gradientStart.R * u + gradientEnd.R * t),
               (int)(gradientStart.G * u + gradientEnd.G * t),
               (int)(gradientStart.B * u + gradientEnd.B * t));

            return color;
        }

Color[][] MapGradient(Color gradientStart, Color gradientEnd, float[][] perlinNoise)
        {
           int width = perlinNoise.Length;
           int height = perlinNoise[0].Length;

           Color[][] image = new Color[width][];
           for (int i = 0; i < width; i++)
           {
               image[i] = new Color[height];
           }

           for (int i = 0; i <width; i++)
           {
              for (int j = 0; j <height; j++)
              {
                 image[i][j] = GetColor(gradientStart, gradientEnd, perlinNoise[i][j]);
              }
           }

           return image;
        }
int o = 5;
        private void mapPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            Bitmap bm = new Bitmap(500, 500);
            float[][] noises = GenerateWhiteNoise(500, 500,6527);
            float[][] smooth = GenerateSmoothNoise(noises, 5);
            float[][] perlins = GeneratePerlinNoise(noises, o);

            Color[][] map = MapGradient( Color.Red,ColorTranslator.FromHtml("#7FFFD4"), perlins);

            for (int i = 0; i < 500; i++)
            {
                for (int j = 0; j < 500; j++)
                {
                    int co = (int)(perlins[i][j] * 256);
                    //bm.SetPixel(i, j, Color.FromArgb(co, co, co));
                    bm.SetPixel(i, j, map[i][j]);
                }
            }
            Pen p = new Pen(Color.Black, 1);
            g.DrawImage(bm,new Point(0,0));
        }

这里写图片描述

随机地图的生成

查阅了一些资料,不同的游戏有不同的生成方式。
有的是使用维诺图加上柏林函数来生成世界,有的是生成高度图,再生成降水,再经过腐蚀而生成一个接近真实世界的地貌。
这里的实例是一个最简单的实现方法,使用方块格,根据柏林噪声获取该格的高度(0.1~1),再根据其高度来设定其地形。

public Color GetColorByFloat(float noise)
        {
            noise = noise * 255;
            if (noise > 240) return Color.White;
            if (noise > 210) return ColorTranslator.FromHtml("#AAAAAA");
            if (noise > 180) return ColorTranslator.FromHtml("#C5C1AA");
            if (noise > 165) return ColorTranslator.FromHtml("#3CB371");
            if (noise > 130) return ColorTranslator.FromHtml("#228B22");
            if (noise > 70) return ColorTranslator.FromHtml("#71C671");
            return Color.SkyBlue;
        }
private void button2_Click(object sender, EventArgs e)
        {
            Random r = new Random();
            float[][] whiteNoise = GenerateWhiteNoise(100, 100, r.Next());
            float[][] perlinNoise = GeneratePerlinNoise(whiteNoise, 5);
            for (int i = 0; i < 100; i++)
            {
                for (int j = 0; j < 100; j++)
                {
                    SolidBrush sb = new SolidBrush(GetColorByFloat(perlinNoise[i][j]));
                    pbG.FillRectangle(sb, i * width, j * height, width , height );
                }
            }
            pictureBox1.Invalidate();
        }

这里写图片描述

生成的效果勉强可以,但是如果添加更多的地形,加入更多的处理,相信可以做出更加好看,更加接近真实的地图。如果你有好的建议,请留言告诉我。

参考

噪声函数
柏林噪声的应用
开发者分享地形生成制作经验
地图生成器

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值