游戏中随机地图的实现

来源:互联网 发布:php系统源码 编辑:程序博客网 时间:2024/05/17 06:21

游戏中随机地图的实现

很多游戏都用到了随机地图,比如矮人要塞,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();        }

这里写图片描述

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

参考

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

1 0
原创粉丝点击