<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Game Dev Without a Cause &#187; Procedural Content</title>
	<atom:link href="https://gamedevwithoutacause.com/?cat=12&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://gamedevwithoutacause.com</link>
	<description>Rob&#039;s Thoughts on Games, Game Dev, and Design - Temporary home of Skyboy Games</description>
	<lastBuildDate>Wed, 23 Jul 2014 09:18:52 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>An Island in the Clouds</title>
		<link>https://gamedevwithoutacause.com/?p=816</link>
		<comments>https://gamedevwithoutacause.com/?p=816#comments</comments>
		<pubDate>Tue, 20 Mar 2012 12:06:24 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=816</guid>
		<description><![CDATA[Despite what the title of this post may suggest, I&#8217;m not going to be talking about some recent trip to Borneo or Papua New Guinea (although how cool would that be!) No, instead I&#8217;m talking about my continuing dalliance with &#8230; <a href="https://gamedevwithoutacause.com/?p=816">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/image_display.jpg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/image_display.jpg" alt="" title="image_display" width="600" height="450" class="aligncenter size-full wp-image-819" /></a><br />
Despite what the title of this post may suggest, I&#8217;m not going to be talking about some recent trip to <a href="http://www.pbs.org/edens/borneo/" target="_blank">Borneo</a> or <a href="http://www.amazon.com/Islands-Clouds-Travels-Highlands-Guinea/dp/0864423691" target="_blank">Papua New Guinea</a> (although how cool would that be!)  No, instead I&#8217;m talking about my continuing dalliance with the plasma fractal I mentioned in my last <a href="http://gamedevwithoutacause.com/?p=684" title="Virtual Spelunking: Procedural Textures (or How to Make a Dirty Floor)" target="_blank">procedural content post</a>.<br />
While I used plasma fractals to create variations of the floor tiles in my procedural cave, there are many other applications for plasma fractals.  One of the classic applications of plasma fractals is generating heightmaps.  By imagining the value at each cell of a plasma fractal as the height of ground at that point, it&#8217;s relatively easy to visualize the mountainous terrain that can be generated with a plasma fractal.  Traditionally, people use heightmaps values to perturb a 3D mesh grid to create 3D terrain.  Since I already had the necessary pieces at my fingertips, I decided to see how my plasma fractals would look when rendered with 2D tiles.<span id="more-816"></span><br />
To get a sense of how my heightmaps would look, my first step was to pass a plasma fractal into a class to render it as text.  By mapping ranges of values to numbers as well as coloring the text according to those values, I was able to generate a visualization that would give me a sense of how my tiled heightmap would look.<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island00.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island00.png" alt="" title="island00" width="524" height="540" class="aligncenter size-full wp-image-828" /></a><br />
I was now ready to render the heightmap with <a href="http://www.lostgarden.com/2007/05/dancs-miraculously-flexible-game.html" target="_blank">PlanetCute</a> tiles.  My basic approach was to vary the height of the tiles based on the values in the heightmap.  I also darkened tiles based on their depth to provide a basic shading effect to the terrain.<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island01.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island01.png" alt="" title="island01" width="1019" height="729" class="aligncenter size-full wp-image-829" /></a><br />
It works, but using the same tile for the whole heightmap is dull.  To add more variety to the heightmap, I modified the renderer to use different tiles based on where each heightmap value fell within a particular range.  Declaring that values <= 0.3 map to be water tiles and >= 0.7 map to be dirt tiles gave me the following result.<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island02.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island02.png" alt="" title="island02" width="1020" height="731" class="aligncenter size-full wp-image-830" /></a><br />
Plasma fractals aren&#8217;t just useful for heightmaps, of course.  In order to add a little more visual pizazz, I turned my eye to the sky and used a plasma fractal to generate a cloud-filled sky.  By mapping the fractal&#8217;s values to the alpha channel of a white texture, I created a cloud texture that could turn this empty sky:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island03.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island03.png" alt="" title="island03" width="1013" height="720" class="aligncenter size-full wp-image-831" /></a><br />
Into a cloud-filled one:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island04.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island04.png" alt="" title="island04" width="1015" height="723" class="aligncenter size-full wp-image-832" /></a><br />
Also, by adjusting the alpha of the cloud texture, I could create a darker, stormy sky:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island05.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/03/island05.png" alt="" title="island05" width="1016" height="731" class="aligncenter size-full wp-image-833" /></a><br />
Of course, I kind of prefer the blue sky with white clouds.  We have enough cloudy weather in games nowadays, don&#8217;t we?</p>
<p>For the interested, here&#8217;s the code for generating the heightmap and cloud textures:</p>
<pre class="brush: c++">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ProceduralWorldLib
{
    public class HeightfieldGenerator
    {
        public static float[] GenerateHeightfield(int randomSeed, int sideLength)       //Side length must be POW2
        {
            Random rng = new Random(randomSeed);
            float[] heightField;
            AlgorithmHelper.GeneratePlasma(out heightField, sideLength, rng, 0.5f, 0.75f);

            return heightField;
        }

        public static Texture2D GeneratePlasmaCloud(GraphicsDevice graphics, int randomSeed, int sideLength)
        {
            Random rng = new Random(randomSeed);
            float[] heightField;
            AlgorithmHelper.GeneratePlasma(out heightField, sideLength, rng, 0.5f, 0.75f);

            Texture2D sampleTexture = new Texture2D(graphics, sideLength, sideLength);
            Color[] colorData = new Color[sideLength * sideLength];
            for (int x = 0; x &lt; sideLength; ++x)
            {
                for (int y = 0; y &lt; sideLength; ++y)
                {
                    float genVal = Math.Min(Math.Max(heightField[sideLength * y + x], 0.0f), 1.0f);
                    colorData[y * sideLength + x] = new Color(genVal, genVal, genVal, genVal);
                }
            }
            sampleTexture.SetData&lt;Color&gt;(colorData);

            return sampleTexture;
        }
    }
}
</pre>
<pre class="brush: c++">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ProceduralWorldLib
{
    public static class AlgorithmHelper
    {
        public static void GeneratePlasma(out float[] plasmaArray, int plasmaSideLength, Random rng, float plasmaSeedValue, float plasmaPerturbation)
        {
            plasmaArray = new float[(plasmaSideLength + 1) * (plasmaSideLength + 1)];

            plasmaArray[plasmaIndex(0, 0, plasmaSideLength)] = plasmaSeedValue;     //0,0
            plasmaArray[plasmaIndex(plasmaSideLength, 0, plasmaSideLength)] = plasmaSeedValue;     //MAX,0
            plasmaArray[plasmaIndex(0, plasmaSideLength, plasmaSideLength)] = plasmaSeedValue;     //0,MAX
            plasmaArray[plasmaIndex(plasmaSideLength, plasmaSideLength, plasmaSideLength)] = plasmaSeedValue;     //MAX,MAX

            float h = plasmaPerturbation;

            int currentSideLength = plasmaSideLength;
            while (currentSideLength &gt;= 2)
            {
                int halfSideLength = currentSideLength / 2;
                for (int x = 0; x &lt; plasmaSideLength; x += currentSideLength)
                {
                    for (int y = 0; y &lt; plasmaSideLength; y += currentSideLength)
                    {
                        float average = plasmaArray[plasmaIndex(x, y, plasmaSideLength)]; //top left
                        average += plasmaArray[plasmaIndex(x + currentSideLength, y, plasmaSideLength)]; //top right
                        average += plasmaArray[plasmaIndex(x, y + currentSideLength, plasmaSideLength)]; //bottom left
                        average += plasmaArray[plasmaIndex(x + currentSideLength, y + currentSideLength, plasmaSideLength)]; //bottom right
                        average *= 0.25f;

                        plasmaArray[plasmaIndex(x + halfSideLength, y + halfSideLength, plasmaSideLength)] = average + (float)(rng.NextDouble() * 2.0) * h - h;
                    }
                }


                for (int x = 0; x &lt; plasmaSideLength; x += halfSideLength)
                {
                    for (int y = (x + halfSideLength) % currentSideLength; y &lt; plasmaSideLength; y += currentSideLength)
                    {
                        float average = plasmaArray[plasmaIndex((x - halfSideLength + (plasmaSideLength)) % (plasmaSideLength), y, plasmaSideLength)]; //left of center
                        average += plasmaArray[plasmaIndex((x + halfSideLength) % (plasmaSideLength), y, plasmaSideLength)]; //right of center
                        average += plasmaArray[plasmaIndex(x, (y + halfSideLength) % (plasmaSideLength), plasmaSideLength)]; //below center
                        average += plasmaArray[plasmaIndex(x, (y - halfSideLength + (plasmaSideLength)) % (plasmaSideLength), plasmaSideLength)]; //above center
                        average *= 0.25f;

                        plasmaArray[plasmaIndex(x, y, plasmaSideLength)] = average + (float)(rng.NextDouble() * 2.0) * h - h;

                        if (x == 0) plasmaArray[plasmaIndex(plasmaSideLength, y, plasmaSideLength)] = average;
                        if (y == 0) plasmaArray[plasmaIndex(x, plasmaSideLength, plasmaSideLength)] = average;
                    }
                }

                currentSideLength *= 0.5f;
                h *= 0.5f;
            }
        }

        static int plasmaIndex(int x, int y, int plasmaSideLength)
        {
            return (plasmaSideLength + 1) * y + x;
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=816</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Procedural Textures (or How to Make a Dirty Floor)</title>
		<link>https://gamedevwithoutacause.com/?p=684</link>
		<comments>https://gamedevwithoutacause.com/?p=684#comments</comments>
		<pubDate>Tue, 28 Feb 2012 14:51:30 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=684</guid>
		<description><![CDATA[One of the main advantages of procedural content is being able to generate a near-infinite number of variations on your content without having to waste valuable artist time. This is especially true when you want to make variants on what &#8230; <a href="https://gamedevwithoutacause.com/?p=684">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/IMG_4692.jpeg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/IMG_4692.jpeg" alt="" title="IMG_4692" width="800" height="600" class="aligncenter size-full wp-image-686" /></a><br />
One of the main advantages of procedural content is being able to generate a near-infinite number of variations on your content without having to waste valuable artist time.  This is especially true when you want to make variants on what is generally &#8220;boring&#8221; background content, like rocks, trees, walls, and floor tiles.  Instead of having an artist make 10 variations of rocks for your level, you can simple have them make one (or none!) and create 100 variations by applying perturbation functions to the content in code.<span id="more-684"></span></p>
<p>In this week&#8217;s post, I apply this concept by using a fractal generation function to add varying patterns of darkness to my floor tiles.  By doing this, I can create an arbitrary number of texture variations for my floor tiles each representing different patterns of random wear-and-tear.  With a decent number of variations, it becomes very unlikely for two neighboring floor tiles to look exactly the same which greatly reduces the unrelenting visual regularity you get from seeing the same tile rendered over and over again.</p>
<p>In order to create my floor texture variations, I take my PlanetCute floor texture:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/Plain-Block.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/Plain-Block.png" alt="" title="Plain Block" width="101" height="171" class="aligncenter size-full wp-image-729" /></a><br />
And multiply it in a shader with a plasma fractal like this one:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/FilePlasmafractal.gif"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/FilePlasmafractal.gif" alt="" title="FilePlasmafractal" width="200" height="150" class="aligncenter size-full wp-image-692" /></a><br />
By programmatically generating random plasma fractals, combining them with the floor tile, and rendering them to a texture, I can create sprite textures that the rest of my engine can use just like any other pre-authored texture. </p>
<p>In XNA, creating a texture at runtime is a simple matter of creating a texture render target and temporarily directing your render commands there instead of to the screen:</p>
<pre class="brush: c++">
for (int i = 0; i &lt; variationCount; ++i)
{
    RenderTarget2D renderTarget = new RenderTarget2D(_graphics, baseTexture.Width, baseTexture.Height);
    
    //_graphics is a reference to Game.GraphicDevice        
    _graphics.Textures[1] = _getMyFractal();

    _graphics.SetRenderTarget(renderTarget);
    _graphics.Clear(Color.Transparent);

    //_renderEffect is an Effect instance that I loaded with a custom shader (.fx) file
    spriteBatch.Begin(0, null, null, null, null, _renderEffect);
    spriteBatch.Draw(baseTexture, Vector2.Zero, Color.White);
    spriteBatch.End();

    textureArray[i] = renderTarget;
}
//Setting RenderTarget to null retargets rendering back to the screen
_graphics.SetRenderTarget(null);
</pre>
<p>The shader I use to combine the two textures is about as simple as they come, a simple multiplication of the RGB from the second texture onto the first:</p>
<pre class="brush: c++;">
sampler TextureSampler : register(s0);
sampler DetailTextureSampler : register(s1);

float4 main(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
    // Look up the texture color.
    float4 tex = tex2D(TextureSampler, texCoord);
    float4 detTex = tex2D(DetailTextureSampler, texCoord);
    
    tex.rgb *= detTex.rgb;
	
    return tex;
}

technique Desaturate
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 main();
    }
}
</pre>
<p>With my texture rendering code in place, it&#8217;s just a matter of finding an algorithm for creating my plasma fractal textures.  As usual, Wikipedia provides a convenient solution for this with the<br />
<a href="http://en.wikipedia.org/wiki/Diamond-square_algorithm" title="Diamond-square algorithm at wikipedia">Diamond-Square Algorithm</a>.  This algorithm generates a height field of values dividing a texture into smaller and smaller squares, generating values for points by averaging the corners of their containing squares (and diamonds).  <a href="http://gameprogrammer.com/fractal.html">This page</a> at <a href="http://gameprogrammer.com/">GameProgrammer.com</a> does a good job of describing the algorithm steps:</p>
<blockquote><p>
The diamond step: Taking a square of four points, generate a random value at the square midpoint, where the two diagonals meet. The midpoint value is calculated by averaging the four corner values, plus a random amount. This gives you diamonds when you have multiple squares arranged in a grid.</p>
<p>The square step: Taking each diamond of four points, generate a random value at the center of the diamond. Calculate the midpoint value by averaging the corner values, plus a random amount generated in the same range as used for the diamond step. This gives you squares again.
</p></blockquote>
<p>And the usage of said steps:</p>
<blockquote><p>
While the length of the side of the squares is greater than zero<br />
{<br />
&nbsp;&nbsp;&nbsp;Pass through the array and perform the diamond step for each square present.<br />
&nbsp;&nbsp;&nbsp;Pass through the array and perform the square step for each diamond present.<br />
&nbsp;&nbsp;&nbsp;Reduce the random number range.<br />
}
</p></blockquote>
<p>If you&#8217;re like me and sometimes find code easier to understand than pseudo-code, this <a href="http://stackoverflow.com/questions/2755750/diamond-square-algorithm">answer</a> at <a href="http://stackoverflow.com/">Stack Overflow</a> provides Java code implementing the algorithm.</p>
<p>Once I have an implementation of the diamond-square algorithm working in C#, I&#8217;m able to create a buffer of Color data that I can apply to a Texture2D using SetData().  Then I can use the resulting texture in the rendering code I presented above to create my floor texture variations.</p>
<p>With my floor texture variations ready, I randomly assign them to floor tiles and turn this:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/floor_variation00.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/floor_variation00.png" alt="" title="floor_variation00" width="808" height="487" class="aligncenter size-full wp-image-694" /></a></p>
<p>Into this:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/floor_variation01.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/floor_variation01.png" alt="" title="floor_variation01" width="805" height="482" class="aligncenter size-full wp-image-693" /></a></p>
<p>To many it&#8217;s a subtle difference perhaps, but the increased visual irregularity in the floor goes a long way to reduce the sense of repetition in the dungeon. It&#8217;s a nice touch if we expect our players to spend a significant amount of time in our dungeons (which we do!)</p>
<p>For reference, I&#8217;ve included my texture and fractal rendering code below:</p>
<pre class="brush: c++;">
//************* TextureCreator.cs *********************
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ProceduralWorldLib
{
    public class TextureCreator
    {
        protected GraphicsDevice _graphics;
        List&lt;Texture2D&gt; _textureSamples;
        protected Effect _renderEffect;

        public TextureCreator(GraphicsDevice graphicDevice, Effect renderEffect)
        {
            _graphics = graphicDevice;
            _textureSamples = new List&lt;Texture2D&gt;();
            _renderEffect = renderEffect;
            Debug.Assert(_renderEffect != null);
        }

        public void AddSampleTexture(Texture2D texture)
        {
            Debug.Assert(texture != null);
            _textureSamples.Add(texture);
        }

        public virtual Texture2D[] GenerateTextureArray()
        {
            Texture2D baseTexture = _textureSamples[0];
            int variationCount = _textureSamples.Count - 1;
            
            SpriteBatch spriteBatch = new SpriteBatch(_graphics);

            Texture2D[] textureArray = new Texture2D[variationCount];

            for (int i = 0; i &lt; variationCount; ++i)
            {
                RenderTarget2D renderTarget = new RenderTarget2D(_graphics, baseTexture.Width, baseTexture.Height);
            
                _graphics.Textures[1] = _textureSamples[i + 1];

                _graphics.SetRenderTarget(renderTarget);
                _graphics.Clear(Color.Transparent);

                spriteBatch.Begin(0, null, null, null, null, _renderEffect);
                spriteBatch.Draw(baseTexture, Vector2.Zero, Color.White);
                spriteBatch.End();

                textureArray[i] = renderTarget;
            }
            _graphics.SetRenderTarget(null);

            return textureArray;
        }
    }
}

//************* PlasmaVariationCreator.cs *********************
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ProceduralWorldLib
{
    public class PlasmaVariationCreator : TextureCreator
    {
        Texture2D _baseTexture;
        int _variationCount;
        int _rngSeed;

        float[] _plasmaArray;
        const int PLASMA_DIMENSION_SIZE = 64;

        public PlasmaVariationCreator(Game game, int variationCount, int rngSeed) :
            base(game.GraphicsDevice, game.Content.Load&lt;Effect&gt;(&quot;Shader/TextureGen&quot;))
        {
            _variationCount = variationCount;
            _rngSeed = rngSeed;
        }

        public void SetBaseTexture(Texture2D baseTexture)
        {
            _baseTexture = baseTexture;
        }

        public Texture2D[] GenerateTextureArrayWithBase(Texture2D baseTexture)
        {
            SetBaseTexture(baseTexture);
            return GenerateTextureArray();
        }

        public override Texture2D[] GenerateTextureArray()
        {
            Debug.Assert(_baseTexture != null);

            Texture2D[] textureArray = new Texture2D[_variationCount];
            
            Random rng = new Random(_rngSeed);
            
            SpriteBatch spriteBatch = new SpriteBatch(_graphics);
            for (int i = 0; i &lt; textureArray.Length; ++i)
            {
                _GeneratePlasmaArray();

                Texture2D sampleTexture = new Texture2D(_graphics, PLASMA_DIMENSION_SIZE, PLASMA_DIMENSION_SIZE);
                Color[] colorData = new Color[PLASMA_DIMENSION_SIZE * PLASMA_DIMENSION_SIZE];
                for (int x = 0; x &lt; PLASMA_DIMENSION_SIZE; ++x)
                {
                    for (int y = 0; y &lt; PLASMA_DIMENSION_SIZE; ++y)
                    {
                        float genVal = Math.Min(Math.Max(_GetPlasmaValue(rng, x, y), 0.0f), 1.0f);
                        colorData[y * PLASMA_DIMENSION_SIZE + x] = new Color(genVal, genVal, genVal, 1.0f);
                    }
                }
                sampleTexture.SetData&lt;Color&gt;(colorData);

                RenderTarget2D renderTarget = new RenderTarget2D(_graphics, _baseTexture.Width, _baseTexture.Height);

                _graphics.Textures[1] = sampleTexture;

                _graphics.SetRenderTarget(renderTarget);
                _graphics.Clear(Color.Transparent);

                spriteBatch.Begin(0, null, null, null, null, _renderEffect);
                spriteBatch.Draw(_baseTexture, Vector2.Zero, Color.White);
                spriteBatch.End();

                textureArray[i] = renderTarget;
            }
            _graphics.SetRenderTarget(null);

            return textureArray;
        }

        float _GetPlasmaValue(Random rng, int x, int y)
        {
            return 1.0f - _plasmaArray[plasmaIndex(x, y)] * 0.5f;
        }

        void _GeneratePlasmaArray()
        {
            const float INITIAL_SEED = 0.5f;

            _plasmaArray = new float[(PLASMA_DIMENSION_SIZE + 1) * (PLASMA_DIMENSION_SIZE + 1)];

            _plasmaArray[plasmaIndex(0,0)] = INITIAL_SEED;     //0,0
            _plasmaArray[plasmaIndex(PLASMA_DIMENSION_SIZE, 0)] = INITIAL_SEED;     //MAX,0
            _plasmaArray[plasmaIndex(0, PLASMA_DIMENSION_SIZE)] = INITIAL_SEED;     //0,MAX
            _plasmaArray[plasmaIndex(PLASMA_DIMENSION_SIZE, PLASMA_DIMENSION_SIZE)] = INITIAL_SEED;     //MAX,MAX
            
            Random rng = new Random();
            float h = 0.25f;

            int currentSideLength = PLASMA_DIMENSION_SIZE;
            while (currentSideLength &gt;= 2)
            {
                int halfSideLength = currentSideLength / 2;
                for (int x = 0; x &lt; PLASMA_DIMENSION_SIZE; x+= currentSideLength)
                {
                    for (int y = 0; y &lt; PLASMA_DIMENSION_SIZE; y += currentSideLength)
                    {
                        float average = _plasmaArray[plasmaIndex(x, y)]; //top left
                        average += _plasmaArray[plasmaIndex(x + currentSideLength, y)]; //top right
                        average += _plasmaArray[plasmaIndex(x, y + currentSideLength)]; //bottom left
                        average += _plasmaArray[plasmaIndex(x + currentSideLength, y + currentSideLength)]; //bottom right
                        average *= 0.25f;

                        _plasmaArray[plasmaIndex(x + halfSideLength, y + halfSideLength)] = average + (float)(rng.NextDouble() * 2.0) * h - h;                        
                    }
                }

                for (int x = 0; x &lt; PLASMA_DIMENSION_SIZE; x += halfSideLength)
                {
                    for (int y = (x + halfSideLength) % currentSideLength; y &lt; PLASMA_DIMENSION_SIZE; y += currentSideLength)
                    {
                        float average = _plasmaArray[plasmaIndex((x - halfSideLength + (PLASMA_DIMENSION_SIZE )) % (PLASMA_DIMENSION_SIZE), y)]; //left of center
                        average += _plasmaArray[plasmaIndex((x + halfSideLength) % (PLASMA_DIMENSION_SIZE), y)]; //right of center
                        average += _plasmaArray[plasmaIndex(x, (y + halfSideLength) % (PLASMA_DIMENSION_SIZE))]; //below center
                        average += _plasmaArray[plasmaIndex(x, (y - halfSideLength + (PLASMA_DIMENSION_SIZE)) % (PLASMA_DIMENSION_SIZE))]; //above center
                        average *= 0.25f;

                        _plasmaArray[plasmaIndex(x, y)] = average + (float)(rng.NextDouble() * 2.0) * h - h;

                        if(x == 0)  _plasmaArray[plasmaIndex(PLASMA_DIMENSION_SIZE, y)] = average;
                        if(y == 0)  _plasmaArray[plasmaIndex(x, PLASMA_DIMENSION_SIZE)] = average;
                    }
                }
                currentSideLength *= 0.5F;
                h *= 0.5f;
            }                                        
        }

        int plasmaIndex(int x, int y)
        {
            return (PLASMA_DIMENSION_SIZE + 1) * y + x;
        }
    }    
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=684</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Exits and Treasure</title>
		<link>https://gamedevwithoutacause.com/?p=630</link>
		<comments>https://gamedevwithoutacause.com/?p=630#comments</comments>
		<pubDate>Tue, 21 Feb 2012 14:31:20 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=630</guid>
		<description><![CDATA[I&#8217;ve spent several posts detailing how to procedurally generate a cave for a player to explore. Now, it&#8217;s about time for me to fill that cave with things for the player to do. The most basic goal for any player &#8230; <a href="https://gamedevwithoutacause.com/?p=630">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/treasure.jpeg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/treasure.jpeg" alt="" title="treasure" width="500" height="267" class="aligncenter size-full wp-image-633" /></a></p>
<p>I&#8217;ve <a href="http://gamedevwithoutacause.com/?p=460" title="Virtual Spelunking: Procedural Cave Generation">spent</a> <a href="http://gamedevwithoutacause.com/?p=495" title="Virtual Spelunking: Tiles and Shadows">several</a> <a href="http://gamedevwithoutacause.com/?p=546" title="Virtual Spelunking: Decorating the Floor">posts</a> <a href="http://gamedevwithoutacause.com/?p=596" title="Virtual Spelunking: Turning on the Lights">detailing</a> how to procedurally generate a cave for a player to explore.  Now, it&#8217;s about time for me to fill that cave with things for the player to do.  The most basic goal for any player dropped into the middle of a cave is to find a way out, so the first thing I&#8217;ll do is drop a starting point and an exit point into the cave.  Just trying to escape a cave by itself is a little too sparse though, so I&#8217;ll also add treasure for the player to find and collect while they&#8217;re looking for the exit.  Now, it&#8217;s starting to sound like a real game.<span id="more-630"></span></p>
<p>Placing the player starting point and exit is a straight-forward process.  The primary case I want to avoid is placing the starting point and exit too close together.  After all, I spent all this time generating a cave, I want the player to have to go through it!</p>
<p>In order to accomplish this, I simply place the player starting point and the dungeon exit on opposite ends of the map.  For the starting point, I scan each column from the left and randomly pick a floor space from the first column that isn&#8217;t all wall onto which to drop the player start point.  I then repeat the process from the right side of the map scanning left and drop the exit on a random floor space in the first available column.  For maps that are taller than they are wide, I do the same thing only scanning from the top for starting point placement and from the bottom for exit placement.  </p>
<p>For treasure placement, I&#8217;m still looking for a solution that I like.  For now though, I use the Togetherness algorithm I used for <a href="http://gamedevwithoutacause.com/?p=546" title="Virtual Spelunking: Decorating the Floor">floor decoration</a>. This time around, I use just two states for togetherness (0 &#8211; no treasure, 1 &#8211; treasure) and a significantly lower chance of any given square being non-empty (5% as opposed to the 40% chance for any-given floor space to be something other than gray tile.)  After a bit of parameter tweaking, I get a satisfactory dispersal of treasure. (In the picture below, &#8216;T&#8217; marks the spot.) </p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/TreasureMap.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/TreasureMap.png" alt="" title="TreasureMap" width="681" height="477" class="aligncenter size-full wp-image-646" /></a></p>
<p>Now, with a bit of simple collision code and those nice treasure chest graphics from the <a href="http://www.lostgarden.com/2007/05/dancs-miraculously-flexible-game.html">PlanetCute tileset</a> I can make my cave a more lucrative place to explore.</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/TreasureMap2.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/02/TreasureMap2.png" alt="" title="TreasureMap2" width="794" height="477" class="aligncenter size-full wp-image-647" /></a></p>
<p>You can find my (decidedly unsophisticated) code for placing starting point and exit points below:</p>
<pre class="brush: c#;">
void _PlaceStartAndEndPoints(Random rng)
{
    int maxColumn = _mapToPopulate[0].Count;
    int maxRow = _mapToPopulate.Count;
    bool bVerticalMap = maxRow &gt; maxColumn;

    //Add player start to space on furthest left column / top row
    if (!bVerticalMap)
    {
        for (int currColumn = 0; currColumn &lt; maxColumn; ++currColumn)
        {
            List&amp;lt;int&amp;gt; floorsInCol = new List&amp;lt;int&amp;gt;();
            for (int currRow = 0; currRow &lt; maxRow; ++currRow)
            {
                if (_mapToPopulate[currRow][currColumn].SpaceType == CaveSpaceType.Floor)
                {
                    floorsInCol.Add(currRow);
                }
            }

            if (floorsInCol.Count &gt; 0)
            {
                _mapInhabitants.Add(new MapInhabitant(InhabitantType.StartingPoint, currColumn, floorsInCol[rng.Next(floorsInCol.Count)]));
                break;
            }
        }
    }
    else
    {
        for (int currRow = 0; currRow &lt; maxRow; ++currRow)
        {
            List&amp;lt;int&amp;gt; floorsInRow = new List&amp;lt;int&amp;gt;();
            for (int currColumn = 0; currColumn &lt; maxColumn; ++currColumn)
            {
                if (_mapToPopulate[currRow][currColumn].SpaceType == CaveSpaceType.Floor)
                {
                    floorsInRow.Add(currColumn);
                }
            }

            if (floorsInRow.Count &gt; 0)
            {
                _mapInhabitants.Add(new MapInhabitant(InhabitantType.StartingPoint, floorsInRow[rng.Next(floorsInRow.Count)], currRow));
                break;
            }
        }
    }

    //Add end point to space on furthest right column / bottom row
    if (!bVerticalMap)
    {
        for (int currColumn = maxColumn - 1; currColumn &gt;= 0; --currColumn)
        {
            List&amp;lt;int&amp;gt; floorsInCol = new List&amp;lt;int&amp;gt;();
            for (int currRow = 0; currRow &lt; maxRow; ++currRow)
            {
                if (_mapToPopulate[currRow][currColumn].SpaceType == CaveSpaceType.Floor)
                {
                    floorsInCol.Add(currRow);
                }
            }

            if (floorsInCol.Count &gt; 0)
            {
                _mapInhabitants.Add(new MapInhabitant(InhabitantType.EndingPoint, currColumn, floorsInCol[rng.Next(floorsInCol.Count)]));
                break;
            }
        }
    }
    else
    {
        for (int currRow = maxRow - 1; currRow &gt;= 0; --currRow)
        {
            List&amp;lt;int&amp;gt; floorsInRow = new List&amp;lt;int&amp;gt;();
            for (int currColumn = 0; currColumn &lt; maxColumn; ++currColumn)
            {
                if (_mapToPopulate[currRow][currColumn].SpaceType == CaveSpaceType.Floor)
                {
                    floorsInRow.Add(currColumn);
                }
            }

            if (floorsInRow.Count &gt; 0)
            {
                _mapInhabitants.Add(new MapInhabitant(InhabitantType.EndingPoint, floorsInRow[rng.Next(floorsInRow.Count)], currRow));
                break;
            }
        }
    }
}
</pre>
<p>And the treasure placement code:<br />
(You can find my Togetherness implementation <a href="http://gamedevwithoutacause.com/?p=546" title="Virtual Spelunking: Decorating the Floor">here</a>)</p>
<pre class="brush: c#;">
void _PlaceTreasure(Random rng)
{
    //Working but not satisfactory, trying Browninan walk?

    TogethernessAutomoton togetherArray = new TogethernessAutomoton(_randomSeed, _mapToPopulate[0].Count, _mapToPopulate.Count, 1, _treasureDensity, 1000, 6, 12);
    int[] resultArray = new int[togetherArray.DesiredArraySize];
    togetherArray.FillArray(ref resultArray);

    int startPointCol = -1, startPointRow = -1;
    int endPointCol = -1, endPointRow = -1;

    foreach (MapInhabitant inhabitant in _mapInhabitants)
    {
        switch (inhabitant.InhabitantType)
        {
            case InhabitantType.StartingPoint:
                startPointCol = inhabitant.Column;
                startPointRow = inhabitant.Row;
                break;
            case InhabitantType.EndingPoint:
                endPointCol = inhabitant.Column;
                endPointRow = inhabitant.Row;
                break;
        }
    }

    for (int rowIdx = 0; rowIdx &lt; _mapToPopulate.Count; ++rowIdx)
    {
        for (int colIdx = 0; colIdx &lt; _mapToPopulate[rowIdx].Count; ++colIdx)
        {
            if (_mapToPopulate[rowIdx][colIdx].SpaceType == CaveSpaceType.Floor)
            {
                if ( (colIdx == startPointCol &amp;&amp; rowIdx == startPointRow) ||
                    (colIdx == endPointCol &amp;&amp; rowIdx == endPointRow) )
                {
                    continue;
                }

                if (resultArray[colIdx * _mapToPopulate.Count + rowIdx] == 1)
                {
                    _mapInhabitants.Add(new MapInhabitant(InhabitantType.Treasure, colIdx, rowIdx));
                }
            }
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=630</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Turning on the Lights</title>
		<link>https://gamedevwithoutacause.com/?p=596</link>
		<comments>https://gamedevwithoutacause.com/?p=596#comments</comments>
		<pubDate>Tue, 07 Feb 2012 14:02:49 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=596</guid>
		<description><![CDATA[Continuing my adventure in interior decoration for imaginary places, it&#8217;s about time for me to start populating my procedural cave with objects. This time around, I&#8217;ll focus on adding light sources to my cave. There are two major reasons for &#8230; <a href="https://gamedevwithoutacause.com/?p=596">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Lewes_Bonfire_discarded_torch.jpg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Lewes_Bonfire_discarded_torch-1024x709.jpg" alt="" title="Lewes_Bonfire,_discarded_torch" width="584" height="404" class="aligncenter size-large wp-image-601" /></a><br />
Continuing my adventure in interior decoration for imaginary places, it&#8217;s about time for me to start populating my procedural cave with objects.  This time around, I&#8217;ll focus on adding light sources to my cave.  There are two major reasons for this: 1) light sources, like torches, won&#8217;t have to be collidable so I can lay them out without worrying about breaking the navigability of my cave; 2) rendering the lights gives me an excuse play around with pixel shaders.</p>
<p>In order to lay out the lights, I need to come up with rules for their placement that will produce results that are neither too regular nor too random.  Since I&#8217;m planning on eventually rendering the lights as wall-mounted torches, I need to make sure the lights are located on cave walls and not lying some place in the middle of the floor.  I also need to make sure that the lights I place are on walls neighboring floor tiles, otherwise I might end up with lights buried inside large wall sections.<span id="more-596"></span></p>
<p>With that in mind, I come up with the following rules for light placement:</p>
<ol>
<li>Divide the cave into multiple subsections</li>
<li>Place a light on a random floor space in each subsection (if there is open floor available in that section)</li>
<li>Move the light in an approximation of an <a href="http://en.wikipedia.org/wiki/Archimedean_spiral">Archimedes&#8217; Spiral</a> until it hits a wall</li>
</ol>
<p>By controlling the number of subsections, I can control the density of lights in my map as well as making sure that lights are relatively evenly distributed around my map.  Moving each light around in a spiral allows me to attach the light source to a wall without running the risk of shooting a light down a long hallway which might have happened had I used straight movement.</p>
<p>Dividing my map into subsections is relatively trivial, I simple iterate over a number of spaces equal to column or row count divided by my desired number of subsections.  At each subsection, I create a list of floor spaces and randomly assign one of those spaces to contain a light.  Using my test map and a subsection count of 8, I end up with the following result.</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitTextPreSpiral.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitTextPreSpiral.png" alt="" title="LitTextPreSpiral" width="676" height="471" class="aligncenter size-full wp-image-597" /></a></p>
<p>With all my lights spawned on the floor, I just move spirally them and attach them to the first wall space they hit.  To accomplish this, I use the spiral rasterizer described <a href="http://cnr.lwlss.net/RasterArchimedes">here</a>.  Instead of generating a full spiral path, I stop at the first wall space encountered by the spiral algorithm and change my current light&#8217;s position to that space.  Running the algorithm over the data rearranges the lights like so:</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitText.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitText.png" alt="" title="LitText" width="706" height="477" class="aligncenter size-full wp-image-598" /></a></p>
<p>Now that I have the positions of my lights figured out, I just need to render them.  The specific rendering technique can vary by game, of course, but this time around I wanted to get a bit of pixel shader experience under my belt.  So, for my lights, I picked an arbitrary size and a torch-like orange color for my lights.  Then, by feeding the positions of any on-screen lights into my pixel shader, I can tint every pixel with an amount of a light&#8217;s color based on the pixel&#8217;s distance from that light.  Combined with the tile-rendered cave from my previous posts, I get an effect like this:</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitTiles.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/LitTiles.png" alt="" title="LitTiles" width="826" height="496" class="aligncenter size-full wp-image-599" /></a></p>
<p>Atmospheric!</p>
<p>For reference, I&#8217;ve included the the most salient code below. Here is the placement code for the lights:</p>
<pre class="brush: c#;">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace ProceduralWorldLib
{
    public enum InhabitantType
    {
        Unknown,
        Light
    }

    public class MapInhabitant
    {
        public InhabitantType InhabitantType;
        public int Column;
        public int Row;

        public MapInhabitant(InhabitantType inhabitantType, int column, int row)
        {
            InhabitantType = inhabitantType;
            Column = column;
            Row = row;
        }
    }

    public class MapPopulator
    {
        private List&amp;lt;MapInhabitant&amp;gt; _mapInhabitants;
        private List&amp;lt;List&amp;lt;CaveSpaceInfo&amp;gt;&amp;gt; _mapToPopulate;
        private int _randomSeed;

        public MapPopulator(int randomSeed)
        {
            _mapInhabitants = new List&amp;lt;MapInhabitant&amp;gt;();
            _randomSeed = randomSeed;
        }

        public int RandomSeed
        {
            set
            {
                _randomSeed = value;
            }
        }

        public List&amp;lt;List&amp;lt;CaveSpaceInfo&amp;gt;&amp;gt; MapToPoplulate
        {
            set
            {
                _mapToPopulate = value;
            }
        }

        public List&amp;lt;MapInhabitant&amp;gt; MapInhabitants
        {
            get
            {
                return _mapInhabitants;
            }
        }

        public void PopulateMap()
        {
            Random rng = new Random(_randomSeed);
            int segmentCount = 8;

            _mapInhabitants.Clear();

            int longestDimension = _mapToPopulate.Count;
            if (_mapToPopulate[0].Count &gt; longestDimension)
            {
                longestDimension = _mapToPopulate[0].Count;
            }

            int entriesPerSegment = 0;
            entriesPerSegment = longestDimension / segmentCount;

            for (int segmentRow = 0; segmentRow &lt; segmentCount; ++segmentRow)
            {
                for (int segmentColumn = 0; segmentColumn &lt; segmentCount; ++segmentColumn)
                {
                    int minRow = segmentRow * entriesPerSegment;
                    int maxRow = (segmentRow + 1) * entriesPerSegment;
                    int minCol = segmentColumn * entriesPerSegment;
                    int maxCol = (segmentColumn + 1) * entriesPerSegment;

                    List&amp;lt;Vector2&amp;gt; floorSpacesInSegment = new List&amp;lt;Vector2&amp;gt;();
                    for (int currRow = minRow; currRow &lt; maxRow &amp;&amp; currRow &lt; _mapToPopulate.Count; ++currRow)
                    {
                        for (int currCol = minCol; currCol &lt; maxCol &amp;&amp; currCol &lt; _mapToPopulate[currRow].Count; ++currCol)
                        {
                            if (_mapToPopulate[currRow][currCol].SpaceType == CaveSpaceType.Floor)
                            {
                                floorSpacesInSegment.Add(new Vector2(currCol, currRow));
                            }
                        }
                    }

                    if (floorSpacesInSegment.Count &gt; 0)
                    {
                        Vector2 selectedFloorSpace = _SpiralIntoWall(
                            floorSpacesInSegment[rng.Next(floorSpacesInSegment.Count)],
                            new Vector2(minCol, minRow),
                            new Vector2(maxCol, maxRow));
                        _mapInhabitants.Add(new MapInhabitant(InhabitantType.Light, (int)selectedFloorSpace.X, (int)selectedFloorSpace.Y));
                    }
                }
            }
        }

        Vector2 _SpiralIntoWall(Vector2 startingPoint, Vector2 upperLeft, Vector2 lowerRight)
        {
            // Distance between spiral loops (1 space)
            float alpha = 1.0f / (float)Math.Sqrt(2.0f);

            // Distance along spiral at each step
            float omega = alpha;

            //Starting theta
            float theta = 0.0f;

            // Find which corner is farthest from the spiral focus
            Vector2[] corners = new Vector2[4];
            corners[0] = upperLeft;
            corners[1] = new Vector2(lowerRight.X, upperLeft.Y);    //Upper Right
            corners[2] = lowerRight;
            corners[3] = new Vector2(upperLeft.X, lowerRight.Y);    //Lower Left
            float currDistance = 0.0f;
            int farthestCornerIndex = -1;
            for (int i = 0; i &lt; corners.Length; ++i)
            {
                float distToCorner = 0.0f;
                Vector2.Distance(ref corners[i], ref startingPoint, out distToCorner);
                if (distToCorner &gt; currDistance)
                {
                    currDistance = distToCorner;
                    farthestCornerIndex = i;
                }
            }
            Vector2 farthestCorner = corners[farthestCornerIndex];

            bool bHitWall = false;
            Vector2 currPoint = startingPoint;
            while (currPoint != farthestCorner)
            {
                //Update the step in angle based on current angle etc.
                float newDelta = _ThetaDelta(alpha, theta, omega);
                theta += newDelta;

                //Calculate the next coordinates
                currPoint = startingPoint + _SpiralCoordinate(theta, alpha);

                //If these coordinates lie within the defined rectangle
                if (currPoint.X &gt;= upperLeft.X &amp;&amp; currPoint.Y &gt;= upperLeft.Y &amp;&amp;
                    currPoint.X &lt;= lowerRight.X &amp;&amp; currPoint.Y &lt;= lowerRight.Y)
                {
                    //Stop if we hit a wall
                    if (_mapToPopulate[(int)currPoint.Y][(int)currPoint.X].SpaceType == CaveSpaceType.Wall)
                    {
                        bHitWall = true;
                        break;
                    }
                }
            }

            return bHitWall ? currPoint : startingPoint;
        }

        float _ThetaDelta(float alpha, float theta, float omega )
        {
            return (float)((2.0f * Math.PI * omega) / Math.Sqrt( Math.Pow( alpha, 2 ) * (1 + Math.Pow( theta, 2 ) ) ));
        }

        Vector2 _SpiralCoordinate(float theta, float alpha)
        {
            return new Vector2(
                    (int)Math.Round(alpha * theta * Math.Cos(theta) / (2 * Math.PI) ),
                    (int)Math.Round(alpha * theta * Math.Sin(theta) / (2 * Math.PI) )
                );
        } 
    }
}
</pre>
<p>Also, rendering code:</p>
<pre class="brush: c#;">
private void _SetLightParams(Vector2 cameraTransform)
{
	_renderEffect.Parameters[&quot;AmbientLightColor&quot;].SetValue(Vector3.One * 0.045f);
        
	int lightIndex = 0;
	foreach(LightInfo light in _lights)
	{	
		Vector2 renderPos = light.Position * _drawScale + cameraTransform;
		float lightSize = light.Size * _drawScale;
		Vector2 sizeVector = Vector2.One * lightSize;
		Vector2 upperLeftExtent = renderPos - sizeVector;
		Vector2 lowerRightExtent = renderPos + sizeVector;

		bool bCull = false;
		bCull = (lowerRightExtent.X &lt; 0.0f || lowerRightExtent.Y &lt; 0.0f);
		bCull |= (upperLeftExtent.X &gt; Game.GraphicsDevice.PresentationParameters.BackBufferWidth || upperLeftExtent.Y &gt; Game.GraphicsDevice.PresentationParameters.BackBufferHeight);

		if( !bCull &amp;&amp; lightIndex &lt; MAX_LIGHT_COUNT )	
		{	
			_renderEffect.Parameters[&quot;LightPosition&quot;].Elements[lightIndex].SetValue(renderPos);
			_renderEffect.Parameters[&quot;LightSizeInverse&quot;].Elements[lightIndex].SetValue(1 / lightSize);
			_renderEffect.Parameters[&quot;LightColor&quot;].Elements[lightIndex].SetValue(light.Color);
			lightIndex++;
		}
	}
	_renderEffect.Parameters[&quot;LightCount&quot;].SetValue(lightIndex);
}
</pre>
<p>And finally, the light pixel shader:</p>
<pre class="brush: c#;">
// Based on the Sprite Effects sample at http://create.msdn.com/en-US/education/catalog/sample/sprite_effects

sampler TextureSampler : register(s0);
float Saturation;

float3 AmbientLightColor;
int LightCount;
float2 LightPosition[8];
float LightSizeInverse[8];
float3 LightColor[8];

float4x4 MatrixTransform : register(vs, c0);

void SpriteVS(inout float4 color    : COLOR0, 
                        inout float2 texCoord : TEXCOORD0, 
                        inout float4 position : SV_Position) 
{ 
    position = mul(position, MatrixTransform); 
} 

float4 main(float2 screenPos : VPOS, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
	// Look up the texture color.
    float4 tex = tex2D(TextureSampler, texCoord);
    
    // Convert it to greyscale. The constants 0.3, 0.59, and 0.11 are because
    // the human eye is more sensitive to green light, and less to blue.
    float greyscale = dot(tex.rgb, float3(0.3, 0.59, 0.11));

	float3 lightPower = AmbientLightColor;
	for(int i=0; i &lt; LightCount; i++)
	{
		float distance = length(screenPos - LightPosition[i]);
		lightPower += clamp(1.0f - (distance * LightSizeInverse[i]), 0.0f, 1.0f) * LightColor[i];
	}
	tex.rgb = lerp(greyscale, tex.rgb, Saturation) * lightPower;

    return tex;
}


technique Desaturate
{
    pass Pass1
    {
		VertexShader = compile vs_3_0 SpriteVS();
        PixelShader = compile ps_3_0 main();
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=596</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Decorating the Floor</title>
		<link>https://gamedevwithoutacause.com/?p=546</link>
		<comments>https://gamedevwithoutacause.com/?p=546#comments</comments>
		<pubDate>Tue, 24 Jan 2012 00:00:06 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=546</guid>
		<description><![CDATA[With the basic shape generated and tile rendering logic in place, my procedurally-generated cave is ready to be navigated. Unfortunately, it&#8217;s still a little bland since all the wall and floor tiles look the same. Worse still, this means that &#8230; <a href="https://gamedevwithoutacause.com/?p=546">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/mario_rug-1.jpg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/mario_rug-1.jpg" alt="" title="mario_rug-1" width="500" height="375" class="aligncenter size-full wp-image-548" /></a><br />
With the basic shape generated and tile rendering logic in place, my procedurally-generated cave is ready to be navigated.  Unfortunately, it&#8217;s still a little bland since all the wall and floor tiles look the same.  Worse still, this means that the player would likely have trouble distinguishing between different areas of the map.  What we need is a little bit of visual variety to help give each area of the cave a unique visual signature.</p>
<p>To accomplish this, I decided to start by adding more visual detail to the floor.  Of course, I could just randomly pepper the ground with detail tiles, but this would a) look random and b) do nothing to help the player distinguish different areas of the floor from each other.  So, I needed a way to create distinct regions of like details on my map.  I found a method to do this in a rather sentimentally-named cellular automaton called &#8220;Togetherness&#8221;.<br />
<span id="more-546"></span></p>
<p>&#8220;Togetherness&#8221; is an algorithm described on <a href="http://www.hermetic.ch/pca/tg.htm">this page</a>.  Just as it&#8217;s name suggests, given several iterations, it takes randomly placed cells and brings them closer together with cells of the same type.  The rules for &#8220;Togetherness&#8221; are (quoted from <a href="http://www.hermetic.ch/pca/algorithms.htm">here</a>, see &#8220;Togetherness&#8221;):</p>
<blockquote><p>
(i) Select an integer <B>q</B> in the range 1 through 7. Cells may be in any of the states 1 through <B>q</B> (represented by <B>q</B> distinct colors) and do not change their state as the system evolves.<BR><br />
(ii) Select a grid size <B>s</B> and an integer <B>n</B> such that 1&nbsp;&lt;=&nbsp;n&nbsp;&lt;=&nbsp;s (this is the &quot;selection range&quot;).<BR><br />
(iii) Select a real-valued &quot;concentration&quot; <B>c</B> (range 0 through 1), which is the probability that a&#160;location on the grid is occupied by a&#160;cell, then set up the initial state of the system by placing c.s<sup>2</sup> cells at random locations on the grid and in random states subject to the condition that there are approximately equal numbers of cells in each of the <B>q</B> states.<BR><br />
(iv) Select an integer <B>m</B> in the range 1 through 9999 (the &quot;lossy move chance&quot;).<BR><br />
(v) The system evolves as a&#160;succession of &quot;steps&quot;, each of which consists of a&#160;succession of <B>s<sup>2</sup></B> &quot;substeps&quot;.<BR><br />
(vi) A &quot;substep&quot; consists of the following process:<br />
&nbsp;&nbsp;&nbsp;(a) Select at random a cell which is not surrounded by 8 neighbors all in the same state.<BR><br />
&nbsp;&nbsp;&nbsp;(b) Among those locations on the grid that are within <B>n</B> units of distance from the cell select one at random (which is not the location of the cell itself).  (Distance is measured as the length of a&#160;minimal path, in horizontal and vertical steps, along the grid.)<BR><br />
&nbsp;&nbsp;&nbsp;(c) If there is a cell at that location and either it has the same state as the first or it is surrounded by 8 neighbors all in the same state as that (second) cell then continue with the next substep.<BR><br />
&nbsp;&nbsp;&nbsp;(d) If there is no cell at that location then calculate the &quot;gain&quot;&nbsp;<B>g</B>, that is, the increase or decrease in the number of neighbors of the cell in the same state as that cell, which would result from moving the cell to that location.  If&nbsp;g&nbsp;&gt;=&nbsp;0 then move the cell to the new location.  If&nbsp;g&nbsp;=&nbsp;-1 then make the move with a&#160;probability of 1 in <B>m</B>.<BR><br />
&nbsp;&nbsp;&nbsp;(e) If there is a cell at that location then calculate the combined &quot;gain&quot;&nbsp;<B>g</B>, that is, the net increase or decrease in the number of neighbors of the two cells in the same state as those cells which would result from swapping them.  If&nbsp;g&nbsp;&gt;=&nbsp;0 then swap the cells. If&nbsp;g&nbsp;=&nbsp;-1 or g&nbsp;=&nbsp;-2 then perform the swap with a&#160;probability of 1&nbsp;in&nbsp;<B>m</B>.<BR><br />
&nbsp;&nbsp;&nbsp;(f) This completes a&#160;substep.
</p></blockquote>
<p>These rules are a mouthful but they essentially break down into the following:</p>
<ol>
<li>Populate a board with cells of a random type</li>
<li>For a number of steps per generation:
<ol>
<li>Pick a random cell and a random target location</li>
<li>If the swapping the cell to the target location would result in an increase in the number of neighbors sharing the same type, perform the swap</li>
<li>Else, perform a &#8220;lossy&#8221; move (a move that would decrease the total togetherness of the board) with a certain probability</li>
</ol>
</li>
</ol>
<p>Applying the above algorithm for about 20 generations turns an array of randomly placed numbers like the one below:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor01.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor01.png" alt="" title="floor01" width="634" height="329" class="aligncenter size-full wp-image-552" /></a></p>
<p>Into an array of much more coherently grouped numbers:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor02.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor02.png" alt="" title="floor02" width="616" height="322" class="aligncenter size-full wp-image-555" /></a><br />
Now that I have an array of well-grouped values, I can use it to modify the visualization of my map floor.  Here you can see one of my procedurally-generated caves with the floor detail areas created with &#8220;Togetherness&#8221;.<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor02-5.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor02-5.png" alt="" title="floor02-5" width="619" height="313" class="aligncenter size-full wp-image-575" /></a></p>
<p>By using the floor detail numbers as selectors for different graphical tiles I end up with results like this:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor03.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/floor03.png" alt="" title="floor03" width="805" height="489" class="aligncenter size-full wp-image-556" /></a><br />
It&#8217;s not perfect, but it&#8217;s definitely more interesting to look at than a single uniform floor.  Of course, the floor detail array doesn&#8217;t necessarily have to be used to determine which tiles to render.  It could also be used to determine what sort of features to blend-in over the floor: patches of moss or discolored wood, for example.</p>
<p>As always, source code for the interested:</p>
<pre class="brush: c#;">
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ProceduralWorldLib
{
    public class TogethernessAutomoton
    {
        int _rngSeed;
        int _columnCount;
        int _rowCount;
        int _stateCount;
        float _concentration;
        int _lossyMoveChance;
        int _generationCount;
        int _selectionRange;

        public TogethernessAutomoton(int seed, int columns, int rows, int stateCount, float concentration, int lossyMoveChance, int generationCount, int selectionRange)
        {
            _rngSeed = seed;
            _columnCount = columns;
            _rowCount = rows;
            _stateCount = stateCount;
            _concentration = concentration;
            _lossyMoveChance = lossyMoveChance;
            _generationCount = generationCount;
            _selectionRange = selectionRange;

            _CheckInvariants();
        }

        public int DesiredArraySize
        {
            get
            {
                return _columnCount * _rowCount;
            }
        }

        public void FillArray(ref int[] targetArray)
        {
            Debug.Assert(targetArray != null);
            Debug.Assert(targetArray.Length == DesiredArraySize);

            Random rng = new Random(_rngSeed);

            //Fill array with random cells
            for (int i = 0; i &lt; DesiredArraySize; ++i)
            {
                if ((float)rng.NextDouble() &lt;= _concentration)
                {
                    targetArray[i] = rng.Next(_stateCount) + 1;
                }
                else
                {
                    targetArray[i] = 0;
                }
            }

            for (int currentGeneration = 0; currentGeneration &lt; _generationCount; ++currentGeneration)
            {
                List&amp;lt;int&amp;gt; notSurroundedIndexList = new List&amp;lt;int&amp;gt;(targetArray.Length);
                for (int y = 0; y &lt; _rowCount; ++y)
                {
                    for( int x = 0; x &lt; _columnCount; ++x )
                    {
                        if (!_IsSurroundedBySame(targetArray, x, y) &amp;&amp; targetArray[_ArrayIndex(x, y)] != 0)
                        {
                            notSurroundedIndexList.Add(_ArrayIndex(x, y));
                        }
                    }
                }

                for (int currentSubStep = 0; currentSubStep &lt; _rowCount * _columnCount; ++currentSubStep)
                {
                    //(a) Select at random a cell which is not surrounded by 8 neighbors all in the same state.
                    int targetIndex = notSurroundedIndexList[rng.Next(notSurroundedIndexList.Count)];

                    //(b) Among those locations on the grid that are within n units of distance from the cell select one at random (which is not the location of the cell itself). (Distance is measured as the length of a minimal path, in horizontal and vertical steps, along the grid.)
                    int swapTargetIndex = _RandomIndexWithinSelectionRange(targetArray, _IndexToX(targetIndex), _IndexToY(targetIndex), rng);

                    //(c) If there is a cell at that location and either it has the same state as the first or it is surrounded by 8 neighbors all in the same state as that (second) cell then continue with the next substep.
                    if( targetArray[swapTargetIndex] == targetArray[targetIndex] || 
                        _IsSurroundedBySame(targetArray, _IndexToX(swapTargetIndex), _IndexToY(swapTargetIndex)))
                    {
                        continue;
                    }
                    
                    //(d) If there is no cell at that location then calculate the &quot;gain&quot; g, that is, the increase or decrease in the number of neighbors of the cell in the same state as that cell, which would result from moving the cell to that location. If g &gt;= 0 then move the cell to the new location. If g = -1 then make the move with a probability of 1 in m.
                    if( targetArray[swapTargetIndex] == 0 )
                    {
                        int currentValue = _SameTypeNeighborCount(targetArray, _IndexToX(targetIndex), _IndexToY(targetIndex), targetArray[targetIndex]);
                        int destinationValue = _SameTypeNeighborCount(targetArray, _IndexToX(swapTargetIndex), _IndexToY(swapTargetIndex), targetArray[targetIndex]);
                        int gainFromMove = destinationValue - currentValue;

                        if( gainFromMove &gt;= 0 )
                        {
                            _SwapValues(targetArray, targetIndex, swapTargetIndex);
                        }
                        else if( gainFromMove == -1 )
                        {
                            if( rng.Next(9999) &lt;= _lossyMoveChance )
                            {
                                _SwapValues(targetArray, targetIndex, swapTargetIndex);
                            }
                        }
                    }
                    
                    //(e) If there is a cell at that location then calculate the combined &quot;gain&quot; g, that is, the net increase or decrease in the number of neighbors of the two cells in the same state as those cells which would result from swapping them. If g &gt;= 0 then swap the cells. If g = -1 or g = -2 then perform the swap with a probability of 1 in m.
                    if( targetArray[swapTargetIndex] != 0 )
                    {
                        int currentValue = _SameTypeNeighborCount(targetArray, _IndexToX(targetIndex), _IndexToY(targetIndex), targetArray[targetIndex]);
                        int destinationValue = _SameTypeNeighborCount(targetArray, _IndexToX(swapTargetIndex), _IndexToY(swapTargetIndex), targetArray[targetIndex]);
                        int gainFromMove1 = destinationValue - currentValue;
                        
                        int currentValue2 = _SameTypeNeighborCount(targetArray, _IndexToX(swapTargetIndex), _IndexToY(swapTargetIndex), targetArray[swapTargetIndex]);
                        int destinationValue2 = _SameTypeNeighborCount(targetArray, _IndexToX(targetIndex), _IndexToY(targetIndex), targetArray[swapTargetIndex]);
                        int gainFromMove2 = destinationValue2 - currentValue2;

                        int netGain = gainFromMove1 + gainFromMove2;
                        if (netGain &gt;= 0)
                        {
                            _SwapValues(targetArray, targetIndex, swapTargetIndex);
                        }
                        else if (netGain == -1 || netGain == -2)
                        {
                            if (rng.Next(9999) &lt;= _lossyMoveChance)
                            {
                                _SwapValues(targetArray, targetIndex, swapTargetIndex);
                            }
                        }
                    }
                }
            }
        }

        private int _IndexToX(int i)
        {
            return i / _rowCount;
        }

        private int _IndexToY(int i)
        {
            return i % _rowCount;
        }

        private int _ArrayIndex(int x, int y)
        {
            return x * _rowCount + y;
        }

        private void _SwapValues(int[] targetArray, int index1, int index2)
        {
            int backup = targetArray[index1];
            targetArray[index1] = targetArray[index2];
            targetArray[index2] = backup;
        }

        private int _SameTypeNeighborCount(int[] targetArray, int x, int y, int currentTileValue)
        {
            int neighborCount = 0;
            if (x &gt; 0 &amp;&amp; y &gt; 0 &amp;&amp; targetArray[_ArrayIndex(x - 1, y - 1)] == currentTileValue) ++neighborCount;
            if (y &gt; 0 &amp;&amp; targetArray[_ArrayIndex(x, y - 1)] == currentTileValue) ++neighborCount;
            if (x &lt; _columnCount - 1 &amp;&amp; y &gt; 0 &amp;&amp; targetArray[_ArrayIndex(x + 1, y - 1)] == currentTileValue) ++neighborCount;

            if (x &gt; 0 &amp;&amp; targetArray[_ArrayIndex(x - 1, y)] == currentTileValue) ++neighborCount;
            if (x &lt; _columnCount - 1 &amp;&amp; targetArray[_ArrayIndex(x + 1, y)] == currentTileValue) ++neighborCount;

            if (x &gt; 0 &amp;&amp; y &lt; _rowCount - 1 &amp;&amp; targetArray[_ArrayIndex(x - 1, y + 1)] == currentTileValue) ++neighborCount;
            if (y &lt; _rowCount - 1 &amp;&amp; targetArray[_ArrayIndex(x, y + 1)] == currentTileValue) ++neighborCount;
            if (x &lt; _columnCount - 1 &amp;&amp; y &lt; _rowCount - 1 &amp;&amp; targetArray[_ArrayIndex(x + 1, y + 1)] == currentTileValue) ++neighborCount;

            return neighborCount;
        }

        private bool _IsSurroundedBySame(int[] targetArray, int x, int y)
        {
            if (x == 0 || x == _columnCount - 1 ||
                y == 0 || y == _rowCount - 1)
            {
                return false;
            }

            int currentTileValue = targetArray[_ArrayIndex(x, y)];
            if (currentTileValue == 0)
            {
                return false;
            }

            return _SameTypeNeighborCount(targetArray, x, y, currentTileValue) &gt;= 8;
        }

        private int _DistanceBetween(int x, int y, int x2, int y2)
        {
            return Math.Abs(x - x2) + Math.Abs(y - y2);
        }

        private int _RandomIndexWithinSelectionRange(int[] targetArray, int baseX, int baseY, Random rng)
        {
            List&amp;lt;int&amp;gt; indexesWithinRange = new List&amp;lt;int&amp;gt;(targetArray.Length);
            for (int y = 0; y &lt; _rowCount; ++y)
            {
                for (int x = 0; x &lt; _columnCount; ++x)
                {
                    if (_DistanceBetween(baseX, baseY, x, y) &lt;= _selectionRange)
                    {
                        indexesWithinRange.Add(_ArrayIndex(x, y));
                    }
                }
            }

            return indexesWithinRange[rng.Next(indexesWithinRange.Count)];
        }

        private void _CheckInvariants()
        {
            Debug.Assert(_columnCount &gt;= 1 &amp;&amp; _rowCount &gt;= 1);
            Debug.Assert(_stateCount &gt;= 1 &amp;&amp; _stateCount &lt; (_columnCount * _rowCount));
            Debug.Assert(_concentration &gt;= 0.0f &amp;&amp; _concentration &lt;= 1.0f);
            Debug.Assert(_lossyMoveChance &gt;= 1 &amp;&amp; _lossyMoveChance &lt;= 9999);
            Debug.Assert(_generationCount &gt;= 0);
        }
    }
}

</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=546</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Tiles and Shadows</title>
		<link>https://gamedevwithoutacause.com/?p=495</link>
		<comments>https://gamedevwithoutacause.com/?p=495#comments</comments>
		<pubDate>Tue, 17 Jan 2012 00:00:29 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=495</guid>
		<description><![CDATA[Last week, I used a combination of algorithms to procedurally generate caves and render them on screen as arrays of &#8216;#&#8217;s and &#8216;.&#8217;s. While text-based rendering is great for debugging and Rogue-likes, sometimes you need something a little more graphical. &#8230; <a href="https://gamedevwithoutacause.com/?p=495">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Bathroom-Wall-Tiles2.jpg"><img class="aligncenter size-full wp-image-496" title="Bathroom-Wall-Tiles2" src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Bathroom-Wall-Tiles2.jpg" alt="" width="520" height="391" /></a><br />
Last <a title="Virtual Spelunking: Procedural Cave Generation" href="http://gamedevwithoutacause.com/?p=460">week</a>, I used a combination of algorithms to procedurally generate caves and render them on screen as arrays of &#8216;#&#8217;s and &#8216;.&#8217;s. While text-based rendering is great for debugging and Rogue-likes, sometimes you need something a little more graphical. Rendering the floor and wall spaces created by the random map generator as tile sprites should do just the trick.</p>
<p>For my cave tile rendering implementation, I decided to use some of the best, free graphic resources available on the web: <a href="http://www.lostgarden.com/2007/05/dancs-miraculously-flexible-game.html">Danc&#8217;s Miraculously Flexible Game Prototyping Tiles</a>. In addition to being attractive, this tileset is also very easy to use because each tile is a self-contained piece. With the exception of shadows (which I will cover later), tiles don&#8217;t affect each other visually so you don&#8217;t need to change which tile you draw based on its neighbors. Because you can treat each tile as a self-contained piece it&#8217;s very easy to map a tile to the floor and wall spaces created by the procedural cave generator and end up with something like this:<br />
<span id="more-495"></span></p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering01.png"><img class="aligncenter size-full wp-image-504" title="cave_rendering01" src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering01.png" alt="" width="807" height="487" /></a></p>
<p>Adding a vertical offset to the wall tiles to raise them by about 36 pixels makes them actually look like walls:</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering02.png"><img class="aligncenter size-full wp-image-505" title="cave_rendering02" src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering02.png" alt="" width="801" height="483" /></a></p>
<p>Not a bad start, but the map still feels flat. This is where the shadow tiles in prototyping tile kit come into play. Danc has lamented that people using his prototyping tiles often don&#8217;t take advantage of the shadow tiles he provides. He has a very good reason for feeling this way: they can make a world of difference in the visual quality of a map.</p>
<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering03.png"><img class="aligncenter size-full wp-image-506" title="cave_rendering03" src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering03.png" alt="" width="802" height="485" /></a></p>
<p>At first glance, implementing the rules for applying the shadow textures can be a bit daunting. Even with the provided <a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/PlanetCuteShadowTest-734680.jpeg">diagram</a> and <a href="http://www.lostgarden.com/2007/05/cutegod-prototyping-challenge.html">instructions</a> (see &#8220;Shadow Tile Placement&#8221;), it can take some time to grok the logic needed to place the shadow tiles correctly. For my shadow placement algorithm I broke down the logic as follows:</p>
<p>For a given tile</p>
<ol>
<li>If a taller tile exists to the N, E, S, or W<br />
Draw the N, E, S, or W shadow on the current tile
</li>
<li>If a taller tile exists to the SW<br />
Draw the SW shadow on the current tile only if the tile to the west is the same height or less
</li>
<li>If a taller tile exists to the SE<br />
Draw the SE shadow on the current tile only if the tile to the east is the same height or less
</li>
<li>If a taller tile exists to the NW<br />
Draw the NW shadow on the current tile only if the tiles to the west and to the north are the same height or less
</li>
<li>If a taller tile exists to the NE<br />
Draw the NE shadow on the current tile only if the tiles to the east and to the north are the same height or less
</li>
</ol>
<p>Or in code:</p>
<pre class="brush: c++;">
private void _DrawShadow(int i, int j, Vector2 drawPos, float widthIncrement, float heightIncrement)
{
    int currTileHeight = _GetTileHeight(_CaveTileToRenderTile(i, j));

    int iMinus = i &gt; 0 ? i - 1 : i;
    int iPlus = i &lt; _arrayToRender.Count - 1 ? i + 1 : i;
    int jMinus = j &gt; 0 ? j - 1 : j;
    int jPlus = j &lt; _arrayToRender[i].Count - 1 ? j + 1 : j;

    if (_GetTileHeight(_CaveTileToRenderTile(iMinus, jMinus)) &gt; currTileHeight)
    {
        if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &lt;= currTileHeight &amp;&amp; _GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &lt;= currTileHeight)
        {
            _DrawShadowTile(TileShadowType.NorthWest, drawPos, widthIncrement, heightIncrement);
        }
    }
    if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &gt; currTileHeight)
    {
        _DrawShadowTile(TileShadowType.North, drawPos, widthIncrement, heightIncrement);
    }
    if (_GetTileHeight(_CaveTileToRenderTile(iMinus, jPlus)) &gt; currTileHeight)
    {
        if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &lt;= currTileHeight &amp;&amp; _GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &lt;= currTileHeight)
        {
            _DrawShadowTile(TileShadowType.NorthEast, drawPos, widthIncrement, heightIncrement);
        }
    }
    if (_GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &gt; currTileHeight)
    {
        _DrawShadowTile(TileShadowType.West, drawPos, widthIncrement, heightIncrement);
    }
    if (_GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &gt; currTileHeight)
    {
        _DrawShadowTile(TileShadowType.East, drawPos, widthIncrement, heightIncrement);
    }
    if (_GetTileHeight(_CaveTileToRenderTile(iPlus, jMinus)) &gt; currTileHeight)
    {
        if (_GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &lt;= currTileHeight)
        {
            _DrawShadowTile(TileShadowType.SouthWest, drawPos, widthIncrement, heightIncrement);
        }
    }
    if (_GetTileHeight(_CaveTileToRenderTile(iPlus, j)) &gt; currTileHeight)
    {
        _DrawShadowTile(TileShadowType.South, drawPos, widthIncrement, heightIncrement);
    }
    if (_GetTileHeight(_CaveTileToRenderTile(iPlus, jPlus)) &gt; currTileHeight)
    {
        if (_GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &lt;= currTileHeight)
        {
            _DrawShadowTile(TileShadowType.SouthEast, drawPos, widthIncrement, heightIncrement);
        }
    }
}
</pre>
<p>Danc&#8217;s instructions also include two special case instructions for shadow placement, but I didn&#8217;t need them for my map so I didn&#8217;t implement them.</p>
<p>Finally, in order to have the walls look taller, I implemented a bit of logic to render some of the walls with the 2-unit tall wall tile instead of the 1-unit tall version. In order to not obscure floor tiles located north of walls, I only drew tall walls in cases where the given wall tile had no floor tiles above it in the same column.  Walls with floors to their north are rendered as 1-unit tall walls so the player will never find themselves completely obscured by the wall to their south.<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering04.png"><img class="aligncenter size-full wp-image-507" title="cave_rendering04" src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/cave_rendering04.png" alt="" width="803" height="484" /></a></p>
<p>Now that&#8217;s starting to look like a cave worth exploring!</p>
<p>Here&#8217;s the tile and shadow rendering code:</p>
<pre class="brush: c++;">
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace ProceduralWorldLib
{
    public enum RenderTileType
    {
        Floor,
        Wall,
        TallWall
    }

    public enum TileShadowType
    {
        SouthEast,
        South,
        SouthWest,
        East,
        West,
        NorthEast,
        North,
        NorthWest
    }

    public class MapRendererTilesComponent : MapRendererBaseComponent
    {
        class TileTexture
        {
            public string TexturePath;
            public Texture2D Texture;
            public Vector2 PositionOffset;
            public bool DrawFloor;              //Should we draw floor under this tile or not? (Will be drawn sans positionoffset)

            public TileTexture(string _texturePath, Vector2 _positionOffset, bool _drawFloor)
            {
                TexturePath = _texturePath;
                Texture = null;
                PositionOffset = _positionOffset;
                DrawFloor = _drawFloor;
            }
        }

        private SpriteBatch _spriteBatch;

        private int _tileWidth;
        private int _tileHeight;
        private Dictionary&amp;lt;RenderTileType, TileTexture&amp;gt; _tileTextures;
        private Dictionary&amp;lt;TileShadowType, TileTexture&amp;gt; _shadowTextures;
        private float _drawScale;

        private bool _drawShadows;
        private Vector2 _cameraPosition;

        public MapRendererTilesComponent(Game game, List&amp;lt;List&amp;lt;CaveSpaceType&amp;gt;&amp;gt; arrayToRender, int tileWidth, int tileHeight, float drawScale)
            : base(game, arrayToRender)
        {
            _tileWidth = tileWidth;
            _tileHeight = tileHeight;
            _tileTextures = new Dictionary&amp;lt;RenderTileType, TileTexture&amp;gt;(2);
            _shadowTextures = new Dictionary&amp;lt;TileShadowType, TileTexture&amp;gt;(8);
            _drawScale = drawScale;

            _drawShadows = true;
        }

        public Vector2 CameraPosition
        {
            get
            {
                return _cameraPosition;
            }
            set
            {
                _cameraPosition = value;
            }
        }

        public float DrawScale
        {
            set
            {
                _drawScale = value;
            }
        }

        public bool DrawShadows
        {
            set
            {
                _drawShadows = value;
            }
        }

        public void AddTile(RenderTileType spaceType, string texturePath, Vector2 posOffset)
        {
            _tileTextures.Add(spaceType, new TileTexture(texturePath, posOffset, false));
        }

        public void AddShadowTile(TileShadowType spaceType, string texturePath)
        {
            _shadowTextures.Add(spaceType, new TileTexture(texturePath, Vector2.Zero, false));
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(Game.GraphicsDevice);

            foreach (KeyValuePair&amp;lt;RenderTileType, TileTexture&amp;gt; entry in _tileTextures)
            {
                entry.Value.Texture = Game.Content.Load&amp;lt;Texture2D&amp;gt;(entry.Value.TexturePath);
                Debug.Assert(entry.Value.Texture != null);
            }

            foreach (KeyValuePair&amp;lt;TileShadowType, TileTexture&amp;gt; entry in _shadowTextures)
            {
                entry.Value.Texture = Game.Content.Load&amp;lt;Texture2D&amp;gt;(entry.Value.TexturePath);
                Debug.Assert(entry.Value.Texture != null);
            }

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            _spriteBatch.Begin();

            Vector2 cameraTransform = _GetCameraTransform();
            Vector2 drawPos = Vector2.Zero;
            float widthIncrement = _tileWidth * _drawScale;
            float heightIncrement = _tileHeight * _drawScale;

            for (int i = 0; i &lt; _arrayToRender.Count; ++i)
            {
                for (int j = 0; j &lt; _arrayToRender[i].Count; ++j)
                {
                    RenderTileType renderTileType = _CaveTileToRenderTile(i, j);

                    Color drawColor = Color.White;
                    Vector2 tilePos = drawPos + cameraTransform + _tileTextures[renderTileType].PositionOffset * _drawScale;
                    
                    if (_tileTextures[renderTileType].DrawFloor)
                    {
                        //Draw floor underneath tiles with negative Y offset since you&#039;ll be able to see below them
                        _spriteBatch.Draw(_tileTextures[RenderTileType.Floor].Texture, drawPos + cameraTransform, null, drawColor, 0.0f, Vector2.Zero, _drawScale, SpriteEffects.None, 0.0f);
                    }
                    _spriteBatch.Draw(_tileTextures[renderTileType].Texture, tilePos, null, drawColor, 0.0f, Vector2.Zero, _drawScale, SpriteEffects.None, 0.0f);
                    if (_drawShadows)
                    {
                        _DrawShadow(i, j, tilePos, widthIncrement, heightIncrement);
                    }
                    drawPos.X += widthIncrement;
                }

                drawPos.X = 0.0f;
                drawPos.Y += heightIncrement;
            }

            _spriteBatch.End();
            base.Draw(gameTime);
        }

        private RenderTileType _CaveTileToRenderTile(int i, int j)
        {
            RenderTileType renderTileType = RenderTileType.Floor;
            if (_arrayToRender[i][j] == CaveSpaceType.Wall)
            {
                bool bFloorToNorth = false;
                for( int tempI = i; tempI &gt; 0; --tempI)
                {
                    if (_arrayToRender[tempI][j] == CaveSpaceType.Floor)
                    {
                        bFloorToNorth = true;
                        break;
                    }
                }
                renderTileType = bFloorToNorth ? RenderTileType.Wall : RenderTileType.TallWall;
            }
            return renderTileType;
        }

        private Vector2 _GetCameraTransform()
        {
            return -_cameraPosition;
        }

        private int _GetTileHeight(RenderTileType renderTileType)
        {
            switch (renderTileType)
            {
                case RenderTileType.Floor:
                    return 0;
                case RenderTileType.Wall:
                    return 1;
                case RenderTileType.TallWall:
                    return 2;
            }
            return 0;
        }

        private void _DrawShadow(int i, int j, Vector2 drawPos, float widthIncrement, float heightIncrement)
        {
            int currTileHeight = _GetTileHeight(_CaveTileToRenderTile(i, j));

            int iMinus = i &gt; 0 ? i - 1 : i;
            int iPlus = i &lt; _arrayToRender.Count - 1 ? i + 1 : i;
            int jMinus = j &gt; 0 ? j - 1 : j;
            int jPlus = j &lt; _arrayToRender[i].Count - 1 ? j + 1 : j;

            if (_GetTileHeight(_CaveTileToRenderTile(iMinus, jMinus)) &gt; currTileHeight)
            {
                if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &lt;= currTileHeight &amp;&amp; _GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &lt;= currTileHeight)
                {
                    _DrawShadowTile(TileShadowType.NorthWest, drawPos, widthIncrement, heightIncrement);
                }
            }
            if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &gt; currTileHeight)
            {
                _DrawShadowTile(TileShadowType.North, drawPos, widthIncrement, heightIncrement);
            }
            if (_GetTileHeight(_CaveTileToRenderTile(iMinus, jPlus)) &gt; currTileHeight)
            {
                if (_GetTileHeight(_CaveTileToRenderTile(iMinus, j)) &lt;= currTileHeight &amp;&amp; _GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &lt;= currTileHeight)
                {
                    _DrawShadowTile(TileShadowType.NorthEast, drawPos, widthIncrement, heightIncrement);
                }
            }
            if (_GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &gt; currTileHeight)
            {
                _DrawShadowTile(TileShadowType.West, drawPos, widthIncrement, heightIncrement);
            }
            if (_GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &gt; currTileHeight)
            {
                _DrawShadowTile(TileShadowType.East, drawPos, widthIncrement, heightIncrement);
            }
            if (_GetTileHeight(_CaveTileToRenderTile(iPlus, jMinus)) &gt; currTileHeight)
            {
                if (_GetTileHeight(_CaveTileToRenderTile(i, jMinus)) &lt;= currTileHeight)
                {
                    _DrawShadowTile(TileShadowType.SouthWest, drawPos, widthIncrement, heightIncrement);
                }
            }
            if (_GetTileHeight(_CaveTileToRenderTile(iPlus, j)) &gt; currTileHeight)
            {
                _DrawShadowTile(TileShadowType.South, drawPos, widthIncrement, heightIncrement);
            }
            if (_GetTileHeight(_CaveTileToRenderTile(iPlus, jPlus)) &gt; currTileHeight)
            {
                if (_GetTileHeight(_CaveTileToRenderTile(i, jPlus)) &lt;= currTileHeight)
                {
                    _DrawShadowTile(TileShadowType.SouthEast, drawPos, widthIncrement, heightIncrement);
                }
            }
        }

        private void _DrawShadowTile(TileShadowType shadowType, Vector2 drawPos, float widthIncrement, float heightIncrement)
        {
            Vector2 shadowDrawPos = drawPos + _shadowTextures[shadowType].PositionOffset;
            _spriteBatch.Draw(_shadowTextures[shadowType].Texture, shadowDrawPos, null, Color.White, 0.0f, Vector2.Zero, _drawScale, SpriteEffects.None, 0.0f);
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=495</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Virtual Spelunking: Procedural Cave Generation</title>
		<link>https://gamedevwithoutacause.com/?p=460</link>
		<comments>https://gamedevwithoutacause.com/?p=460#comments</comments>
		<pubDate>Tue, 10 Jan 2012 00:00:27 +0000</pubDate>
		<dc:creator><![CDATA[Rob]]></dc:creator>
				<category><![CDATA[Procedural Content]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://gamedevwithoutacause.com/?p=460</guid>
		<description><![CDATA[It&#8217;s easy to understand the lure of algorithmically generating game maps, especially for a programmer. A game using this technique can generate a near-infinite set of unique maps allowing it to be played over-and-over again. Generating random maps is the &#8230; <a href="https://gamedevwithoutacause.com/?p=460">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Go-spelunking-in-Slovenia.jpg"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Go-spelunking-in-Slovenia.jpg" alt="" title="Go-spelunking-in-Slovenia" width="600" height="300" class="aligncenter size-full wp-image-463" /></a><br />
It&#8217;s easy to understand the lure of algorithmically generating game maps, especially for a programmer.  A game using this technique can generate a near-infinite set of unique maps allowing it to be played over-and-over again.  Generating random maps is the bread-and-butter of certain game genres, Rogue-likes for example, that thrive on such variety and replay.</p>
<p>There is a wealth of information on the subject of procedurally generated maps on the web.  Particularly on sites devoted to the aforementioned Rogue-like genre of game.  By using techniques documented on one such <a href="http://roguebasin.roguelikedevelopment.org/index.php/Main_Page">site</a> and mixing in some well-known algorithms, I managed to implement a random cave generator that I think I&#8217;ll be able to use in a game or two in the future.<span id="more-460"></span></p>
<p>For my cave generation algorithm, I decided to implement the cellular automata-based cave generation technique described <a href="http://roguebasin.roguelikedevelopment.org/index.php/Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels">here</a>.  The term &#8220;cellular automata&#8221; refers to arrays of cells with states that run (usually) simple algorithms that change their state based on the states of their neighbors.  Cellular automata-based cave generation creates maps by randomly filling an array of spaces with wall and floor tiles.  It then runs an algorithm over the array that changes spaces to walls or floors based on how many walls are in their immediate vicinity.  Each run of the algorithm makes a space more like its neighbors and, after 4-5 generations, creates large contiguous regions of walls/floors in the array.</p>
<p>The cave generation algorithm in a nutshell:</p>
<ol>
<li>Fill each space in the array with either a floor (55% chance) or wall (45% chance)</li>
<li>For each space in the array:
<ol>
<li>If it is a wall and 4+ neighbors are walls, it stays a wall</li>
<li>If it is a floor and 5+ neighbors are walls, it becomes a wall</li>
<li>Otherwise, it becomes a floor</li>
</ol>
<li>Repeat step 2 for as many generations as desired (usually 4-5)</li>
</ol>
<p>Using the algorithm with 4 generations to render a 60&#215;20 map results in a map like this: (&#8216;#&#8217; == walls, &#8216;.&#8217; == floors)<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Cave01.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Cave01.png" alt="" title="Cave01" width="685" height="318" class="aligncenter size-full wp-image-472" /></a></p>
<p>The overall shape of the cavern is appealing and would make a decent play-area.  Unfortunately, not all the caverns are connected which means that some parts of the map would be unreachable.  In order to make this cave generation implementation suitable for use in a game, we need a way to guarantee that all floor spaces will be reachable for any map we generate.</p>
<p>To make sure all floor spaces are reachable, I opted to implement logic to carve out tunnels from the smaller caverns to the largest cavern on the map.  To do this, I need three things: A) a reliable way to detect all the distinct caverns in the map, B) a way to designate the &#8220;central&#8221; cavern, and C) a way to find a reasonably short path to dig tunnels between two caverns.  </p>
<p>Dusting off the old algorithms textbook, I came up with the following solution:</p>
<ol>
<li>Use a <a href="http://en.wikipedia.org/wiki/Flood_fill">flood-fill</a> to detect each cavern (defined as a distinct area of contiguous floor) in the map</li>
<li>Pick the largest cavern as the &#8220;central&#8221; cavern</li>
<li>For each non-central cavern, use <a href="http://en.wikipedia.org/wiki/A*_search_algorithm">A* path-finding</a> to find a path from a random point in the non-central cavern to a random point in the central cavern and turn each space in the path into a floor.</li>
</ol>
<p>For the <a href="http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html">A* heuristic</a> function, I value walls as being more expensive than floors, but not impassible.  This causes the path-finding to favor paths made of floors while still allowing it to dig through walls as a last resort.</p>
<p>Heuristic cost function:</p>
<pre class="brush: c++;">
int _HeuristicCostEstimate(ChamberCoordinate coord1, ChamberCoordinate coord2)
{
    //Manhattan distance with high cost for walls
    int D = 1;
    if (_generatedCave[coord1.i][coord1.j] == CaveSpaceType.Wall)
    {
        D = 10;
    }
    return D * (Math.Abs(coord1.i - coord2.i) + Math.Abs(coord1.j - coord2.j));
}
</pre>
<p>Applying the tunnel-digging logic to the previous map results in the following:<br />
<a href="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Cave02.png"><img src="http://gamedevwithoutacause.com/wp-content/uploads/2012/01/Cave02.png" alt="" title="Cave02" width="680" height="322" class="aligncenter size-full wp-image-478" /></a></p>
<p>Now, every floor space in the map is reachable, ready for the player to explore.  By varying the number I use to seed my random number generator, I can easily create a large number of navigable maps.</p>
<p>For the code-hungry, here is my cave generation implementation:</p>
<pre class="brush: c++;">
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace ProceduralWorldLib
{
    public enum CaveSpaceType
    {
        Floor,
        Wall
    }

    public class RandomCaveGenerator
    {
        struct ChamberCoordinate
        {
            public int i;
            public int j;

            public ChamberCoordinate(int inI, int inJ)
            {
                i = inI;
                j = inJ;
            }

            public void Empty()
            {
                i = -1;
                j = -1;
            }

            public bool IsSet()
            {
                return i &gt;= 0 &amp;&amp; j &gt;= 0;
            }

            public bool Equals(ChamberCoordinate c)
            {
                return c.i == this.i &amp;&amp;
                    c.j == this.j;
            }

            public override bool Equals(object obj)
            {
                if (obj is ChamberCoordinate)
                    return Equals((ChamberCoordinate)obj);
                return false;
            }
        };

        int _columnCount;
        int _rowCount;
        int _rngSeed;
        List&amp;lt;List&amp;lt;CaveSpaceType&amp;gt;&amp;gt; _generatedCave;

        Random _rng;
        List&amp;lt;List&amp;lt;ChamberCoordinate&amp;gt;&amp;gt; _chambers;
        int _centralChamberIndex;
        bool _bDigToCentralCavern;

        const int INITIAL_WALL_CHANCE_PERCENT = 45;
        const int WALLS_FOR_WALL_CONVERSION = 4;
        const int WALLS_FOR_NON_WALL_CONVERSION = 5;
        const int WALL_CONVERSION_GENERATION_COUNT = 4;

        public RandomCaveGenerator(int seed, int columnCount, int rowCount)
        {
            _columnCount = columnCount;
            _rowCount = rowCount;
            _rngSeed = seed;

            _bDigToCentralCavern = true;
        }

        public int ColumnCount
        {
            set
            {
                _columnCount = value;
            }
        }

        public int RowCount
        {
            set
            {
                _rowCount = value;
            }
        }

        public int RandomSeed
        {
            set
            {
                _rngSeed = value;
            }
        }

        public bool DigToCenter
        {
            set
            {
                _bDigToCentralCavern = value;
            }
        }

        public void GenerateMap()
        {
            _rng = new Random(_rngSeed);
            //Fill initial seeds
            _generatedCave = new List&amp;lt;List&amp;lt;CaveSpaceType&amp;gt;&amp;gt;(_rowCount);
            for (int i = 0; i &lt; _rowCount; ++i)
            {
                List&amp;lt;CaveSpaceType&amp;gt; rowArray = new List&amp;lt;CaveSpaceType&amp;gt;(_columnCount);
                for (int j = 0; j &lt; _columnCount; ++j)
                {
                    if (i == 0 || j == 0 || i == _rowCount - 1 || j == _columnCount - 1)
                    {
                        rowArray.Add(CaveSpaceType.Wall);
                    }
                    else
                    {
                        rowArray.Add(_rng.Next(100) &lt; INITIAL_WALL_CHANCE_PERCENT ? CaveSpaceType.Wall : CaveSpaceType.Floor);
                    }
                }
                _generatedCave.Add(rowArray);
            }

            //Convert spaces to walls or floors based on neighbors
            for (int genCount = 0; genCount &lt; WALL_CONVERSION_GENERATION_COUNT; ++genCount)
            {
                CaveSpaceType[][] scratchArray = new CaveSpaceType[_generatedCave.Count][];
                for (int i = 0; i &lt; _generatedCave.Count; ++i)
                {
                    scratchArray[i] = new CaveSpaceType[_generatedCave[i].Count];
                    for (int j = 0; j &lt; _generatedCave[i].Count; ++j)
                    {
                        scratchArray[i][j] = _generatedCave[i][j];
                    }
                }


                for (int i = 0; i &lt; _generatedCave.Count; ++i)
                {
                    for (int j = 0; j &lt; _generatedCave[i].Count; ++j)
                    {
                        _generatedCave[i][j] = _NewSpaceValue(genCount, scratchArray, i, j);
                    }
                }
            }

            //Paint fill to figure out where our caverns are
            _FillPaintCavernDetection();

            //Pick &quot;central&quot; chamber based on size (or proximity to center of map?)
            int lastChamberSize = 0;
            for (int chamberIndex = 0; chamberIndex &lt; _chambers.Count; ++chamberIndex)
            {
                if (_chambers[chamberIndex].Count &gt; lastChamberSize)
                {
                    _centralChamberIndex = chamberIndex;
                    lastChamberSize = _chambers[chamberIndex].Count;
                }
            }

            //A* pathfinding to go from each cavern to central cavern.  Walls have high cost, but are not impassable
            if (_bDigToCentralCavern)
            {
                _DigToCentralCavern();
            }
        }

        public void CopyGeneratedMap(ref List&amp;lt;List&amp;lt;CaveSpaceType&amp;gt;&amp;gt; arrayTarget)
        {
            //Temp: make it actually copy
            arrayTarget = _generatedCave;
        }

        private CaveSpaceType _NewSpaceValue(int genCount, CaveSpaceType[][] scratchArray, int i, int j)
        {
            if( i == 0 || i == _generatedCave.Count - 1 || j == 0 || j == _generatedCave[i].Count - 1 )
                return scratchArray[i][j];

            int wallCount = 0;
            if (_IsWall(scratchArray[i - 1][j - 1])) wallCount++;
            if ( _IsWall(scratchArray[i][j - 1]) ) wallCount++;
            if ( _IsWall(scratchArray[i + 1][j - 1]) ) wallCount++;

            if ( _IsWall(scratchArray[i - 1][j]) ) wallCount++;
            if ( _IsWall(scratchArray[i + 1][j]) ) wallCount++;

            if ( _IsWall(scratchArray[i - 1][j + 1]) ) wallCount++;
            if ( _IsWall(scratchArray[i][j + 1]) ) wallCount++;
            if ( _IsWall(scratchArray[i + 1][j + 1]) ) wallCount++;

            if (scratchArray[i][j] == CaveSpaceType.Wall &amp;&amp; wallCount &gt;= WALLS_FOR_WALL_CONVERSION)
            {
                return CaveSpaceType.Wall;
            }

            if (scratchArray[i][j] == CaveSpaceType.Floor &amp;&amp; wallCount &gt;= WALLS_FOR_NON_WALL_CONVERSION)
            {
                return CaveSpaceType.Wall;
            }

            return CaveSpaceType.Floor;
        }

        private bool _IsWall(CaveSpaceType spaceType)
        {
            return spaceType == CaveSpaceType.Wall;
        }

        private void _FillPaintCavernDetection()
        {
            _chambers = new List&amp;lt;List&amp;lt;ChamberCoordinate&amp;gt;&amp;gt;();

            char[][] scratchArray = new char[_generatedCave.Count][];
            for (int i = 0; i &lt; _generatedCave.Count; ++i)
            {
                scratchArray[i] = new char[_generatedCave[i].Count];
                for (int j = 0; j &lt; _generatedCave[i].Count; ++j)
                {
                    if (_generatedCave[i][j] == CaveSpaceType.Floor)
                    {
                        scratchArray[i][j] = &#039;.&#039;;
                    }
                    else
                    {
                        scratchArray[i][j] = &#039;#&#039;;
                    }
                }
            }

            int fillNumber = 0;
            for (int i = 0; i &lt; _generatedCave.Count; ++i)
            {
                for (int j = 0; j &lt; _generatedCave[i].Count; ++j)
                {
                    if (scratchArray[i][j] == &#039;.&#039;)
                    {
                        _chambers.Add(new List&amp;lt;ChamberCoordinate&amp;lt;());
                        _FillPaintCavern(scratchArray, i, j, Convert.ToChar(fillNumber.ToString()));
                        fillNumber++;
                    }
                }
            }

            //For visualization test
            /*for (int i = 0; i &lt; _generatedCave.Count; ++i)
            {
                for (int j = 0; j &lt; _generatedCave[i].Count; ++j)
                {
                    _generatedCave[i][j] = scratchArray[i][j];
                }
            }*/
        }

        private void _FillPaintCavern(char[][] scratchArray, int i, int j, char fillNumber)
        {
            /*1. If the color of node is not equal to target-color, return.
            2. Set the color of node to replacement-color.
            3. Perform Flood-fill (one step to the west of node, target-color, replacement-color).
                Perform Flood-fill (one step to the east of node, target-color, replacement-color).
                Perform Flood-fill (one step to the north of node, target-color, replacement-color).
                Perform Flood-fill (one step to the south of node, target-color, replacement-color).
            4. Return.*/

            if (scratchArray[i][j] != &#039;.&#039;)
                return;

            scratchArray[i][j] = fillNumber;
            _chambers[_chambers.Count - 1].Add(new ChamberCoordinate(i, j));

            if (i &gt; 0)
                _FillPaintCavern(scratchArray, i - 1, j, fillNumber);
            if (i &lt; _generatedCave.Count - 1)
                _FillPaintCavern(scratchArray, i + 1, j, fillNumber);

            if (j &gt; 0)
                _FillPaintCavern(scratchArray, i, j - 1, fillNumber);
            if (i &lt; _generatedCave[i].Count - 1)
                _FillPaintCavern(scratchArray, i, j + 1, fillNumber);
        }

        private void _DigToCentralCavern()
        {
            for (int chamberIndex = 0; chamberIndex &lt; _chambers.Count; ++chamberIndex)
            {
                if (chamberIndex != _centralChamberIndex)
                {
                    ChamberCoordinate originCoordinate = _chambers[chamberIndex][_rng.Next(_chambers[chamberIndex].Count)];
                    ChamberCoordinate targetCoordinate = _chambers[_centralChamberIndex][_rng.Next(_chambers[_centralChamberIndex].Count)];

                    _DigBetweenPoints(originCoordinate, targetCoordinate);
                }
            }
        }

        void _DigBetweenPoints(ChamberCoordinate originCoordinate, ChamberCoordinate targetCoordinate)
        {
            List&amp;lt;ChamberCoordinate&amp;gt; closed_set = new List&amp;lt;ChamberCoordinate&amp;gt;();
            List&amp;lt;ChamberCoordinate&amp;gt; open_set = new List&amp;lt;ChamberCoordinate&amp;gt;();
            ChamberCoordinate[] came_from = new ChamberCoordinate[_rowCount * _columnCount];
            open_set.Add(originCoordinate);

            int cameFromSize = _rowCount * _columnCount;
            for (int i = 0; i &lt; cameFromSize; ++i)
            {
                came_from[i].Empty();
            }

            int[] g_score = new int[_rowCount * _columnCount];
            int[] h_score = new int[_rowCount * _columnCount];
            int[] f_score = new int[_rowCount * _columnCount];
            g_score[_ChamberToIndex(originCoordinate)] = 0;
            h_score[_ChamberToIndex(originCoordinate)] = _HeuristicCostEstimate(originCoordinate, targetCoordinate);
            f_score[_ChamberToIndex(originCoordinate)] = g_score[_ChamberToIndex(originCoordinate)] + h_score[_ChamberToIndex(originCoordinate)];

            while (open_set.Count &gt; 0)
            {
                int openSetIndex = 0;
                ChamberCoordinate x = open_set[openSetIndex];
                int currentFScore = f_score[_ChamberToIndex(x)];
                for (int idx = 0; idx &lt; open_set.Count; ++idx)
                {
                    ChamberCoordinate check_x = open_set[idx];
                    int check_currentFScore = f_score[_ChamberToIndex(check_x)];

                    if (check_currentFScore &lt; currentFScore)
                    {
                        openSetIndex = idx;
                        currentFScore = check_currentFScore;
                        x = check_x;
                    }
                }

                /*  
                    if x = goal
                        return reconstruct_path(came_from, came_from[goal])*/
                if (x.Equals(targetCoordinate))
                {
                    List&amp;lt;ChamberCoordinate&amp;gt; FinalPath = new List&amp;lt;ChamberCoordinate&amp;gt;();
                    _ReconstructPath(ref FinalPath, ref came_from, came_from[_ChamberToIndex(targetCoordinate)]);

                    for (int i = 0; i &lt; FinalPath.Count; ++i)
                    {

                        _generatedCave[FinalPath[i].i][FinalPath[i].j] = CaveSpaceType.Floor;
                    }
                    return;
                }

                open_set.RemoveAt(openSetIndex);
                closed_set.Add(x);

                //Neighbor calculation purposely excludes outermost edge of spaces from being potential dig targets
                // i.e. [2,rowCount-2), [2,columbCount-2) instead of [1,rowCount-1), [1,columbCount-1)
                List&amp;lt;ChamberCoordinate&amp;gt; xNeighbors = new List&amp;lt;ChamberCoordinate&amp;gt;();
                if (x.i &gt; 1) xNeighbors.Add(new ChamberCoordinate(x.i - 1, x.j));
                if (x.j &gt; 1) xNeighbors.Add(new ChamberCoordinate(x.i, x.j - 1));
                if (x.i &lt; _rowCount - 2) xNeighbors.Add(new ChamberCoordinate(x.i + 1, x.j));
                if (x.j &lt; _columnCount - 2) xNeighbors.Add(new ChamberCoordinate(x.i, x.j + 1));

                for (int neighborIdx = 0; neighborIdx &lt; xNeighbors.Count; ++neighborIdx)
                {
                    ChamberCoordinate y = xNeighbors[neighborIdx];
                    if( closed_set.Contains(y) )
                        continue;

                    int tentative_g_score = g_score[_ChamberToIndex(x)] + _DistanceBetween(x, y);
                    bool tentative_is_better = false;

                    if (!open_set.Contains(y))
                    {
                        open_set.Add(y);
                        tentative_is_better = true;
                    }
                    else if (tentative_g_score &lt; g_score[_ChamberToIndex(y)])
                    {
                        tentative_is_better = true;
                    }
                    else
                    {
                        tentative_is_better = false;
                    }

                    if (tentative_is_better)
                    {
                        //_generatedCave[x.i][x.j] = CaveSpaceType.Floor;
                        came_from[_ChamberToIndex(y)] = x;
                        g_score[_ChamberToIndex(y)] = tentative_g_score;
                        h_score[_ChamberToIndex(y)] = _HeuristicCostEstimate(y, targetCoordinate);
                        f_score[_ChamberToIndex(y)] = g_score[_ChamberToIndex(y)] + h_score[_ChamberToIndex(y)];
                    }
                }
            }
        }

        void _ReconstructPath(ref List&amp;lt;ChamberCoordinate&amp;gt; finalPath, ref ChamberCoordinate[] cameFrom, ChamberCoordinate currentNode)
        {
            if (cameFrom[_ChamberToIndex(currentNode)].IsSet())
            {
                _ReconstructPath(ref finalPath, ref cameFrom, cameFrom[_ChamberToIndex(currentNode)]);
                finalPath.Add(cameFrom[_ChamberToIndex(currentNode)]);
            }
            finalPath.Add(currentNode);
        }

        int _ChamberToIndex(ChamberCoordinate chamberCoord)
        {
            return chamberCoord.j * _rowCount + chamberCoord.i;
        }

        int _HeuristicCostEstimate(ChamberCoordinate coord1, ChamberCoordinate coord2)
        {
            //Manhattan distance with high cost for walls
            int D = 1;
            if (_generatedCave[coord1.i][coord1.j] == CaveSpaceType.Wall)
            {
                D = 10;
            }

            return D * (Math.Abs(coord1.i - coord2.i) + Math.Abs(coord1.j - coord2.j));
        }

        int _DistanceBetween(ChamberCoordinate coord1, ChamberCoordinate coord2)
        {
            int D = 5;
            return D * (Math.Abs(coord1.i - coord2.i) + Math.Abs(coord1.j - coord2.j));
        }
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>https://gamedevwithoutacause.com/?feed=rss2&#038;p=460</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
